2014-01-18 3 views
4

Я пытаюсь реализовать классический высокий диапазон порядка zipWith следующимПопытка реализовать zipWith

import std.traits: allSatisfy; 
import std.range: isInputRange; 

auto zipWith(fun, Ranges...)(Ranges ranges) if (Ranges.length >= 2 && allSatisfy!(isInputRange, Ranges)) 
{ 
    import std.range: zip; 
    return zip(ranges).map!fun; 
} 

но

unittest 
{ 
    auto x = [1, 2, 3, 4, 5]; 
    zipWith!((a, b) => a + b)(x, x); 
} 

с ошибкой

template algorithm_ex.zipWith cannot deduce function from argument types !((a, b) => a + b)(int[], int[]), candidates are: (d-dmd-unittest) 
algorithm_ex.zipWith(fun, Ranges...)(Ranges ranges) if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 

и я дон Не понимаю почему. Кто-нибудь подсказывает?

Update:

После CyberShadows хороший ответ теперь у меня есть

import std.traits: allSatisfy; 

/** Zip $(D ranges) together with operation $(D fun). 
    TODO: Simplify when Issue 8715 is fixed providing zipWith 
*/ 
auto zipWith(alias fun, Ranges...)(Ranges ranges) if (Ranges.length >= 2 && allSatisfy!(isInputRange, Ranges)) { 
    import std.range: zip; 
    import std.algorithm: map; 
    import std.functional: binaryFun; 
    static if (ranges.length == 2) 
     return zip(ranges).map!(a => binaryFun!fun(a.expand)); 
    else if (ranges.length >= 3) 
     return zip(ranges).map!(a => naryFun(a.expand)); 
    else 
     static assert(false, "Need at least 2 range arguments."); 
} 
unittest { 
    auto x = [1, 2, 3]; 
    import std.array: array; 
    assert(zipWith!"a+b"(x, x).array == [2, 4, 6]); 
    assert(zipWith!((a, b) => a + b)(x, x).array == [2, 4, 6]); 
    assert(zipWith!"a+b+c"(x, x, x).array == [3, 6, 9]); 
} 

Можно ли расширить его поддержку ни капли веселья с помощью строк, как zipWith!"a+b+c"(x,x,x)? Я спрашиваю, особенно, потому что я замечаю, что есть код для naryFun в std.functional, но он закомментирован.

ответ

5
  1. Вы должны объявить параметр fun шаблона в качестве alias параметра, в противном случае он объявлен как параметр типа:

    auto zipWith(alias fun, Ranges...)(// ... 
    
  2. Вам нужно импортировать std.algorithm для map.

  3. std.range.zip вернет диапазон std.typecons.Tuple, который не будет автоматически расширяться в два параметра вашей лямбда. Вам нужно явно расширить кортеж.

Исправлен код:

import std.traits: allSatisfy; 
import std.range: isInputRange; 
import std.algorithm: map; 

auto zipWith(alias fun, Ranges...)(Ranges ranges) if (Ranges.length >= 2 && allSatisfy!(isInputRange, Ranges)) 
{ 
    import std.range: zip; 
    return zip(ranges).map!(t => fun(t.expand)); 
} 

unittest 
{ 
    auto x = [1, 2, 3, 4, 5]; 
    zipWith!((a, b) => a + b)(x, x); 
} 

Можно ли расширить его поддержку ни капли веселья с помощью строк, как zipWith!"a+b+c"(x,x,x)?

Я не понимаю, почему нет:

import std.string; 

private string genNaryFun(string fun, V...)() 
{ 
    string code; 
    foreach (n, v; V) 
     code ~= "alias values[%d] %s;".format(n, cast(char)('a'+n)); 
    code ~= "return " ~ fun ~ ";"; 
    return code; 
} 

template naryFun(string fun) 
{ 
    auto naryFun(V...)(V values) 
    { 
     mixin(genNaryFun!(fun, V)); 
    } 
} 

unittest 
{ 
    alias naryFun!"a + b + c" test; 
    assert(test(1, 2, 3) == 6); 
} 
+0

Пожалуйста, смотрите обновления выше относительно 'naryFunction'. –

+1

Обновленный ответ. –

+1

Возможно, вы также захотите, чтобы naryFun возвращал нечто, отличное от int. авто приходит на ум. :п – BioTronic

Смежные вопросы