www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Another task

reply bearophile <bearophileHUGS lycos.com> writes:
Now and then I like to test Phobos with simple tasks, to see how it's going.

This simple task is to create a dynamic array of pairs (tuples) like:
[(10,"aa"), (30,"bb"), (50,"cc")]

from the associative array:
[1:'a', 2:'b', 3:'c']

If possible read things lazily from the associative array.

---------------------

Idiomatic Python2 solution (iteritems is lazy):

 d = {1:'a', 2:'b', 3:'c'}
 [(k*10, v*2) for k,v in d.iteritems()]



--------------------- D2 lazy solution without map(): import std.stdio, std.typecons; void main() { auto aa = [1:'a', 2:'b', 3:'c']; Tuple!(int, string)[] r; foreach (k, v; aa) r ~= tuple(k*10, ""~v~v); writeln(r); } --------------------- Alternative D2 lazy solution without append and map(): import std.stdio, std.typecons; void main() { auto aa = [1:"a", 2:"b", 3:"c"]; auto r = new Tuple!(int, string)[aa.length]; int count = 0; foreach (k, v; aa) r[count++] = tuple(k*10, v~v); writeln(r); } --------------------- Now to test Phobos a little, is it easy to write a D2 lazy version that uses map()? Are you able to write it? How many tries needs a D2 programmer with about a month of D2 programming experience to write a correct version that uses map()? Bye, bearophile
Jan 19 2011
next sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
bearophile <bearophileHUGS lycos.com> wrote:

 Now and then I like to test Phobos with simple tasks, to see how it's  
 going.

 This simple task is to create a dynamic array of pairs (tuples) like:
 [(10,"aa"), (30,"bb"), (50,"cc")]

 from the associative array:
 [1:'a', 2:'b', 3:'c']

 If possible read things lazily from the associative array.

 ---------------------

 Idiomatic Python2 solution (iteritems is lazy):

 d = {1:'a', 2:'b', 3:'c'}
 [(k*10, v*2) for k,v in d.iteritems()]



--------------------- D2 lazy solution without map(): import std.stdio, std.typecons; void main() { auto aa = [1:'a', 2:'b', 3:'c']; Tuple!(int, string)[] r; foreach (k, v; aa) r ~= tuple(k*10, ""~v~v); writeln(r); } --------------------- Alternative D2 lazy solution without append and map(): import std.stdio, std.typecons; void main() { auto aa = [1:"a", 2:"b", 3:"c"]; auto r = new Tuple!(int, string)[aa.length]; int count = 0; foreach (k, v; aa) r[count++] = tuple(k*10, v~v); writeln(r); } --------------------- Now to test Phobos a little, is it easy to write a D2 lazy version that uses map()? Are you able to write it? How many tries needs a D2 programmer with about a month of D2 programming experience to write a correct version that uses map()?

Why use map()? The correct solution for this looks like so: import std.range; void main( ) { auto aa = [1:"a", 2:"b", 3:"c"]; auto result = zip( aa.keys, aa.values ); } -- Simen
Jan 19 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Simen kjaeraas:

 Why use map()? The correct solution for this looks like so:
 
 
 import std.range;
 
 void main( ) {
      auto aa = [1:"a", 2:"b", 3:"c"];
      auto result = zip( aa.keys, aa.values );
 }

That result is not the requested one: [(10,"aa"), (30,"bb"), (50,"cc")] And that result is not generated by lazily as the task asks (keys and values return true arrays). Bye, bearophile
Jan 19 2011
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
 That result is not the requested one:
 [(10,"aa"), (30,"bb"), (50,"cc")]

Sorry, the last tuple is (30,"cc"). Bye, bearophile
Jan 19 2011
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Simen kjaeraas:

 Soz, I read a bit too fast. It /is/ lazy, though perhaps not the way
 you meant. This returns the right thing, but does not *read* lazily
  from the AA, a task I am unsure how, if at all possible, one should
 perform.

In the task was written:
 If possible read things lazily from the associative array.

D AAs have byKey and byValue that return a lazy iterator. So if we add a "byItem" or "byPair" or "byKeyValue" you are able to read pairs lazily :-)
 import std.algorithm;
 import std.range;
 
 void main( ) {
      auto aa = [1:"a", 2:"b", 3:"c"];
      auto result = map!"tuple(a[0]*10,a[1]~a[1])"( zip( aa.keys, aa.values  
 ) );
 }

This is a nice solution I didn't think about, thank you :-) It doesn't read data lazily from the AA as requested, but it's not bad looking. Bye and thank you, bearophile
Jan 19 2011
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Simen kjaeraas:

 byKey is essentially an opApply. You have to wrap it in a fiber to make it
 work with the range interface:

Thank you for all your code and work. I have found a bug (it's not a bug of yours): if I compile your code with -release, DMD prints: test.d(45): Error: function D main is a nested function and cannot be accessed from array test.d(45): Error: function D main is a nested function and cannot be accessed from array Where the line 45 is the one that starts with: auto o = map!(... I use D associative arrays often (it comes from my Python practice), I suggest to modify AAs to change byKey() and byValue() into ranges usable with std.algorithm. AAs may become iterable with zero method calls too (this is equivalent to byKey or byValue): map!q{a*10}([1:2, 3:4]) And I suggest to add a third associative array member function that returns a range of key,value tuples, as in Python3. This allows to solve the task like this: auto r = map!q{ tuple(a[0]*10, a[1]~a[1]) }(aa.byPair()); Bye, bearophile
Jan 20 2011
parent bearophile <bearophileHUGS lycos.com> writes:
spir:

 Yes, this is the only nice looking, high-level, and D-style solution. 

I have added: http://d.puremagic.com/issues/show_bug.cgi?id=5466
 While I by far prefer avoiding stringcode:
 	auto r = map!((p) (tuple(p[0]*10, p[1]~p[1])) (aa.byPair());
 where p means pair; should be correct, should'nt it?

If you use a lambda template you need braces and the return statement: auto r = map!((p){ return tuple(p[0]*10, p[1]~p[1]); })(aa.byPair());
 I think for a newcomer the most difficult part is related to tuples:
 * find them (in std.typecons!!!)
 * catch after much time, pains, research, they should not even try to 
 construct a tuple using Tuple!, but using the convenience tuple() func 
 instead.

I agree that like dynamic arrays, tuples are better as built-ins, in D too. Another very useful thing is tuple unpacking syntax: http://d.puremagic.com/issues/show_bug.cgi?id=4579
 And also that map expects a range, which an AA is not according to my 
 trials (code rejected at link time until I used byKey). Or am I wrong?

You aren't wrong.
 PS: sh*t, I cannot have this work, what's wrong?
 
 auto pairs = map!
          ((int i) {return tuple(10*i, aa[i]~aa[i]);})
          (aa.byKey());

Look at the answers by Simen kjaeraas in this thread, he has explained the situation. Bye, bearophile
Jan 20 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 01/20/2011 11:12 AM, bearophile wrote:
 And I suggest to add a third associative array member function that 

the task like this:
 auto r = map!q{ tuple(a[0]*10, a[1]~a[1]) }(aa.byPair());

Yes, this is the only nice looking, high-level, and D-style solution. While I by far prefer avoiding stringcode: auto r = map!((p) (tuple(p[0]*10, p[1]~p[1])) (aa.byPair()); where p means pair; should be correct, should'nt it? I think for a newcomer the most difficult part is related to tuples: * find them (in std.typecons!!!) * catch after much time, pains, research, they should not even try to construct a tuple using Tuple!, but using the convenience tuple() func instead. And also that map expects a range, which an AA is not according to my trials (code rejected at link time until I used byKey). Or am I wrong? PS: sh*t, I cannot have this work, what's wrong? auto pairs = map! ((int i) {return tuple(10*i, aa[i]~aa[i]);}) (aa.byKey()); Denis _________________ vita es estrany spir.wikidot.com
Jan 20 2011
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Thu, 20 Jan 2011 02:40:29 +0100, bearophile <bearophileHUGS lycos.com>  
wrote:

 Simen kjaeraas:

 Why use map()? The correct solution for this looks like so:


 import std.range;

 void main( ) {
      auto aa = [1:"a", 2:"b", 3:"c"];
      auto result = zip( aa.keys, aa.values );
 }

That result is not the requested one: [(10,"aa"), (30,"bb"), (50,"cc")] And that result is not generated by lazily as the task asks (keys and values return true arrays).

Soz, I read a bit too fast. It /is/ lazy, though perhaps not the way you meant. This returns the right thing, but does not *read* lazily from the AA, a task I am unsure how, if at all possible, one should perform. import std.algorithm; import std.range; void main( ) { auto aa = [1:"a", 2:"b", 3:"c"]; auto result = map!"tuple(a[0]*10,a[1]~a[1])"( zip( aa.keys, aa.values ) ); } -- Simen
Jan 19 2011
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Thu, 20 Jan 2011 02:44:39 +0100, bearophile <bearophileHUGS lycos.com>  
wrote:

 That result is not the requested one:
 [(10,"aa"), (30,"bb"), (50,"cc")]

Sorry, the last tuple is (30,"cc").

And the second is (20,"bb"), I hope. :p -- Simen
Jan 19 2011
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
bearophile <bearophileHUGS lycos.com> wrote:

 D AAs have byKey and byValue that return a lazy iterator. So if we add a  
 "byItem" or "byPair" or "byKeyValue" you are able to read pairs lazily  
 :-)

byKey is essentially an opApply. You have to wrap it in a fiber to make it work with the range interface: import std.algorithm; import std.range; import std.stdio; import std.typecons; import core.thread; class opApplyRange( T ) { alias int delegate( int delegate( ref T ) ) dgType; Fiber fib; T value; dgType dg; void fibFun( ) { dg( ( ref T t ){ value = t; Fiber.yield(); return 0; } ); } this( dgType _dg ) { dg = _dg; fib = new Fiber( &fibFun ); popFront( ); } property T front( ) { return value; } property bool empty() { return fib.state == Fiber.State.TERM; } void popFront( ) { if ( !empty ) { fib.call( ); } } } opApplyRange!T toRange( T )( int delegate( int delegate( ref T ) ) dg ) { return new opApplyRange!T( dg ); } void main( ) { auto aa = [1:"a", 2:"b", 3:"c"]; auto o = map!((a){return tuple(a*10,aa[a]~aa[a]);})( toRange( aa.byKey() ) ); writeln(typeof(o).stringof); writeln(array(o)); } Ugly, but it works. -- Simen
Jan 19 2011
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
bearophile <bearophileHUGS lycos.com> wrote:

 I think for a newcomer the most difficult part is related to tuples:
 * find them (in std.typecons!!!)
 * catch after much time, pains, research, they should not even try to
 construct a tuple using Tuple!, but using the convenience tuple() func
 instead.

I agree that like dynamic arrays, tuples are better as built-ins, in D too. Another very useful thing is tuple unpacking syntax: http://d.puremagic.com/issues/show_bug.cgi?id=4579

And if not necessarily being built-ins, they are useful enough to warrant inclusion in object.d rather than std.typecons. -- Simen
Jan 20 2011