foreach iterator with closure

Ali Çehreli acehreli at yahoo.com
Sun Jun 28 18:47:41 UTC 2020


On 6/28/20 9:07 AM, Denis wrote:

 > * foreach is the actual iterator,

Yes. foreach is "lowered" to the following equivalent:

   for ( ; !range.empty; range.popFront()) {
     // Use range.front here
   }

A struct can support foreach iteration through its opCall() member 
function as well. opCall() takes the body of the foreach as a delegate. 
Because it's a function call, it can take full advantage of the function 
call stack. This may help with e.g. writing recursive iteration algorithms.

 
http://ddili.org/ders/d.en/foreach_opapply.html#ix_foreach_opapply.opApply

 > the instantiation of a struct is the
 > range.

Yes.

 > * When a constructor is not used, the arguments in the call to
 > instantiate the range (in this case, `hello` in letters(`hello`)) are
 > mapped sequentially to the member variables in the struct definition
 > (i.e. to letters.str).

Yes, that is a very practical struct feature. I write my structs with as 
little as needed and provide a constructor only when it is necessary as 
in your case.

 > * When a constructor is used, the member variables in the struct
 > definition are in essence private.

Not entirely true. You can still make them public if you want.

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

 > The arguments in the call to
 > instantiate the range are now mapped directly to the parameters in the
 > definition of the "this" function.

Yes.

 > * The syntax and conventions for constructors is difficult and
 > non-intuitive for anyone who hasn't learned Java (or a derivative).

C++ uses the name of the class as the constructor:

// C++ code
struct S {
   S();     // <-- Constructor
   S(int);  // <-- Another one
};

The problem with that syntax is having to rename more than one thing 
when the name of struct changes e.g. to Q:

struct Q {
   Q();
   Q(int);
};

And usually in the implementation:

Q::Q() {}
Q::Q(int) {}

D's choice of 'this' is productive.

 > The
 > linked document provides a simplified explanation for the "this"
 > keyword, which is helpful for the first read:
 > https://docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html.

I like searching for keywords in my index. The "this, constructor" here 
links to the constructor syntax:

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

 > * In some respects, the Java syntax is not very D-like. (For example, it
 > breaks the well-established convention of "Do not use the same name to
 > mean two different things".)

Yes but it competes with another goal: Change as little code as possible 
when one thing needs to be changed. This is not only practical but helps 
with correctness.

 > However, it does need to be learned,
 > because it is common in D source code.

I like D. :p

 > Here is the complete revised code for the example (in condensed form):
 >
 >    import std.stdio;
 >
 >    struct letters {
 >
 >      string str;
 >      int pos = 1;        // Assign here or in this())
 >
 >      this(string param1) {    // cf. shadow str
 >        str = param1;        // cf. this.str = param1 / this.str = str
 >        writeln(`BEGIN`); }
 >
 >      char front() { return str[pos]; }
 >      void popFront() { pos ++; }
 >      bool empty() { return pos == str.length; }
 >
 >      ~this() { writeln("\nEND"); }}
 >
 >    void main() {
 >      foreach (letter; letters(`hello`)) {
 >        write(letter, ' '); }}
 >
 > At this point, I do have one followup question:
 >
 > Why is the shadow str + "this.str = str" the more widely used syntax in
 > D, when the syntax in the code above is unambiguous?

Because one needs to come up with names like "param7", "str_", "_str", 
"s", etc. I like and follow D's standard here.

 > One possible reason that occurred to me is that "str = param1" might
 > require additional GC, because they are different names.

Not at all because there is not memory allocation at all. strings are 
implemented as the equivalent of the following struct:

struct __D_native_string {
   size_t length_;
   char * ptr;
   // ...
}

So, the "str = param1" assignment is nothing but two 64 bit data 
transfer, which can easily by optimized away by the compiler in many cases.

 > But I wouldn't
 > think it'd make any difference to the compiler.

Yes. :)

 >
 > Denis

Ali



More information about the Digitalmars-d-learn mailing list