www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Getting started - D meta-program question

reply Justin Johansson <no spam.com> writes:
There was mention** on the general discussion group that the D foreach_reverse
language construct could be replaced (emulated?) with a (D) meta-program.

** "Even a novice programmer can write a meta-program to replace
foreach_reverse without any runtime performance hit."

   http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=97362
  
As I'm less than a novice with the D meta-programming facilities (at this stage
of my journey into D),
if someone would kindly show me the D meta-program solution to do this,
I'd really appreciate the enlightenment.

Thanks again.
Oct 03 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Justin Johansson wrote:
 There was mention** on the general discussion group that the D foreach_reverse
 language construct could be replaced (emulated?) with a (D) meta-program.
 
 ** "Even a novice programmer can write a meta-program to replace
 foreach_reverse without any runtime performance hit."
 
    http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=97362
   
 As I'm less than a novice with the D meta-programming facilities (at this
stage of my journey into D),
 if someone would kindly show me the D meta-program solution to do this,
 I'd really appreciate the enlightenment.
 
 Thanks again.
Short answer: you can't. Long answer: you can, provided you aren't trying to reverse an opApply, which is patently impossible. As for the "meta-program", I would suspect whoever said that was talking about writing a templated type or function to handle it. You would need to use template specialisation or static ifs to switch on what type you've been given to reverse. http://digitalmars.com/d/1.0/template.html http://digitalmars.com/d/1.0/version.html#staticif
Oct 03 2009
parent reply Justin Johansson <no spam.com> writes:
Daniel Keep Wrote:

 Justin Johansson wrote:
 There was mention** on the general discussion group that the D foreach_reverse
 language construct could be replaced (emulated?) with a (D) meta-program.
 
 ** "Even a novice programmer can write a meta-program to replace
 foreach_reverse without any runtime performance hit."
 
    http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=97362
   
 As I'm less than a novice with the D meta-programming facilities (at this
stage of my journey into D),
 if someone would kindly show me the D meta-program solution to do this,
 I'd really appreciate the enlightenment.
 
 Thanks again.
Short answer: you can't. Long answer: you can, provided you aren't trying to reverse an opApply, which is patently impossible. As for the "meta-program", I would suspect whoever said that was talking about writing a templated type or function to handle it. You would need to use template specialisation or static ifs to switch on what type you've been given to reverse. http://digitalmars.com/d/1.0/template.html http://digitalmars.com/d/1.0/version.html#staticif
Thanks for your answer, Daniel. I thought the original post was a bit of a red herring but thought I'd ask about it anyway. Good thing I did as you motivated me to delve into the subject just after reading your reply. Just now I've written a bit of CS101 play around code (shown below) for constructing a singly-linked list of numbers (Conslist idiom). As the code seems to work, it looks like I've cut my first teeth on "D meta programming". I'm sure there's lots to learn though .. especially looking though some of that std.functional stuff. If you have any comments / suggestions for improvement about this code, I'd graciously accept the same. It's just a tutorial exercise for learning the D way of doing things. class List { List next; int data; this( int data) { this.next = null; this.data = data; } static List opCall() { return cast(List) null; } static List opCall( int data) { return new List( data); } static List cons( int data, List list) { auto newList = List( data); newList.next = list; return newList; } int opApply( int delegate( ref int data) dg) { auto result = 0; auto list = this; while ( list !is null) { if ((result = dg( list.data)) != 0) break; list = list.next; } return result; } } void printdata( int data) { writef( " %d", data); } void print( List list) { writef( "("); if (list !is null) { foreach ( x; list) { printdata( x); } } writef( ")"); } void println( List list) { print( list); writefln(); } List list(A...)( A args) { static if (A.length == 0) { return List(); } else { return List.cons( args[0], list( args[1..$])); } } void main() { auto l0 = list(); println( l0); auto l1 = list( 10); println( l1); auto l2 = list( 10, 20); println( l2); auto l3 = list( 10, 20, 30); println( l3); } Outputs: () ( 10) ( 10 20) ( 10 20 30) Thanks again, Justin
Oct 03 2009
parent reply downs <default_357-line yahoo.de> writes:
Justin Johansson wrote:
 Daniel Keep Wrote:
 
 Justin Johansson wrote:
 There was mention** on the general discussion group that the D foreach_reverse
 language construct could be replaced (emulated?) with a (D) meta-program.

 ** "Even a novice programmer can write a meta-program to replace
 foreach_reverse without any runtime performance hit."

    http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=97362
   
 As I'm less than a novice with the D meta-programming facilities (at this
stage of my journey into D),
 if someone would kindly show me the D meta-program solution to do this,
 I'd really appreciate the enlightenment.

 Thanks again.
Short answer: you can't. Long answer: you can, provided you aren't trying to reverse an opApply, which is patently impossible. As for the "meta-program", I would suspect whoever said that was talking about writing a templated type or function to handle it. You would need to use template specialisation or static ifs to switch on what type you've been given to reverse. http://digitalmars.com/d/1.0/template.html http://digitalmars.com/d/1.0/version.html#staticif
Thanks for your answer, Daniel. I thought the original post was a bit of a red herring but thought I'd ask about it anyway. Good thing I did as you motivated me to delve into the subject just after reading your reply. Just now I've written a bit of CS101 play around code (shown below) for constructing a singly-linked list of numbers (Conslist idiom). As the code seems to work, it looks like I've cut my first teeth on "D meta programming". I'm sure there's lots to learn though .. especially looking though some of that std.functional stuff. If you have any comments / suggestions for improvement about this code, I'd graciously accept the same. It's just a tutorial exercise for learning the D way of doing things. class List { List next; int data; this( int data) { this.next = null; this.data = data; } static List opCall() { return cast(List) null; } static List opCall( int data) { return new List( data); } static List cons( int data, List list) { auto newList = List( data); newList.next = list; return newList; } int opApply( int delegate( ref int data) dg) { auto result = 0; auto list = this; while ( list !is null) { if ((result = dg( list.data)) != 0) break; list = list.next; } return result; } } void printdata( int data) { writef( " %d", data); } void print( List list) { writef( "("); if (list !is null) { foreach ( x; list) { printdata( x); } } writef( ")"); } void println( List list) { print( list); writefln(); } List list(A...)( A args) { static if (A.length == 0) { return List(); } else { return List.cons( args[0], list( args[1..$])); } } void main() { auto l0 = list(); println( l0); auto l1 = list( 10); println( l1); auto l2 = list( 10, 20); println( l2); auto l3 = list( 10, 20, 30); println( l3); } Outputs: () ( 10) ( 10 20) ( 10 20 30) Thanks again, Justin
I really recommend using a struct. There's rarely need to subclass List nodes dynamically, and structs would allow you to define allocators externally, for instance, for implementing freelists. Also, I generally think of a list node as a value, not an object :) In that sense .. module test; import std.string; struct List(T) { List* next; T data; static List* opCall(T data, List* next) { auto res = new List; res.next = next; res.data = data; return res; } static List* opCall(T[] data...) { if (!data.length) return null; else if (data.length == 1) return opCall(data[0], null); else return opCall(data[0], opCall(data[1..$])); } List* cons(T data) { return List(data, this); } int opApply( int delegate( ref int data) dg) { auto current = this; do { if (auto res = dg(current.data)) return res; current = current.next; } while (current); return 0; } string toString() { string res = "("; bool first = true; foreach (value; *this) { if (first) first = false; else res ~= ", "; res ~= format(value); } return res ~ ")"; } } void print(T)(T what) { if (!what) writefln("(nil)"); else writefln(what.toString()); } import std.stdio; void main() { auto l0 = List!(int)(); print(l0); auto l1 = List!(int)(10); print(l1); auto l2 = List!(int)(10, 20); print(l2); auto l3 = List!(int)(10, 20, 30); print(l3); } gentoo-pc ~ $ gdc test.d -o test_c -g && ./test_c (nil) (10) (10, 20) (10, 20, 30)
Oct 04 2009
parent reply Justin Johansson <no spam.com> writes:
downs Wrote:

 Justin Johansson wrote:
 Daniel Keep Wrote:
 
 Just now I've written a bit of CS101 play around code (shown below) for
constructing a
 singly-linked list of numbers (Conslist idiom).  As the code seems to work, it
looks like
 I've cut my first teeth on "D meta programming".
 
 I'm sure there's lots to learn though .. especially looking though some of
that std.functional
 stuff.
 
 If you have any comments / suggestions for improvement about this code, I'd
graciously accept the same.
 It's just a tutorial exercise for learning the D way of doing things.
 
 
 class List
 {
    List next;
    int data;
 
    this( int data) {
       this.next = null;
       this.data = data;
    }
 
    static List opCall() {
       return cast(List) null;
    }
 
    static List opCall( int data) {
       return new List( data);
    }
 
    static List cons( int data, List list) {
       auto newList = List( data);
       newList.next = list;
       return newList;
    }
 
    int opApply( int delegate( ref int data) dg) {
       auto result = 0;
       auto list = this;
 
       while ( list !is null) {
          if ((result = dg( list.data)) != 0)
             break;
 
          list = list.next;
       }
 
       return result;
    }
 
 }
 
 
 void printdata( int data) {
    writef( " %d", data);
 }
 
 
 void print( List list) {
    writef( "(");
 
    if (list !is null) {
       foreach ( x; list) {
          printdata( x);
       }
    }
 
    writef( ")");
 }
 
 
 void println( List list) {
    print( list);
    writefln();
 }
 
 
 List list(A...)( A args) {
    static if (A.length == 0) {
       return List();
    }
 
    else {
       return List.cons( args[0], list( args[1..$]));
    }
 }
 
 
 void main() {
    auto l0 = list();
    println( l0);
    
    auto l1 = list( 10);
    println( l1);
    
    auto l2 = list( 10, 20);
    println( l2);
    
    auto l3 = list( 10, 20, 30);
    println( l3);
    
 }
 
 
 Outputs:
 
 ()
 ( 10)
 ( 10 20)
 ( 10 20 30)
 
 Thanks again, Justin
 
I really recommend using a struct. There's rarely need to subclass List nodes dynamically, and structs would allow you to define allocators externally, for instance, for implementing freelists. Also, I generally think of a list node as a value, not an object :) In that sense .. module test; import std.string; struct List(T) { List* next; T data; static List* opCall(T data, List* next) { auto res = new List; res.next = next; res.data = data; return res; } static List* opCall(T[] data...) { if (!data.length) return null; else if (data.length == 1) return opCall(data[0], null); else return opCall(data[0], opCall(data[1..$])); } List* cons(T data) { return List(data, this); } int opApply( int delegate( ref int data) dg) { auto current = this; do { if (auto res = dg(current.data)) return res; current = current.next; } while (current); return 0; } string toString() { string res = "("; bool first = true; foreach (value; *this) { if (first) first = false; else res ~= ", "; res ~= format(value); } return res ~ ")"; } } void print(T)(T what) { if (!what) writefln("(nil)"); else writefln(what.toString()); } import std.stdio; void main() { auto l0 = List!(int)(); print(l0); auto l1 = List!(int)(10); print(l1); auto l2 = List!(int)(10, 20); print(l2); auto l3 = List!(int)(10, 20, 30); print(l3); }
Great, this question was well worth asking :-) My code above was true to C style. Your code as below, using auto to declare a temporary var in an if statement, ahh, nice, didn't know that. if (auto res = dg(current.data)) return res; What other statement types can you generalized use of auto like this to?
 I really recommend using a struct. There's rarely need to subclass List nodes
dynamically, and structs would allow you to define allocators externally, for
instance, for implementing freelists.
I think with the overhead of at least 8 bytes for a PODO, your suggestion certainly makes sense; struct is a lot greener than class in this case, though going by some of the posts on the main discussion list, you get the feeling that structs are frowned these days upon by some of the language pundits. Thank you for taking the time downs, -- Justin
Oct 04 2009
parent downs <default_357-line yahoo.de> writes:
Justin Johansson wrote:
 Your code as below, using auto to declare a temporary var in an if statement,
ahh, nice,
 didn't know that.
 
          if (auto res = dg(current.data))
             return res;
 
 What other statement types can you generalized use of auto like this to?
 
Sadly, it's an if-specific syntax.
 Thank you for taking the time downs,
 
 -- Justin
Anytime.
Oct 07 2009