Thread overview
array slicing currently does not support steps?
Aug 03, 2020
mw
Aug 03, 2020
Paul Backus
Aug 04, 2020
mw
Aug 04, 2020
Paul Backus
Aug 04, 2020
WebFreak001
Aug 04, 2020
Simen Kjærås
Aug 04, 2020
jmh530
Aug 04, 2020
Paul Backus
Aug 04, 2020
jmh530
August 03, 2020
Hi,

I just noted that D array slice does not support steps?

https://dlang.org/articles/d-array-article.html


Ever since Python 1.4, the slicing syntax has supported an optional third ``step'' or ``stride'' argument. For example, these are all legal Python syntax: L[1:10:2], L[:-1:1], L[::-1]. This was added to Python at the request of the developers of Numerical Python, which uses the third argument extensively.

https://docs.python.org/2.3/whatsnew/section-slices.html

>>> L = range(10)
>>> L[::2]
[0, 2, 4, 6, 8]


Shall we add a DIP for this?

August 03, 2020
On Monday, 3 August 2020 at 23:32:29 UTC, mw wrote:
> Hi,
>
> I just noted that D array slice does not support steps?
>

You can use std.range.stride for this:

http://dpldocs.info/experimental-docs/std.range.stride.html
August 04, 2020
On Monday, 3 August 2020 at 23:50:45 UTC, Paul Backus wrote:
> On Monday, 3 August 2020 at 23:32:29 UTC, mw wrote:
>> Hi,
>>
>> I just noted that D array slice does not support steps?
>>
>
> You can use std.range.stride for this:
>
> http://dpldocs.info/experimental-docs/std.range.stride.html

OK, good to know.

But if it's built into the language syntax (as extended array slicing), it will be better: more succinct, and much easier for Python programmers to adopt D.

August 04, 2020
On Tuesday, 4 August 2020 at 00:03:16 UTC, mw wrote:
> On Monday, 3 August 2020 at 23:50:45 UTC, Paul Backus wrote:
>>
>> You can use std.range.stride for this:
>>
>> http://dpldocs.info/experimental-docs/std.range.stride.html
>
> OK, good to know.
>
> But if it's built into the language syntax (as extended array slicing), it will be better: more succinct, and much easier for Python programmers to adopt D.

In general, D tries to avoid adding special-purpose syntax for things that can be easily solved with library code.
August 04, 2020
On Monday, 3 August 2020 at 23:32:29 UTC, mw wrote:
> Hi,
>
> I just noted that D array slice does not support steps?
>
> https://dlang.org/articles/d-array-article.html
>
>
> Ever since Python 1.4, the slicing syntax has supported an optional third ``step'' or ``stride'' argument. For example, these are all legal Python syntax: L[1:10:2], L[:-1:1], L[::-1]. This was added to Python at the request of the developers of Numerical Python, which uses the third argument extensively.
>
> https://docs.python.org/2.3/whatsnew/section-slices.html
>
>>>> L = range(10)
>>>> L[::2]
> [0, 2, 4, 6, 8]
>
>
> Shall we add a DIP for this?

Since D slices are just views of memory, this would either change how slicing works (copy the data when using stride), or change the memory layout of slices.

Currently, a slice is essentially a (ptr, length) struct, and adding stride to the mix would require adding another field to every slice. This would break all D code ever, so we can disregard that solution.

Copying the data doesn't actually break any code, since the no-stride case would be unaffected, and is the only case currently in use. However, having a different behavior here would break the principle of least astonishment.

Lastly, there's std.range.stride, which is an excellent workaround with none of these drawbacks.

All in all, a DIP is very unlikely to be accepted given the above.

--
  Simen
August 04, 2020
On Tuesday, 4 August 2020 at 00:03:16 UTC, mw wrote:
> On Monday, 3 August 2020 at 23:50:45 UTC, Paul Backus wrote:
>> On Monday, 3 August 2020 at 23:32:29 UTC, mw wrote:
>>> Hi,
>>>
>>> I just noted that D array slice does not support steps?
>>>
>>
>> You can use std.range.stride for this:
>>
>> http://dpldocs.info/experimental-docs/std.range.stride.html
>
> OK, good to know.
>
> But if it's built into the language syntax (as extended array slicing), it will be better: more succinct, and much easier for Python programmers to adopt D.

just act like this is syntax in the language and not a function call:

auto x = array[0 .. 10].stride(2);

I think readability-wise this beats other syntax you can come up with and it's just a library solution :)

If you then want to copy it into an actual array you can pass as slice use

auto x = array[0 .. 10].stride(2).array;

or adjust your functions to handle ranges properly :p

On the function implementation side I can definitely see room for improvement though, like the signatures DIP rikkimax had once made which would make templates not needed for supporting passing and storing ranges.
August 04, 2020
On Tuesday, 4 August 2020 at 07:39:04 UTC, Simen Kjærås wrote:
> [snip]
> Currently, a slice is essentially a (ptr, length) struct, and adding stride to the mix would require adding another field to every slice. This would break all D code ever, so we can disregard that solution.
[snip]

I think I've suggested something similar in the past. I don't understand why another field is required as it would just be some special syntax over indexing.

For instance, I would imagine it used like
auto x = [1, 2, 3, 4, 5, 6, 7];
auto y = x[0..2..5];
assert(y == [1, 3, 5]);

This would be marginally different from python, which puts the strides last and uses colons (which I prefer), but its the same general idea (other languages have similar behavior).

A language like Chapel optionally allows for strides (which do take up space). A user-defined type in D could do the same thing. If the stride is a compile-time type, then you could have a specialization where the stride is 1 and does not take up space.



August 04, 2020
On Tuesday, 4 August 2020 at 13:15:39 UTC, jmh530 wrote:
> On Tuesday, 4 August 2020 at 07:39:04 UTC, Simen Kjærås wrote:
>> [snip]
>> Currently, a slice is essentially a (ptr, length) struct, and adding stride to the mix would require adding another field to every slice. This would break all D code ever, so we can disregard that solution.
> [snip]
>
> I think I've suggested something similar in the past. I don't understand why another field is required as it would just be some special syntax over indexing.
>
> For instance, I would imagine it used like
> auto x = [1, 2, 3, 4, 5, 6, 7];
> auto y = x[0..2..5];
> assert(y == [1, 3, 5]);

Currently, when you do `y = x[0..5]`, both x and y point to the same memory.

In order for `y == [1, 3, 5]` to be true, one of the following must hold:

1) y points to memory which contains 1, 3, and 5 in contiguous locations.
2) y has an overloaded == operator which takes the stride into account.

If #1 is true, then y cannot point to the same memory as x, which means a copy has to be made. If #2 is true, then y cannot be a simple int[], but must instead be a custom type that stores the stride in addition to a pointer and a length.

As WebFreak001 pointed out, you can get the "custom type" version with `x[0..5].stride(2)`, and the the "array with copied elements" version with `x[0..5].stride(2).array`.
August 04, 2020
On Tuesday, 4 August 2020 at 14:52:49 UTC, Paul Backus wrote:
> [snip]
>
> Currently, when you do `y = x[0..5]`, both x and y point to the same memory.
>
> In order for `y == [1, 3, 5]` to be true, one of the following must hold:
>
> 1) y points to memory which contains 1, 3, and 5 in contiguous locations.
> 2) y has an overloaded == operator which takes the stride into account.
>
> If #1 is true, then y cannot point to the same memory as x, which means a copy has to be made. If #2 is true, then y cannot be a simple int[], but must instead be a custom type that stores the stride in addition to a pointer and a length.
>
> As WebFreak001 pointed out, you can get the "custom type" version with `x[0..5].stride(2)`, and the the "array with copied elements" version with `x[0..5].stride(2).array`.

Thanks, that makes sense.