【问题标题】:Unable to call each on a lockstep range containing 2 or more ranges无法在包含 2 个或更多范围的锁步范围上调用每个
【发布时间】:2015-11-18 12:16:00
【问题描述】:

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));
}

错误:模板 std.algorithm.iteration.each 不能推导出函数 从参数类型 !((a, b, c) => writeln(a, b, c))(Lockstep!(RangeT!(Array!int), RangeT!(Array!int), RangeT!(Array!int))),候选对象是: /opt/compilers/dmd2/include/std/algorithm/iteration.d(820):
std.algorithm.iteration.each(别名 pred = "a")

arange 有效,但 arange2 无效,因为编译器无法推断该函数。即使我显式添加参数类型也会出现错误。

【问题讨论】:

标签: d


【解决方案1】:

我认为这个问题是因为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 一起使用,因为它返回的“范围”(注意括号)只定义了一个三元组opApplyeach 不支持。这就是为什么lockstep(Range, Range) 有效但lockstep(Range, Range, Range) 无效(顺便说一句,这只是巧合,因为第一个参数是一个索引,由Array!int 提供。你的代码没有按照你的想法做确实)。

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

【讨论】:

    【解决方案2】:

    lockstep 实际上并没有返回范围;它返回一个带有opApply 方法的结构,foreach 使用该方法。这是因为范围的front 方法只能返回一个值,而lockstep 必须将n 值提供给foreach 循环。 each 显然对 opApply 结构有一些支持(尽管事实上两个范围的锁步“范围”工作是一个错误;第一个参数旨在作为当前索引)。

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

    例子:

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

    【讨论】:

    • 他们无法使用zip,因为它目前不支持更改其front 值。
    【解决方案3】:

    其他答案很好地解释了为什么这不适用于 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,但是,嘿,那么你就没有借口用 mixins 和模板做疯狂的事情了。

    【讨论】:

    • 实际上,也许您可​​以通过查看范围而不是 lambda 来确定 arity...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    相关资源
    最近更新 更多