Thread overview
Decrease number of front evaluations
Aug 26, 2015
FreeSlave
Aug 26, 2015
Yazan D
Aug 26, 2015
FreeSlave
Aug 26, 2015
Ali Çehreli
August 26, 2015
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?

August 26, 2015
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
August 26, 2015
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.
August 26, 2015
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