Rust如何找出迭代器的上限?
我正在浏览几个生锈的例子,其中有一段特殊的代码,我并不真正理解它是如何工作的。特别是高阶函数。我的重点是这段代码:Rust如何找出迭代器的上限?,rust,Rust,我正在浏览几个生锈的例子,其中有一段特殊的代码,我并不真正理解它是如何工作的。特别是高阶函数。我的重点是这段代码: let sum_of_squared_odd_numbers: u32 = (0..).map(|n| n * n) // All natural numbers squared .take_while(|&n| n < upper) // Below upper limit .filter(|n|
let sum_of_squared_odd_numbers: u32 =
(0..).map(|n| n * n) // All natural numbers squared
.take_while(|&n| n < upper) // Below upper limit
.filter(|n| is_odd(*n)) // That are odd
.fold(0, |sum, i| sum + i); // Sum them
设奇数的平方和:u32=
(0...map(|n | n*n)//所有自然数的平方
.当(|&n | n<上限)//低于上限时
.filter(|n |为_奇数(*n))//为奇数的
.fold(0,| sum,i | sum+i);//求和
以下是我的问题:
(0..)
何时结束?循环是否在编译时展开,是否对所有lambda进行了计算(0..).map(|n | n*n)
本身将占用O(n)内存我不是铁锈专家,但是评论
// All natural numbers squared
告诉我,列表(或流、枚举等)无法完全计算。所以我会把赌注押在某种懒惰的评估上
在这种情况下,范围将携带一个内部状态,每次使用时都会计算下一个元素。然后,通过“存储”相应的实现(而不是直接对其进行评估)来实现反变形(折叠、映射)
更新:我在部分时忽略了拍摄。这种方法似乎有责任(也根据评论)实际强制进行评估。之前的一切只计算范围抽象,即没有任何具体元素。这是可能的,因为函数可以被组合
这种行为的标准例子是我不是铁锈专家,而是评论
// All natural numbers squared
告诉我,列表(或流、枚举等)无法完全计算。所以我会把赌注押在某种懒惰的评估上
在这种情况下,范围将携带一个内部状态,每次使用时都会计算下一个元素。然后,通过“存储”相应的实现(而不是直接对其进行评估)来实现反变形(折叠、映射)
更新:我在
部分时忽略了拍摄。这种方法似乎有责任(也根据评论)实际强制进行评估。之前的一切只计算范围抽象,即没有任何具体元素。这是可能的,因为函数可以组合
这种行为的标准示例是编译程序不知道(0..)
何时结束。但是,迭代器是惰性的(正如您链接到的页面上所提到的),并且.take_while(|&n | n
语句将在n
大于或等于upper
时停止序列,编译器不知道(0..)
何时结束。但是,迭代器是惰性的(正如您链接到的页面上所提到的),只要大于或等于上限,take_while(|&n | n
语句就会停止序列
编译器如何知道(0..)
何时结束
编译器根本不知道。这是一个范围文字,特别是一个。注意,它实现了trait。迭代器的核心部分是:
无论何时在最终适配器上调用next
,适配器堆栈的每一层都做了足够的工作来获得下一个值。在原始示例中,fold
是一个迭代器终止符,它使用整个迭代器,调用next
,直到没有更多值为止
†正如您所说,您并不想尝试超过某个范围的最大值,这取决于它是在调试模式还是发布模式下构建的
编译器如何知道(0..)
何时结束
编译器根本不知道。这是一个范围文字,特别是一个。注意,它实现了trait。迭代器的核心部分是:
无论何时在最终适配器上调用next
,适配器堆栈的每一层都做了足够的工作来获得下一个值。在原始示例中,fold
是一个迭代器终止符,它使用整个迭代器,调用next
,直到没有更多值为止
†作为一个例子,您并不想尝试超过某个范围的最大值,因为它也会超过最大值,这取决于它是在调试模式还是发布模式下构建的。您的意思是说让foo=(0...map(| n | n*n);让bar=foo.take_while(…);bar
的编译方式与let bar=(0..).map(|n | n*n)不同。以_while(…)
为例?Haskell是一个有点奇怪的例子,因为它使用隐式thunks,并且比Rust更“高级”。一个更明显的例子是类似于Ocaml的惰性列表,它只是“A*(unit->”A stream)的Nil | Cons
。主要区别在于Haskell的懒惰是发生在代码上的,而显式的懒惰是从代码中产生的。take\u而
不负责任map
,take_,而,filter
是接受并返回惰性迭代器的适配器fold
是消费者,它接受迭代器并生成值<代码>take_while
的作用是,如果谓词失败,它可以结束流。你的意思是说让foo=(0...map(| n | n*n);让bar=foo.take_while(…);bar
的编译方式与let bar=(0..).map(|n | n*n)不同。以_while(…)
为例?Haskell是一个有点奇怪的例子,因为它使用隐式thunks,并且比Rust更“高级”。一个更明显的例子是类似于Ocaml的惰性列表,它只是“A*(unit->”A stream)的Nil | Cons
。主要的区别在于Haskell的懒惰是发生在代码中的事情,而显式的懒惰则是你所做的事情
// Note the type is
// Filter<TakeWhile<Map<RangeFrom<_>, [closure]>, [closure]>, [closure]>
let () =
(0..)
.map(|n| n * n)
.take_while(|&n| n < 20)
.filter(|n| n % 2 == 0);
// At this point, we still haven't even looked at a single value