Jump to page: 1 2 3
Thread overview
Ranges
Aug 04, 2022
pascal111
Aug 04, 2022
Salih Dincer
Aug 04, 2022
frame
Aug 04, 2022
Ali Çehreli
Aug 04, 2022
pascal111
Aug 05, 2022
Salih Dincer
Aug 06, 2022
pascal111
Aug 06, 2022
H. S. Teoh
Aug 06, 2022
pascal111
Aug 07, 2022
Ali Çehreli
Aug 07, 2022
pascal111
Aug 07, 2022
Salih Dincer
Aug 07, 2022
ag0aep6g
Aug 07, 2022
pascal111
Aug 07, 2022
Ali Çehreli
Aug 07, 2022
pascal111
Aug 06, 2022
Salih Dincer
Aug 06, 2022
Salih Dincer
Aug 06, 2022
Ali Çehreli
Aug 07, 2022
Salih Dincer
Aug 07, 2022
Ali Çehreli
Aug 07, 2022
Emanuele Torre
Aug 05, 2022
frame
Aug 05, 2022
Ali Çehreli
Aug 05, 2022
H. S. Teoh
Aug 04, 2022
Ali Çehreli
August 04, 2022

In next code from "https://www.tutorialspoint.com/d_programming/d_programming_ranges.htm", we have two issues:

  1. Why the programmer needs to program "empty()", "front()", and "popFront()" functions for ranges while they exist in the language library? it seems there's no need to exert efforts for that. "https://dlang.org/phobos/std_range_primitives.html"

  2. "front()", and "popFront()" are using fixed constants to move forward the range, while they should use variables.

    '''D
    import std.stdio;
    import std.string;

    struct Student {
    string name;
    int number;

    string toString() const {
       return format("%s(%s)", name, number);
    }
    

    }

    struct School {
    Student[] students;
    }
    struct StudentRange {
    Student[] students;

    this(School school) {
       this.students = school.students;
    }
    @property bool empty() const {
       return students.length == 0;
    }
    @property ref Student front() {
       return students[0];
    }
    void popFront() {
       students = students[1 .. $];
    }
    

    }

    void main() {
    auto school = School([ Student("Raj", 1), Student("John", 2), Student("Ram", 3)]);
    auto range = StudentRange(school);
    writeln(range);

    writeln(school.students.length);
    
    writeln(range.front);
    
    range.popFront;
    
    writeln(range.empty);
    writeln(range);
    

    }
    '''

August 04, 2022

On Thursday, 4 August 2022 at 13:08:21 UTC, pascal111 wrote:

>
    import std.stdio;
    import std.string;

    struct Student {
       string name;
       int number;

       string toString() const {
          return format("%s(%s)", name, number);
       }
    }

    struct School {
       Student[] students;
    }
    struct StudentRange {
       Student[] students;

       this(School school) {
          this.students = school.students;
       }
       @property bool empty() const {
          return students.length == 0;
       }
       @property ref Student front() {
          return students[0];
       }
       void popFront() {
          students = students[1 .. $];
       }
    }

    void main() {
       auto school = School([ Student("Raj", 1), Student("John", 2), Student("Ram", 3)]);
       auto range = StudentRange(school);
       writeln(range);

       writeln(school.students.length);

       writeln(range.front);

       range.popFront;

       writeln(range.empty);
       writeln(range);
    }

😀

August 04, 2022

On Thursday, 4 August 2022 at 13:08:21 UTC, pascal111 wrote:

>
  1. Why the programmer needs to program "empty()", "front()", and "popFront()" functions for ranges while they exist in the language library? it seems there's no need to exert efforts for that. "https://dlang.org/phobos/std_range_primitives.html"
  • These functions are wrappers to use something as range
  • Ranges need to implement the functions to keep their data private, also there are complex types the need to handle data differently
  • Ranges must implement the functions so other function can recognize it as such (eg. isInputRange) - there is no common interface, it's determined by compile time
>
  1. "front()", and "popFront()" are using fixed constants to move forward the range, while they should use variables.

front() is always using the first element BUT popFront() copies all elements except the first one into the variable (and overwrites it), so it moves the data forward.

August 04, 2022
On 8/4/22 06:08, pascal111 wrote:
> In next code from
> "https://www.tutorialspoint.com/d_programming/d_programming_ranges.htm",

That page seems to be adapted from this original:

  http://ddili.org/ders/d.en/ranges.html

> we have two issues:
>
> 1) Why the programmer needs to program "empty()", "front()", and
> "popFront()" functions for ranges

The programmer almost never needs to implement those functions. Existing data structures and algorithms are almost always sufficient. (I did need to implement them but really rarely.)

I tried to explain what those functions do. I don't like my Students example much because wrapping a D slice does not make much sense. Again, I just try to explain them.

> while they exist in the language
> library?

The existing front, popFronh, etc. are only for arrays (slices).

> it seems there's no need to exert efforts for that.

Exactly.

> "https://dlang.org/phobos/std_range_primitives.html"
>
> 2) "front()", and "popFront()" are using fixed constants to move forward
> the range, while they should use variables.

Well, 0 is always the first element and 1..$ are always the rest. Variables would not add any value there.

However, the example could use the existing library function that you mention:

>         @property bool empty() const {
>            return students.length == 0;

Better:

             import std.array : empty;
             return students.empty;

>         }
>         @property ref Student front() {
>            return students[0];

Better:

             import std.array : front;
             return students.front;

>         }
>         void popFront() {
>            students = students[1 .. $];

Better:

             import std.array : popFront;
             students.popFront();

But I think those implementations might confuse the reader.

Ali

August 04, 2022
On 8/4/22 11:05, frame wrote:

> `popFront()`

The function was this:

       void popFront() {
          students = students[1 .. $];
       }

> copies all
> elements except the first one into the variable (and overwrites it), so
> it moves the data forward.

That would be very slow. :) What actually happens is, just the two variables that define a slice is adjusted.

Slices consist of two members:

struct __an_int_D_array_behind_the_scenes {
  size_t length;
  int * ptr;
}

So,

  students = students[1..$];

is the same as doing the following:

  students.length = (students.length - 1);
  students.ptr = students.ptr + 1;

(ptr's value would change by 4 bytes because 'int'.)

No element is copied or moved. :)

Ali

August 04, 2022
On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote:
> On 8/4/22 11:05, frame wrote:
>
> > `popFront()`
>
> The function was this:
>
>        void popFront() {
>           students = students[1 .. $];
>        }
>
> > copies all
> > elements except the first one into the variable (and
> overwrites it), so
> > it moves the data forward.
>
> That would be very slow. :) What actually happens is, just the two variables that define a slice is adjusted.
>
> Slices consist of two members:
>
> struct __an_int_D_array_behind_the_scenes {
>   size_t length;
>   int * ptr;
> }
>
> So,
>
>   students = students[1..$];
>
> is the same as doing the following:
>
>   students.length = (students.length - 1);
>   students.ptr = students.ptr + 1;
>
> (ptr's value would change by 4 bytes because 'int'.)
>
> No element is copied or moved. :)
>
> Ali

I didn't notice that all what we needs to pop a range forward is just a slice, yes, we don't need variable here.
August 05, 2022
On Thursday, 4 August 2022 at 22:54:42 UTC, pascal111 wrote:
>
> I didn't notice that all what we needs to pop a range forward is just a slice, yes, we don't need variable here.

Ranges and Slices are not the same thing. Slicing an array is easy. This is a language possibility. For example, you need an incrementing variable for the Fibonacci Series.

SDB@79

August 05, 2022
On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote:

> No element is copied or moved. :)
>
> Ali

I know that :) I just found that this user has problems to understand basics in D, so I tried not to go in detail and keep at its kind of logical layer. It seems the better way to help until the user asks specific questions.
August 05, 2022
On 8/5/22 01:59, frame wrote:
> On Thursday, 4 August 2022 at 22:14:26 UTC, Ali Çehreli wrote:
>
>> No element is copied or moved. :)
>>
>> Ali
>
> I know that :)

And I know that. :) We don't know who else is reading these threads, so I didn't want to give wrong impression.

Copying would happen if we added slicing on the left-hand side. However, I realized that the following fails with a RangeError:

void main() {
  auto arr = [1, 2, 3];
  arr[0..$-1] = arr[1..$];    // <-- Runtime error
}

I suspect the length of the array is stamped too soon. (?)

Should that operation be supported?

Ali

August 05, 2022
On Fri, Aug 05, 2022 at 08:06:00AM -0700, Ali Çehreli via Digitalmars-d-learn wrote:
> [...] I realized that the following fails with a RangeError:
> 
> void main() {
>   auto arr = [1, 2, 3];
>   arr[0..$-1] = arr[1..$];    // <-- Runtime error
> }
> 
> I suspect the length of the array is stamped too soon. (?)
> 
> Should that operation be supported?
[...]

This is why in C there's a difference between memcpy and memmove.  I don't know how to express the equivalent in D, though. In general, you can't tell until runtime whether two slices overlap (`arr` could be aliased by another slice, for example, so you can't just tell by whether you're copying an overlapping range from the same variable).

But if you know beforehand the ranges being copied are overlapping, you could use std.algorithm.bringToFront which would do the Right Thing(tm) in this case.


T

-- 
Why are you blatanly misspelling "blatant"? -- Branden Robinson
« First   ‹ Prev
1 2 3