www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - List comprehensions in D?

reply David Medlock <noone nowhere.com> writes:
My meager attempts to clone some list functionality in Python:

r = [ y for y in array if y > 10 ];

thanks to shortened delegate declarations its pretty close in D:
(this is a first stab but I'm sure we can get closer with some hackery)

int[] r = array.where( (int y){ return y>10; } );

and for mutating the array:

int[] r = array.map( (int y){return y + 5;} );

and in place modifications:

array.update( (inout int y){ y+=5; } );


// template code begins

template where(T)
{
   T[]   where( T[] arr, bool delegate(T) dg )
   {
     T[] result ;
     foreach( T val; arr ) if ( dg(val) ) result ~= val;
     return result;
   }
}

template  map(T)
{
   T[]   map( T[] arr, T delegate(T) dg )
   {
     T[] result = new T[arr.length];
     for( int n =0; n<arr.length; n++ ) result[n] = dg(arr[n]);
     return result;
   }
}

template update(T)
{
   void  update( T[] arr, void delegate(inout T) dg )
   {
     foreach( int n,T val; arr ) dg( arr[n] );
   }
}

Pretty cool, IMO.
-DavidM
Jun 25 2006
next sibling parent reply David Medlock <ashleymedlock no.spam.yahoo.com> writes:
David Medlock wrote:
 My meager attempts to clone some list functionality in Python:
 
 r = [ y for y in array if y > 10 ];
<snip>
 Pretty cool, IMO.
 -DavidM
in addition, If you consider that arrays are just machine-optimized versions of maps(or graphs) with the allowed index as integers between 0 and N, then passing a predicate function on those indexes makes some sense. for containers: (depending on your view of the syntax) T[] opIndex( bool delegate(T) dg ) { return items.where( dg ); } T[] opIndexAssign( void delegate( inout T) dg ){ items.update( dg ); } x = vector[ (int t){return t>5;} ]; // subset of vector vector[] = (inout int x) { x += 100; } ; // update all members of vector And of course extending to opSlice/opSliceAssign.... -DavidM
Jun 25 2006
next sibling parent Rémy Mouëza <ray.jay.ay.moueza at gmail dot com> <Rémy_member pathlink.com> writes:
In article <e7mg88$1mh3$1 digitaldaemon.com>, David Medlock says...
David Medlock wrote:
 My meager attempts to clone some list functionality in Python:
 
 r = [ y for y in array if y > 10 ];
<snip>
 Pretty cool, IMO.
 -DavidM
in addition, If you consider that arrays are just machine-optimized versions of maps(or graphs) with the allowed index as integers between 0 and N, then passing a predicate function on those indexes makes some sense. for containers: (depending on your view of the syntax) T[] opIndex( bool delegate(T) dg ) { return items.where( dg ); } T[] opIndexAssign( void delegate( inout T) dg ){ items.update( dg ); } x = vector[ (int t){return t>5;} ]; // subset of vector vector[] = (inout int x) { x += 100; } ; // update all members of vector And of course extending to opSlice/opSliceAssign.... -DavidM
Wonderfull !!! And simply powerfull too !!! D is looking more and more like a strongly typed Python like language with braces. Compiled languages can also support high level programming ( if well designed, but D is indeed ).
Jun 25 2006
prev sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
David Medlock wrote:
 David Medlock wrote:
 My meager attempts to clone some list functionality in Python:

 r = [ y for y in array if y > 10 ];
<snip>
 Pretty cool, IMO.
Yes, the new delegate syntax is very convenient. The above functions are also in my std.array proposal (under different names). Your update function with an inout argument instead of my suggested doMap with a pure functional argument is interesting: arr.update((inout int x){ x++; }); vs arr.doMap((int x){ return x+1; }); A smart implementation would be able to support both versions in one function. I wounder if that is appropriate. It would be helpful if there were any way to tell if the arguments of a delegate type were in/out/inout. I guess some .mangleof hackery could help there.
 in addition,
 
 If you consider that arrays are just machine-optimized versions of 
 maps(or graphs) with the allowed index as integers between 0 and N,
 then passing a predicate function on those indexes makes some sense.
 
 for containers: (depending on your view of the syntax)
 
 T[]  opIndex( bool delegate(T) dg ) { return items.where( dg ); }
 T[]  opIndexAssign( void delegate( inout T) dg ){ items.update( dg ); }
In my view of the syntax, opIndex(bool delegate) should return a view, rater then a new array with copied elements, similar to how a slice of an array returns a view rather than copied elements. A combined select/update would also be cool: void opIndexAssign(T delegate(T) updater, bool delegate(T) selecter) /Oskar
Jun 29 2006
parent reply David Medlock <noone nowhere.com> writes:
Oskar Linde wrote:
 David Medlock wrote:
 
 David Medlock wrote:

 My meager attempts to clone some list functionality in Python:

 r = [ y for y in array if y > 10 ];
<snip>
 Pretty cool, IMO.
Yes, the new delegate syntax is very convenient. The above functions are also in my std.array proposal (under different names). Your update function with an inout argument instead of my suggested doMap with a pure functional argument is interesting: arr.update((inout int x){ x++; }); vs arr.doMap((int x){ return x+1; }); A smart implementation would be able to support both versions in one function. I wounder if that is appropriate. It would be helpful if there were any way to tell if the arguments of a delegate type were in/out/inout. I guess some .mangleof hackery could help there.
I'm not sure, but since arrays are by reference I would prefer to make update functions named differently (sort and reverse still bite me on occasion).
 
 in addition,

 If you consider that arrays are just machine-optimized versions of 
 maps(or graphs) with the allowed index as integers between 0 and N,
 then passing a predicate function on those indexes makes some sense.

 for containers: (depending on your view of the syntax)

 T[]  opIndex( bool delegate(T) dg ) { return items.where( dg ); }
 T[]  opIndexAssign( void delegate( inout T) dg ){ items.update( dg ); }
In my view of the syntax, opIndex(bool delegate) should return a view, rater then a new array with copied elements,
Do you mean a container which transforms the elements passed through opIndex using the delegate? That could be an issue with the delegate going out of scope. Otherwise I don't see how you could do it without making a new array. similar to how a slice of
 an array returns a view rather than copied elements. A combined 
 select/update would also be cool:
 
 void opIndexAssign(T delegate(T) updater, bool delegate(T) selecter)
 
 /Oskar
Yes combined functionality is nice and getting close to the list comprehensions i covet from python. I also toyed with the idea of using the (just added) opIn operator. int[] result = container in (int a) {return a>5;}; But this seemed too 'out of the ordinary'. PS. I will be glad to see your array lib in phobos. -DavidM
Jun 29 2006
parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
David Medlock wrote:
 Oskar Linde wrote:
 David Medlock wrote:

 David Medlock wrote:

 My meager attempts to clone some list functionality in Python:

 r = [ y for y in array if y > 10 ];
<snip>
 Pretty cool, IMO.
Yes, the new delegate syntax is very convenient. The above functions are also in my std.array proposal (under different names). Your update function with an inout argument instead of my suggested doMap with a pure functional argument is interesting: arr.update((inout int x){ x++; }); vs arr.doMap((int x){ return x+1; }); A smart implementation would be able to support both versions in one function. I wounder if that is appropriate. It would be helpful if there were any way to tell if the arguments of a delegate type were in/out/inout. I guess some .mangleof hackery could help there.
I'm not sure, but since arrays are by reference I would prefer to make update functions named differently (sort and reverse still bite me on occasion).
I should have mentioned that the doMap is also an in-place modifying function. I agree that such functions should preferably have a different name, and that is the reason for the "do"-prefix. update is a nice and clear name though. I also find the behavior of sort and reverse to be unfortunate. For example, I've more than once seen code similar to: foreach(x ; arr.reverse) {...}, where I believe the side effect was unintentional. Since .sort and .reverse are now fully library-implementable (sans the lacking trailing parentheses), I think they should be made depreciated at some point.
 in addition,

 If you consider that arrays are just machine-optimized versions of 
 maps(or graphs) with the allowed index as integers between 0 and N,
 then passing a predicate function on those indexes makes some sense.

 for containers: (depending on your view of the syntax)

 T[]  opIndex( bool delegate(T) dg ) { return items.where( dg ); }
 T[]  opIndexAssign( void delegate( inout T) dg ){ items.update( dg ); }
In my view of the syntax, opIndex(bool delegate) should return a view, rater then a new array with copied elements,
Do you mean a container which transforms the elements passed through opIndex using the delegate? That could be an issue with the delegate going out of scope.
Yes. And you are right.
 Otherwise I don't see how you could do it without making a new array.
You could use the delegate to evaluate an index array, but that would also count as making a new array I guess. :)
 similar to how a slice of
 an array returns a view rather than copied elements. A combined 
 select/update would also be cool:

 void opIndexAssign(T delegate(T) updater, bool delegate(T) selecter)
Yes combined functionality is nice and getting close to the list comprehensions i covet from python. I also toyed with the idea of using the (just added) opIn operator. int[] result = container in (int a) {return a>5;}; But this seemed too 'out of the ordinary'. PS. I will be glad to see your array lib in phobos.
Thanks. I'm actually not very concerned about getting /my/ library in phobos, but I would certainly like to see /a/ library there. Sadly, D's incomplete ifti support is an inhibiting factor that makes writing template libraries more awkward than needed. The uncertainty regarding when and how we can expect an improvement doesn't help either. Regards, Oskar
Jun 30 2006
prev sibling parent David Medlock <noone nowhere.com> writes:
David Medlock wrote:
 My meager attempts to clone some list functionality in Python:
 
 r = [ y for y in array if y > 10 ];
 
 thanks to shortened delegate declarations its pretty close in D:
 (this is a first stab but I'm sure we can get closer with some hackery)
 
 int[] r = array.where( (int y){ return y>10; } );
 
 and for mutating the array:
 
 int[] r = array.map( (int y){return y + 5;} );
 
 and in place modifications:
 
 array.update( (inout int y){ y+=5; } );
 
 
 // template code begins
 
 template where(T)
 {
   T[]   where( T[] arr, bool delegate(T) dg )
   {
     T[] result ;
     foreach( T val; arr ) if ( dg(val) ) result ~= val;
     return result;
   }
 }
 
 template  map(T)
 {
   T[]   map( T[] arr, T delegate(T) dg )
   {
     T[] result = new T[arr.length];
     for( int n =0; n<arr.length; n++ ) result[n] = dg(arr[n]);
     return result;
   }
 }
 
 template update(T)
 {
   void  update( T[] arr, void delegate(inout T) dg )
   {
     foreach( int n,T val; arr ) dg( arr[n] );
   }
 }
 
 Pretty cool, IMO.
 -DavidM
Another useful pair of templates for parsing: skip and collect // skip whitespace during parsing char[] r = text.skip( (char c){ return c<=32; } ); // get the next token from the input text char[] tok = text.collect( (char c){return isalnum(c)!=0; } ); template skip(T) { T[] skip( T[] arr, bool delegate(T) dg ) { int count = 0; foreach( T val; arr ) if ( dg( arr[count] )) count++; else break; return arr[count..arr.length]; } } template collect(T) { T[] collect( T[] arr, bool delegate(T) dg ) { int count = 0; foreach( T val; arr ) if ( dg( arr[count] )) count++; else break; return arr[0..count]; } } Love the new syntax, so clean. -DavidM
Jun 27 2006