www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - SListRange, Ranges, costructors

reply bearophile <bearophileHUGS lycos.com> writes:
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
Apr 11 2010
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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.
Apr 12 2010
parent bearophile <bearophileHUGS lycos.com> writes:
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
Apr 12 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
 1) I've seen that the Node struct inside std.range.SListRange is not a
 static struct, is it a small bug?
Bug 4087. Currently it's not a 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?
I've taken a look at SListRange, and it seems the p pointer is useless, this works, and there is no need for the reset(): 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); } bool empty() { return !lh; } T front() { return lh.data; } void popFront() { lh = lh.next; } } void main() { List!int items = [1, 2, 3]; foreach (x; items) write(x, " "); // prints 1 2 3 writeln(); assert(items.lh); foreach (x; items) write(x, " "); // prints 1 2 3 } But I feel dumb and I don't understand, why isn't lh null at the end of the first foreach?
 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:
Enhancement request 4086. Bye, bearophile
Apr 13 2010