请考虑以下代码:
enum size = 16;
double[size] arr1 = [...];
double[size] arr2 = [...];
process = (double x) { return (x + 1); };
arr2[] = map!(process)(arr1[]); // here
我无法将map
的结果转换回我的普通数组。问题不仅适用于map
,还适用于take
,repeat
以及std.algorithm
和std.range
对范围进行操作的所有精美工具。
在这项任务中,我得到Error: cannot implicitly convert expression (map(arr1[])) of type Result to double[]
。如何在不使用
uint i = 0;
foreach (x; map!(process)(arr1[])) {
arr2[i] = x;
i++;
}
此外,有人可以解释一下,为什么我必须使用静态数组调用map!(process)(arr1[])
而不是map!(process)(arr1)
?静态数组不应该与动态的迭代方法兼容,或者我没有得到什么?
此外,似乎简单的枚举语法foreach (index, item; sequence)
不适用于范围 - 是否有解决方法?我想原因与为什么范围不能分配给数组切片的原因相同。
答案 0 :(得分:11)
map
和filter
之类的函数返回范围,而不是数组,所以简单地分配给数组不会比将string
分配给{{1}更有效将要起作用。他们是不同的类型。对于许多基于范围的函数(包括wstring
和map
),它们返回的范围实际上是惰性的,以避免不必要的计算,这使得它们与数组的兼容性更低。解决方案是使用std.array.array
,它接受一个范围并从中创建动态数组。所以,你可以做到
filter
但是,我建议在实际需要之前不要将范围转换为数组,因为它可能导致不必要的计算,这意味着分配一个新数组。如果您确实需要一个数组,那么请务必使用std.array.array来转换范围,但如果您不需要实际数组,则在该范围上运行通常会更有效。但是,如果要将结果转换为静态数组而不是动态数组,那么最好只是在循环中分配每个元素(并且可能完全跳过auto arr = array(map!process(origArray));
),因为使用std.array.array然后会分配一个动态数组,一旦你分配给静态数组就不会使用它。这是浪费记忆。
另外,请注意,使用具有基于范围的函数的静态数组可能会有风险,因为它们必须对静态数组进行切片以获取要处理的基于范围的函数的动态数组,并且如果该动态数组超出范围声明了静态数组,然后你泄漏了对不再存在的数据的引用。例如,
map
非常糟糕。但是,只要你完成使用切片并且在退出带有静态数组的范围之前没有任何东西引用它(包括可能已经创建的任何范围),你应该没问题。但 是值得注意的事情。
关于必须对静态数组进行切片的问题,你真的应该将其作为一个单独的问题,但是与其相关的两个现有问题是this one和this one。它几乎归结为IFTI(隐式函数模板实例化)使用它给出的确切类型进行实例化,静态数组既不是动态数组也不是范围,因此任何模板化函数都需要动态数组或者range将无法使用静态数组进行编译。编译器将隐式地切片静态数组以将它们转换为动态数组,用于显式获取动态数组的函数,但这些隐式转换不会发生在模板实例化中,因此必须将静态数组显式切片为将它们传递给基于范围的函数。
关于将foreach与指数和范围一起使用的问题,再次,你不应该在同一个问题中提出多个问题。请为您提出的每个问题发布单独的问题。但它归结为
auto func()
{
int[5] arr;
return map!process(arr[]);
}
降低到接近
的程度foreach(elem; range)
{
//stuff
}
这根本不涉及指数。 可以进行更改以便为您创建一个索引变量,但是对于范围而言,它的索引在每次迭代时都会像一个迭代一样迭代并不总是有意义的(通常很好) ,所以还没有完成。这很简单,可以添加自己的计数器。
for(; !range.empty; range.popFront())
{
auto elem = range.front;
//stuff
}
{
size_t i;
foreach(elem; range)
{
//stuff
++i;
}
}
确实支持在foreach中使用索引,但它不是范围,并且不适用于基于范围的函数。