www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Decrease number of front evaluations

reply "FreeSlave" <freeslave93 gmail.com> writes:
Example:

import std.stdio;
import std.algorithm;
import std.path;
import std.file;
import std.exception;
import std.getopt;
import std.array;
import std.range;

auto algo(string fileName, string[] dirs, string[] extensions)
{
     return dirs.filter!(delegate(dir) {
         bool ok;
         collectException(dir.isDir, ok);
         return ok;
     }).map!(dir => extensions
         .map!(delegate(ext) {
             string path = buildPath(dir, fileName ~ ext);
             writefln("Map: %s", path);
             return path;
         }).filter!(delegate(filePath) {
             bool ok;
             writefln("Check: %s", filePath);
             collectException(filePath.isFile, ok);
             return ok;
         })
     ).joiner;
}

void main(string[] args)
{
     string fileName;
     string extensionsStr;
     getopt(args,
            "fileName", "file name to search without extension", 
&fileName,
            "extensions", "list of extensions separated by ':'", 
&extensionsStr
           );

     string[] dirs = args[1..$];

     if (fileName.empty) {
         stderr.writeln("File name not given");
         return;
     }

     if (dirs.empty) {
         dirs = ["."];
     }

     string[] extensions = extensionsStr.splitter(':').array;

     if (extensions.empty) {
         extensions = [".d"];
     }

     foreach(item; algo(fileName, dirs, extensions)) {
         writefln("Found: %s", item);
     }
}

When I run this it like this (assuming main.d exists):

main --fileName=main

It gives me:

Map: .\main.d
Check: .\main.d
Map: .\main.d
Check: .\main.d
Map: .\main.d
Check: .\main.d
Map: .\main.d
Found: .\main.d

In this simple example it calls map 4 times and filter 3 times. 
The map transformer and filter predicate can be expensive, so I 
would want to avoid redundant front evaluations.

The real code is more complicated and can be found here 
https://github.com/MyLittleRobo/icontheme/blob/master/source/icontheme.d#L427
It can call filter's predicate with the same argument up to 8 
times, which is not nice.

Are there ways to fix this? Should I consider writing my own 
range type probably?
Aug 26 2015
next sibling parent reply Yazan D <invalid email.com> writes:
On Wed, 26 Aug 2015 08:27:05 +0000, FreeSlave wrote:
 
 Are there ways to fix this? Should I consider writing my own range type
 probably?
Check http://dlang.org/phobos/std_algorithm_iteration.html#.cache
Aug 26 2015
parent "FreeSlave" <freeslave93 gmail.com> writes:
On Wednesday, 26 August 2015 at 08:30:04 UTC, Yazan D wrote:
 On Wed, 26 Aug 2015 08:27:05 +0000, FreeSlave wrote:
 
 Are there ways to fix this? Should I consider writing my own 
 range type probably?
Check http://dlang.org/phobos/std_algorithm_iteration.html#.cache
I tried it. It can help with map (still calls 2 times though) but the number of filter's predicate calls stay the same.
Aug 26 2015
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/26/2015 01:27 AM, FreeSlave wrote:

 I would want to avoid redundant front evaluations.
Another option is std.functional.memoize: import std.stdio; import std.functional; import std.algorithm; import std.array; void main() { auto r = [ 1, 2, 1 ] .map!(memoize!(delegate(int a) { writefln("map for %s", a); return a; })) .filter!(memoize!(delegate(int a) { writefln("filter for %s", a); return a; })); r.each; } Although there are two 1s, the second one is not evaluated because the result of the first one is used: map for 1 filter for 1 map for 2 filter for 2 Ali
Aug 26 2015