Thread overview
Linked list, printing looks destructive.
Apr 25, 2022
Alain De Vod
Apr 25, 2022
Ali Çehreli
Apr 25, 2022
Salih Dincer
Apr 25, 2022
Alain De Vos
Apr 25, 2022
Alain De Vos
Apr 25, 2022
Salih Dincer
Apr 25, 2022
Ali Çehreli
Apr 25, 2022
cc
Apr 25, 2022
Alain De Vos
April 25, 2022

Following program is a single linked list.
We expect as output 1 2 3 1 2 3
But the output is only 1 2 3
I think this has something to do with popFront
How to fix it using "class List" ?

import std.stdio: write,writeln;
import std.range: empty,popFront,front;

struct Node {
	int element;
	Node * next;
}

class List {
	Node * root=null;
	this(int[] AR){foreach(i ; AR)pushfront(i);}
	bool empty() const {return !root;}
	void popFront() {root=root.next;}
	float front() const {return root.element;}
	void pushfront(int element) {
		Node * newnode=new Node();
		newnode.element=element;
		newnode.next=root;
		root=newnode;
	}
}//List

void main(){
	List l=new List([3,2,1]);
	foreach(element; l) writeln(element);
	foreach(element; l) writeln(element);
}

April 24, 2022
On 4/24/22 18:40, Alain De Vod wrote:

> I think this has something to do with popFront

This type violates a fundamental rule: Containers and ranges are separate concepts. Your List is a container, not a range. I changed your code by moving the range functions to a Range struct that is created by opSlice():

class List {
    Node * root=null;
    this(int[] AR){foreach(i ; AR)pushfront(i);}

    auto opSlice() {
      return Range(root);
    }

    static struct Range {
      Node * root_;

      bool empty() const {return !root_;}
      void popFront() {root_=root_.next;}
      float front() const {return root_.element;}
    }

    void pushfront(int element) {
      Node * newnode=new Node();
      newnode.element=element;
      newnode.next=root;
      root=newnode;
    }
}//List

The foreach loop implicitly calls opSlice() and it all works.[1] When you want a range, you can call opSlice indirectly by using [] after the object:

    import std.algorithm;
    writeln(l[].map!(e => e * 2));

Ali

[1] As reported recently by Steven Schveighoffer, this information is missing in my book: https://bitbucket.org/acehreli/ddili/issues/33/add-additional-foreach-mechanism-to-range

April 25, 2022

On Monday, 25 April 2022 at 02:19:46 UTC, Ali Çehreli wrote:

>

This type violates a fundamental rule: Containers and ranges are separate concepts. Your List is a container, not a range. I changed your code by moving the range functions to a Range [...]

Dear Ali,

I implemented a linkedlist over classical logic (*_leaf and const *_root). Now seeing the ranges approach I tried it but without success. The following implementation can work with foreach() though; as long as you call goRoot() :)

class List(dType) {
  struct Node
  {
    dType item;
    Node* next;
  }
  bool FIFO;
  private Node * _leaf;

  this(bool FIFO = true, dType item = dType.init) {
    this.FIFO = FIFO;
    if(FIFO) _leaf= new Node(item, null);
    _root = cast(const)_leaf;
  }

  /*
  auto opSlice() { return Range(_leaf); }
  static struct Range {//*/
    const Node * _root;

    auto empty()
    {
      return !_leaf;
    }

    auto popFront()
    {
      return _leaf = _leaf.next;
    }

    dType front(Node * node = null)
    {
      dType result;
      if(node)
      {
        result = (*node).item;
      } else result = (*_leaf).item;

      return result;
    }
  //}

  alias Next = popFront;
  alias pop = front;
  alias push = InsertBack;

  void InsertBack(dType item)
  {
    _leaf = new Node(item, _leaf);
  }

  void InsertFront(dType item)
  {
    (*_leaf).next = new Node(item, null);
    Next;
  }

  auto goRoot()
  {
    return _leaf = cast(Node*)_root.next;
  }
}

import std.stdio;

enum FIFO { False, True }
enum end = 20;
void main()
{
  int firstNumber = 10;
  auto FIFO_Stack = new List!int;
  with(FIFO_Stack)
  {
    do {
      InsertFront(firstNumber);
    } while(++firstNumber <= end);

    goRoot();

    do pop.writef!"  %s"; while(Next);
    writeln;
  }

  FIFO_Stack.goRoot();

  foreach(stack; FIFO_Stack) {
    stack.writeln;
  }
} /* OUTPUT:
  10  11  12  13  14  15  16  17  18  19  20
10
11
12
13
14
15
16
17
18
19
20
*/

SDB@79

April 25, 2022

On Monday, 25 April 2022 at 05:17:28 UTC, Salih Dincer wrote:

>

On Monday, 25 April 2022 at 02:19:46 UTC, Ali Çehreli wrote:

>

This type violates a fundamental rule: Containers and ranges are separate concepts. Your List is a container, not a range. I changed your code by moving the range functions to a Range [...]

Dear Ali,

I implemented a linkedlist over classical logic (*_leaf and const *_root). Now seeing the ranges approach I tried it but without success. The following implementation can work with foreach() though; as long as you call goRoot() :)

class List(dType) {
  struct Node
  {
    dType item;
    Node* next;
  }
  bool FIFO;
  private Node * _leaf;

  this(bool FIFO = true, dType item = dType.init) {
    this.FIFO = FIFO;
    if(FIFO) _leaf= new Node(item, null);
    _root = cast(const)_leaf;
  }

  /*
  auto opSlice() { return Range(_leaf); }
  static struct Range {//*/
    const Node * _root;

    auto empty()
    {
      return !_leaf;
    }

    auto popFront()
    {
      return _leaf = _leaf.next;
    }

    dType front(Node * node = null)
    {
      dType result;
      if(node)
      {
        result = (*node).item;
      } else result = (*_leaf).item;

      return result;
    }
  //}

  alias Next = popFront;
  alias pop = front;
  alias push = InsertBack;

  void InsertBack(dType item)
  {
    _leaf = new Node(item, _leaf);
  }

  void InsertFront(dType item)
  {
    (*_leaf).next = new Node(item, null);
    Next;
  }

  auto goRoot()
  {
    return _leaf = cast(Node*)_root.next;
  }
}

import std.stdio;

enum FIFO { False, True }
enum end = 20;
void main()
{
  int firstNumber = 10;
  auto FIFO_Stack = new List!int;
  with(FIFO_Stack)
  {
    do {
      InsertFront(firstNumber);
    } while(++firstNumber <= end);

    goRoot();

    do pop.writef!"  %s"; while(Next);
    writeln;
  }

  FIFO_Stack.goRoot();

  foreach(stack; FIFO_Stack) {
    stack.writeln;
  }
} /* OUTPUT:
  10  11  12  13  14  15  16  17  18  19  20
10
11
12
13
14
15
16
17
18
19
20
*/

SDB@79

I implemented an alternative goroot it's called re_init and next program works.
Still I don't understand what is really going on.
Some state is lost using a class and you have restore it.
And the state is not lost using a struct.

cat test.d
import std.stdio: write,writeln;
import std.range: empty,popFront,front;

    struct Node {
    	int element;
    	Node * next;
    }

    class List {
    	Node * root=null;
    	Node * walker=null;
    	this(int[] AR){foreach(i ; AR)pushfront(i);}
    	bool empty() const {return !walker;}
    	void popFront() {walker=walker.next;}
    	float front() const {return walker.element;}
    	void pushfront(int element) {
    		Node * newnode=new Node();
    		newnode.element=element;
    		newnode.next=root;
    		root=newnode;
    		re_init();
    	}
    	void re_init(){walker=root;}
    }//List

    void main(){
    	List l=new List([3,2,1]);
    	l.writeln();
    	l.re_init();
    	l.writeln();
    }
    ```
April 25, 2022

This program works ok, (but List is no Range)

class Node {
	int data;
	Node next;
}

class List {
	Node node=null;
	this(int[] AR){foreach(i ; AR)pushfront(i);}
	bool empty() const {return !node;}
	void popFront() {node=node.next;}
	float front() const {return node.data;}
	void pushfront(int data) {
		Node newnode=new Node();
		newnode.data=data;
		newnode.next=node;
		node=newnode;
	}
		
}//List

void main(){
	List l=new List([3,2,1]);
	Node backupnode=l.node;
	l.writeln();
	l.node=backupnode;//Restore state destroyed by writeln
	l.writeln();
}
April 25, 2022

On Monday, 25 April 2022 at 09:38:05 UTC, Alain De Vos wrote:

>

This program works ok, (but List is no Range)

It is also possible with the copy constructor of a struct. I don't know how to do with class...

    struct Node {
    	int element;
    	Node * next;
    }
	
    struct List
    {
    	Node * root, walker;
    	
    	this(int[] AR)
    	{
			foreach(i; AR)
			{
				pushfront(i);
			}
		}
		
    	bool empty() const
    	{
			return !walker;
		}
		
    	void popFront()
    	{
			walker = walker.next;
		}
		
    	float front() const
    	{
			return walker.element;
		}
    	
    	void pushfront(int element)
    	{
    		Node * newnode = new Node();
    		newnode.element = element;
    		newnode.next = root;
    		root = newnode;
    	}
    	// shallow copy
		this(ref return scope List that)
		{
       		this.walker = that.root;
       	}
		
}//List

import std.stdio;

void main()
{
	List list = List([3,2,1]);
	//Node backupnode=l.node;
	foreach(l; list)
		l.writeln();
	//l.node=backupnode;//Restore state destroyed by writeln
	foreach(l; list)
		l.writeln();
}

SDB@79

April 25, 2022
On 4/25/22 03:48, Salih Dincer wrote:

> It is also possible with the copy constructor of a struct. I don't know
> how to do with class...

Classes don't have language provided construction because nobody needs it and in fact they have to protect themselves when a language provides it. (See, e.g. C++'s slicing problem: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-copy-virtual ) [1]

In case you want to copy a class object, you must provide a function yourself, which may be called anything you want or 'dup' to match with a name used by D.

Ali

Off-topic rant: It frustrates me when C++ programmers shun D because it is "a language with reference types" as told to me by a well-known C++ speaker. I guess they should attempt to read C++'s core guidelines to understand how a polymorphic class makes it an accident to pass its objects by value.

April 25, 2022

On Monday, 25 April 2022 at 01:40:01 UTC, Alain De Vod wrote:

>

Following program is a single linked list.
We expect as output 1 2 3 1 2 3
But the output is only 1 2 3

If you don't need List to be treated as a true range, but just want to iterate, a simple way to do this is with opApply:
https://tour.dlang.org/tour/en/gems/opdispatch-opapply

import std.stdio: write,writeln;
import std.range: empty,popFront,front;

struct Node {
	int element;
	Node * next;
}

class List {
	Node * root=null;
	this(int[] AR){foreach(i ; AR)pushfront(i);}
	bool empty() const {return !root;}
	/*void popFront() {root=root.next;}
	float front() const {return root.element;}*/
	void pushfront(int element) {
		Node * newnode=new Node();
		newnode.element=element;
		newnode.next=root;
		root=newnode;
	}
	int opApply(int delegate(typeof(Node.element)) dg) {
		Node* current = root;
		while (current) {
			if (dg(current.element)) return 1; // stop iteration if the foreach body asks to break
			current = current.next;
		}
		return 0;
	}
}//List

void main(){
	List l=new List([3,2,1]);
	foreach(element; l) writeln(element);
	foreach(element; l) writeln(element);
}

// 1 2 3 1 2 3
April 25, 2022

Indeed code below works,

import std.stdio: write,writeln;

class Node {
	int data;
	Node next;
}

class List {
	Node node=null;
	this(int[] AR){foreach(i ; AR)pushfront(i);}
	void pushfront(int data) {
		Node newnode=new Node();
		newnode.data=data;
		newnode.next=node;
		node=newnode;
	}//pushfront
	int opApply(int delegate(typeof(Node.data)) dg) {
		Node current = node;
		while (current) {
			if (dg(current.data)) return 1;
			current = current.next;
		}
		return 0;
	}//opApply
}//List

void main(){
	List l=new List([3,2,1]);
	foreach(element; l) writeln(element);
	foreach(element; l) writeln(element);
}//main