无法在包含 2 个或更多范围的锁步范围内调用每个

Unable to call each on a lockstep range containing 2 or more ranges

http://dpaste.dzfl.pl/76c79f1f12ab

void main(){
  import std.container;
  import std.stdio;
  import std.algorithm.iteration;
  import std.range;
  Array!int ai = [1,2,3,4];
  Array!int ai1 = [1,2,3,4];
  Array!int ai2 = [1,2,3,4];

  auto arange = lockstep(ai[],ai1[]);
  arange.each!((a,b) => writeln(a, b));

  auto arange2 = lockstep(ai[],ai1[],ai2[]);
  arange2.each!((a,b,c) => writeln(a, b, c));
}

Error: template std.algorithm.iteration.each cannot deduce function from argument types !((a, b, c) => writeln(a, b, c))(Lockstep!(RangeT!(Array!int), RangeT!(Array!int), RangeT!(Array!int))), candidates are: /opt/compilers/dmd2/include/std/algorithm/iteration.d(820):
std.algorithm.iteration.each(alias pred = "a")

arange 有效,但 arange2 无效,因为编译器无法推导该函数。如果我显式添加参数类型,甚至会出现错误。

我认为这个问题是因为 each 的每个重载都有以下模板限制:

void each(Range)(Range r)
    if (isRangeIterable!Range && !isForeachIterable!Range);

void each(Iterable)(Iterable r)
    if (isForeachIterable!Iterable)

isRangeIterable 定义为:

enum isRangeIterable(R) =
    isInputRange!R &&
    (isRangeUnaryIterable!R || isRangeBinaryIterable!R);

isForeachIterable.

也类似

因此 lockstep(Range, Range, Range) 不能与 each 一起使用,因为它只接受一元函数或二元函数(两者都只接受一个范围)。这不能与 lockstep 一起使用,因为 "range"(注意括号)它 returns 只定义一个三元组 opApplyeach 不支持。这就是 lockstep(Range, Range) 有效但 lockstep(Range, Range, Range) 无效的原因(顺便说一句,这只是巧合,因为第一个参数是一个索引,由 Array!int 提供。您的代码确实不要做你认为它做的事)。

这似乎是 lockstep each 设计中的错误;我稍后会为它提交错误报告。至于解决方法,暂时使用常规 foreach 循环而不是 each.

lockstep 实际上 return 不是一个范围;它 return 是一个具有 opApply 方法的结构,foreach 使用该方法。这是因为范围的 front 方法只能 return 一个值,但是 lockstep 必须向 foreach 循环提供 n 值。 each 显然对 opApply 结构有一些支持(尽管双范围锁步 'range' 工作的事实有点错误;第一个参数旨在成为当前索引) .

相反,您可以使用 std.range.zip 函数。这与 lockstep 完全一样,但将结果包装在一个元组中,让 front return 所有结果。不过,它要求您解压元组。

示例:

auto arange2 = zip(ai[],ai1[],ai2[]);
arange2.each!(elems => writeln(elems.expand));

其他答案很好地解释了为什么这不适用于 phobos 中 each 的当前实现。

只是为了好玩,如果你真的想要一个可以使用任意数量参数的each

void each(alias fn, R)(R r) {
  import std.traits    : arity;
  import std.string    : join, format;
  import std.algorithm : map;
  import std.range     : iota;

  // "arg0, arg1, ..."
  enum args = iota(arity!fn)
    .map!(i => "arg%d".format(i))
    .join(",");

  // "ref arg0, ref arg1, ..."
  enum params = iota(arity!fn)
    .map!(i => "ref arg%d".format(i))
    .join(",");

  // foreach(ref arg0, ref arg1, ... ; r) fn(arg0, arg1, ...); 
  mixin(q{
    foreach(%s; r) fn(%s);
  }.format(params, args));
}

unittest {
  import std.range;
  auto a = [1,2];
  auto b = [3,4];
  auto c = [5,6];
  auto d = [7,8];

  lockstep(a,b).each!((ref int a, int b) => ++a);
  assert(a == [2,3]);

  lockstep(a,b,c).each!((ref int a, int b, int c) => ++a);
  assert(a == [3,4]);

  lockstep(a,b,c,d).each!((ref int a, int b, int c, int d) => ++a);
  assert(a == [4,5]);
}

不幸的是,您必须明确指定 lambda 参数的类型。如果你不这样做,它是一个模板而不是委托,arity 无法检索参数计数。

或者您可以只使用 foreach,但是,嘿,这样您就没有借口用混入和模板做一些疯狂的事情了。