www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Problem with map, reduce, ..

reply "Stefan" <stefanliebig web.de> writes:
I tried to refactor some existing code to use more of the 
functional patterns/style (map, filter, reduce, ..).
The task is to read in some sort of a simple property file and 
present the result as an associative array.
My attempt is:

import std.stdio;
import std.algorithm.iteration : filter, map, reduce;
import std.algorithm.mutation : split;
import std.string : indexOf, strip;
import std.stdio : File, writeln;
import std.algorithm : startsWith;

string[string] add( ref string[string] result, in string line) {
     int assignmentIndex = indexOf( line, "=" );
     if ( assignmentIndex > 0 ) {
         string key = line[ 0 .. assignmentIndex ];
         string value = line[ assignmentIndex + 1 .. line.length ];
         result[ key ] = value;
    }
    return result;
}


void main() {
    auto input = File( "test.ini", "r" );
    auto lines = input.byLine()
       .filter!( line => !startsWith( line, "#" ) && !startsWith( 
line, "!") && line.length > 0 )
       .map!( line => strip( line ) );
    string[string] props;
    auto result = reduce!(add)( props, lines );
}

result should now contain the associative array.

However, it fails with an error in the "reduce" line:

c:\develop\dmd2.067.1\windows\bin\..\..\src\phobos\std\algorith
\iteration.d(2536): Error: static assert  "Incompatible function/seed/element:
prop.add/string[string]/char[]"
c:\develop\dmd2.067.1\windows\bin\..\..\src\phobos\std\algorith
\iteration.d(2521):        instantiated from here: reduceImpl!(false,
MapResult!(__lambda2, FilterResult!(__lambda1, ByLine!(char, char))),
string[string])
c:\develop\dmd2.067.1\windows\bin\..\..\src\phobos\std\algorith
\iteration.d(2505):        instantiated from here:
reducePreImpl!(MapResult!(__lambda2, FilterResult!(__lambda1, ByLine!(char,
char))), string[string])
source\prop.d(30):        instantiated from here: 
reduce!(string[string], MapResult!(__lambda2, 
FilterResult!(__lambda1, ByLine!(char, char))))

I have no idea how to resolve this.
Jun 24 2015
parent reply "Adrian Matoga" <epi atari8.info> writes:
On Wednesday, 24 June 2015 at 08:18:52 UTC, Stefan wrote:
 I tried to refactor some existing code to use more of the 
 functional patterns/style (map, filter, reduce, ..).
 The task is to read in some sort of a simple property file and 
 present the result as an associative array.
 My attempt is:

 import std.stdio;
 import std.algorithm.iteration : filter, map, reduce;
 import std.algorithm.mutation : split;
 import std.string : indexOf, strip;
 import std.stdio : File, writeln;
 import std.algorithm : startsWith;

 string[string] add( ref string[string] result, in string line) {
     int assignmentIndex = indexOf( line, "=" );
     if ( assignmentIndex > 0 ) {
         string key = line[ 0 .. assignmentIndex ];
         string value = line[ assignmentIndex + 1 .. line.length 
 ];
         result[ key ] = value;
    }
    return result;
 }


 void main() {
    auto input = File( "test.ini", "r" );
    auto lines = input.byLine()
       .filter!( line => !startsWith( line, "#" ) && 
 !startsWith( line, "!") && line.length > 0 )
       .map!( line => strip( line ) );
    string[string] props;
    auto result = reduce!(add)( props, lines );
 }

 result should now contain the associative array.

 However, it fails with an error in the "reduce" line:

 c:\develop\dmd2.067.1\windows\bin\..\..\src\phobos\std\algorith
\iteration.d(2536): Error: static assert  "Incompatible function/seed/element:
prop.add/string[string]/char[]"
 c:\develop\dmd2.067.1\windows\bin\..\..\src\phobos\std\algorith
\iteration.d(2521):        instantiated from here: reduceImpl!(false,
MapResult!(__lambda2, FilterResult!(__lambda1, ByLine!(char, char))),
string[string])
 c:\develop\dmd2.067.1\windows\bin\..\..\src\phobos\std\algorith
\iteration.d(2505):        instantiated from here:
reducePreImpl!(MapResult!(__lambda2, FilterResult!(__lambda1, ByLine!(char,
char))), string[string])
 source\prop.d(30):        instantiated from here: 
 reduce!(string[string], MapResult!(__lambda2, 
 FilterResult!(__lambda1, ByLine!(char, char))))

 I have no idea how to resolve this.
input.byLine() yields char[]'s as range elements, while props is (correctly) indexed by strings, i.e. immutable(char)[]. Use .idup to create an immutable copy of the property name, e.g.: auto lines = input.byLine() .filter!( line => !startsWith( line, "#" ) && !startsWith( line, "!") && line.length > 0 ) .map!( line => strip( line.idup ) );
Jun 24 2015
parent reply "Adrian Matoga" <epi atari8.info> writes:
On Wednesday, 24 June 2015 at 08:30:29 UTC, Adrian Matoga wrote:
 input.byLine() yields char[]'s as range elements, while props 
 is (correctly) indexed by strings, i.e. immutable(char)[].
Ooops, more precisely it's because of the second argument of add() being string, but the solution above still applies.
Jun 24 2015
parent reply "Stefan" <stefanliebig web.de> writes:
On Wednesday, 24 June 2015 at 08:33:29 UTC, Adrian Matoga wrote:
 On Wednesday, 24 June 2015 at 08:30:29 UTC, Adrian Matoga wrote:
 input.byLine() yields char[]'s as range elements, while props 
 is (correctly) indexed by strings, i.e. immutable(char)[].
Ooops, more precisely it's because of the second argument of add() being string, but the solution above still applies.
Thanks! That does it! Any idea how to make the 'ugly' reduce step more 'pleasant'? I.e. make it a part of the filter, map, .. chain?
Jun 24 2015
parent reply "Adrian Matoga" <epi atari8.info> writes:
On Wednesday, 24 June 2015 at 08:58:10 UTC, Stefan wrote:
 On Wednesday, 24 June 2015 at 08:33:29 UTC, Adrian Matoga wrote:
 On Wednesday, 24 June 2015 at 08:30:29 UTC, Adrian Matoga 
 wrote:
 input.byLine() yields char[]'s as range elements, while props 
 is (correctly) indexed by strings, i.e. immutable(char)[].
Ooops, more precisely it's because of the second argument of add() being string, but the solution above still applies.
Thanks! That does it! Any idea how to make the 'ugly' reduce step more 'pleasant'? I.e. make it a part of the filter, map, .. chain?
What about: auto result = File(test.ini", "r") .byLine() .filter!( line => !startsWith( line, "#" ) && !startsWith( line, "!") && line.length > 0 ) .map!( line => line.idup.split('=')) .filter!( fields => fields.length > 1) .map!( fields => tuple(fields[0].strip(), fields[1].strip() )) .assocArray();
Jun 24 2015
parent "Stefan" <stefanliebig web.de> writes:
On Wednesday, 24 June 2015 at 09:35:35 UTC, Adrian Matoga wrote:
 On Wednesday, 24 June 2015 at 08:58:10 UTC, Stefan wrote:
 On Wednesday, 24 June 2015 at 08:33:29 UTC, Adrian Matoga 
 wrote:
 [...]
Thanks! That does it! Any idea how to make the 'ugly' reduce step more 'pleasant'? I.e. make it a part of the filter, map, .. chain?
What about: auto result = File(test.ini", "r") .byLine() .filter!( line => !startsWith( line, "#" ) && !startsWith( line, "!") && line.length > 0 ) .map!( line => line.idup.split('=')) .filter!( fields => fields.length > 1) .map!( fields => tuple(fields[0].strip(), fields[1].strip() )) .assocArray();
Wow! Very cool! Thanks
Jun 24 2015