Thread overview
SListRange, Ranges, costructors
Apr 12, 2010
bearophile
Apr 13, 2010
Daniel Keep
Apr 13, 2010
bearophile
April 12, 2010
To answers a question in D.learn I've written a minimal single-linked list. This is one version (D2 code):


import std.stdio;

struct List(T) {
    static struct Node {
        T data;
        Node* next;
        this(T d, Node* p) {
            data = d;
            next = p;
        }
    }

    Node* lh;

    this(T[] arr) {
        foreach_reverse (el; arr)
            lh = new Node(el, lh);
    }

    Node* p;
    void reset() { p = lh; } // is reset necessary?
    bool empty() { return !p; }
    T front() { return p.data; }
    void popFront() { p = p.next; }

/*
    static struct ListIterable {
        Node* p;
        bool empty() { return !p; }
        T front() { return p.data; }
        void popFront() { p = p.next; }
    }
    ListIterable opSlice() {
        return ListIterable(lh);
    }
*/
}

void main() {
    List!int items = [1, 2, 3];
    items.reset;
    foreach (x; items)
        write(x, " ");
}


This code has suggested me three questions/musings:

1) I've seen that the Node struct inside std.range.SListRange is not a static struct, is it a small bug?

2) In that List(T) I've used the Range protocol. But I've had to add a reset() function too. Isn't it useful for foreach() to call a function similar to reset() (if present) at the begin of the iteration, to set the variables necessary for the iteration? Is something like this already present with another name in the Range protocol?

3) The stack initalization of a struct has some built-in sugar, while to allocate the struct on the stack you need a this() method:

struct Node {
    int data;
    Node* next;
    this(int d, Node* p) {
        data = d;
        next = p;
    }
}
void main() {
    Node n = Node(10, null); // OK
    Node* pn = new Node(10, null); // OK
}


So is it possible to add to D2 a standard costructor for the heap allocation version too, with the same syntax? (Such default costructor must be absent if any other costructor is defined in the struct/union):

struct Node {
    int data;
    Node* next;
}
struct Foo {
    int data;
    Foo* next;
    this(int d, Foo* p) {
        data = d;
        next = p;
    }
}
void main() {
    Node* pn1 = new Node(10); // OK
    Node* pn2 = new Node(10, null); // OK
    Foo* pf = new Foo(10); // Error, standard initializer this(int) is absent.
}

Bye,
bearophile
April 13, 2010
You don't need reset if you make the range a property of the list.  Then you would use `foreach( x ; list.elements )` or something.
April 13, 2010
Daniel Keep Wrote:
> You don't need reset if you make the range a property of the list.  Then you would use `foreach( x ; list.elements )` or something.

In the List(T) code that I've shown there is a ListIterable struct and a opSlice() that returns it. If you uncomment that part of the code you can use foreach(x; alist[]). (I have copied this usage of opSlice() from Andrei).

But the purpose of my original post was to note that with a reset() method, that foreach can call automatically (if reset() is present), we can avoid that opSlice syntax, or a method like "all" or "elements". So my purpose was to suggest a possible small idea to avoid some clutter in the code that uses the data structure :-) (Maybe Andrei will comment about this when he's back from the ACCU).

Bye,
bearophile