Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
February 05, 2018 Language Idea #6892: in array ops, enable mixing slices and random access ranges | ||||
---|---|---|---|---|
| ||||
General idea ============ Currently arrays ops express loops over slices. a[] = b[] * 2 + c[] It would be nice if one could mix a random access range into such an expression. The compiler would have builtin support for random access range. Example ======= ------------------------------>3--------------------------------------- import std.algorithm; import std.array; import std.range; void main() { int[] A = [1, 2, 3]; // arrays ops only work with slices A[] += iota(3).array[]; // Check that iota is a random access range auto myRange = iota(3); static assert( isRandomAccessRange!(typeof(myRange)) ); // Doesn't work, array ops can't mix random access ranges and slices // NEW A[] += myRange[]; // whatever syntax could help the compiler } ------------------------------>3--------------------------------------- How it could work ================= A[] += myRange[]; // or another syntax for "myRange as an array op operand" would be rewritten to: foreach(i; 0..A.length) A[i] += myRange[i]; myRange should not be a range without "length". Why? ==== Bridges a gap between lazy generation and array ops, now that array ops are reliable. Allow arrays ops to take slice-like objects. What do you think? |
February 06, 2018 Re: Language Idea #6892: in array ops, enable mixing slices and random access ranges | ||||
---|---|---|---|---|
| ||||
Posted in reply to Guillaume Piolat | On Monday, 5 February 2018 at 17:35:45 UTC, Guillaume Piolat wrote:
> General idea
> ============
>
> Currently arrays ops express loops over slices.
>
> a[] = b[] * 2 + c[]
>
> It would be nice if one could mix a random access range into such an expression.
> The compiler would have builtin support for random access range.
>
>
> Example
> =======
>
> ------------------------------>3---------------------------------------
>
> import std.algorithm;
> import std.array;
> import std.range;
>
> void main()
> {
> int[] A = [1, 2, 3];
>
> // arrays ops only work with slices
> A[] += iota(3).array[];
>
>
> // Check that iota is a random access range
> auto myRange = iota(3);
> static assert( isRandomAccessRange!(typeof(myRange)) );
>
>
> // Doesn't work, array ops can't mix random access ranges and slices
> // NEW
> A[] += myRange[]; // whatever syntax could help the compiler
> }
>
> ------------------------------>3---------------------------------------
>
>
> How it could work
> =================
>
> A[] += myRange[]; // or another syntax for "myRange as an array op operand"
>
> would be rewritten to:
>
> foreach(i; 0..A.length)
> A[i] += myRange[i];
>
>
> myRange should not be a range without "length".
>
>
> Why?
> ====
>
> Bridges a gap between lazy generation and array ops, now that array ops are reliable.
> Allow arrays ops to take slice-like objects.
>
>
> What do you think?
It's already possible, with only very slightly worse aesthetics:
struct VecOp(T)
{
T[] arr;
pragma(inline, true)
T[] opOpAssign(string op: "+", Range)(Range r)
{
int i;
foreach (e; r)
{
arr[i] += e;
i++;
}
return arr;
}
}
pragma(inline, true)
VecOp!E vecOp(E)(return E[] arr)
{
return typeof(return)(arr);
}
void main()
{
import std.range: iota;
int[] a = [1, 2, 3];
a.vecOp += iota(3);
assert(a == [1, 3, 5]);
}
I'm not very good at reading assembly, so I have no idea whether it's comparable to doing `a[] += [0, 1, 2]`.
|
February 06, 2018 Re: Language Idea #6892: in array ops, enable mixing slices and random access ranges | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | On Tuesday, 6 February 2018 at 02:14:35 UTC, Meta wrote: >> What do you think? > > It's already possible, with only very slightly worse aesthetics: > [Good stuff] We can do better than that, though: import std.range, std.array, std.algorithm; struct Vec(Range) if (isInputRange!Range) { Range rng; this(Range value) { rng = value; } static if (!isInfinite!Range) { auto get() { return rng.array; } alias get this; } auto getRange(R2)(R2 other) { static if (isInputRange!R2) return other; else static if (isVec!R2) return other.rng; else return repeat(other); } auto opBinary(string op, R2)(R2 rhs) { return vec(rng.zip(getRange(rhs)).map!("a[0] "~op~" a[1]")); } auto opBinaryRight(string op, R2)(R2 lhs) { return vec(getRange(lhs).zip(rng).map!("a[0] "~op~" a[1]")); } auto opOpAssign(string op, R2)(R2 rhs) if (isForwardRange!Range) { auto rhsR = getRange(rhs); auto r = rng.save; foreach (ref e; r) { if (rhsR.empty) break; mixin("e "~op~"= rhsR.front;"); rhsR.popFront(); } return this; } } auto vec(Range)(Range r) if (isInputRange!Range) { return Vec!Range(r); } enum isVec(T) = is(T == Vec!U, U); unittest { import std.stdio; auto a = [1,2,3,4,5,6,7,8]; auto b = a.length.iota; auto c = recurrence!("a[n-1] + a[n-2]")(1, 1); uint[] result = a + b * c.vec; writeln(result); result.vec += result; } -- Simen |
February 06, 2018 Re: Language Idea #6892: in array ops, enable mixing slices and random access ranges | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen Kjærås | On Tuesday, 6 February 2018 at 09:02:46 UTC, Simen Kjærås wrote:
> On Tuesday, 6 February 2018 at 02:14:35 UTC, Meta wrote:
>>> What do you think?
>>
>> It's already possible, with only very slightly worse aesthetics:
>>
> [Good stuff]
>
> We can do better than that, though:
>
> [More good stuff]
The problem in your sample is that you turn a range into a new slice with .array (which is "alias this" also), and this allocates.
Meta's solution brings slices into the range world instead (rather than ranges to array ops like originally proposed).
|
February 06, 2018 Re: Language Idea #6892: in array ops, enable mixing slices and random access ranges | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | On Tuesday, 6 February 2018 at 02:14:35 UTC, Meta wrote:
> It's already possible, with only very slightly worse aesthetics:
>
> struct VecOp(T)
> {
> T[] arr;
>
> pragma(inline, true)
> T[] opOpAssign(string op: "+", Range)(Range r)
> {
> int i;
> foreach (e; r)
> {
> arr[i] += e;
> i++;
> }
>
> return arr;
> }
> }
>
> pragma(inline, true)
> VecOp!E vecOp(E)(return E[] arr)
> {
> return typeof(return)(arr);
> }
I'm not sure if this is equivalent to what I asked, but in the event it is, here is a thought.
A fully featured VecOp could be part of druntime, and slices as array ops operands could be replaced by slice.vecOp by the compiler.
If this vectorize well, this allow to remove array ops from the language. (However it seems to me array ops are recognized at the operator level but well)
|
February 06, 2018 Re: Language Idea #6892: in array ops, enable mixing slices and random access ranges | ||||
---|---|---|---|---|
| ||||
Posted in reply to Guillaume Piolat | On Tuesday, 6 February 2018 at 12:13:22 UTC, Guillaume Piolat wrote:
> On Tuesday, 6 February 2018 at 09:02:46 UTC, Simen Kjærås wrote:
>> On Tuesday, 6 February 2018 at 02:14:35 UTC, Meta wrote:
>>>> What do you think?
>>>
>>> It's already possible, with only very slightly worse aesthetics:
>>>
>> [Good stuff]
>>
>> We can do better than that, though:
>>
>> [More good stuff]
>
> The problem in your sample is that you turn a range into a new slice with .array (which is "alias this" also), and this allocates.
Only if you assign it to an array. While it's not very well indicated in the above example, this also works, and does not allocate:
unittest
{
auto a = [1,2,3,4,5,6,7,8];
auto b = a.length.iota;
a.vec += b;
}
--
Simen
|
Copyright © 1999-2021 by the D Language Foundation