作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
假设我有一个尾递归方法,它循环遍历 Stream
的元素。 ,像这样(简化代码,未测试):
@tailrec
def loop(s: Stream[X], acc: Y): Y =
s.headOption match {
case None => acc
case Some(x) => loop(s.tail, accumulate(x, acc))
}
list
是
List[X]
然后代码正在调用
loop(list.sliding(n).toStream, initialY)
foldLeft
),但非简化代码并没有一次只循环一个元素(有时使用
s
代替
s.tail
有时
s.tail.dropWhile(...)
是使用。所以我想找出如何正确使用
Stream
。
最佳答案
tl;博士:您的方法 loop
是正确的,将不会引用流的头部。您可以在无限 Stream
上测试它.
让我们将您的代码示例简化到极限:
class Test {
private[this] var next: Test = _
final def fold(): Int = {
next = new Test
next.fold()
}
}
loop
方法也是某个对象的方法。
final
(就像
Stream#foldLeft
) - 这很重要。
scalac -Xprint:all test.scala
尾递归优化后,你会得到这个:
final def fold(): Int = {
<synthetic> val _$this: Test = Test.this;
_fold(_$this: Test){
({
Test.this.next = new Test();
_fold(Test.this.next)
}: Int)
}
};
java
字节码。
method of object
这样的东西。 .所有方法都是“静态的”。和
this
只是方法的第一个参数。如果该方法是虚拟的,则有这样的东西
vtable ,但是我们的方法是final的,所以在这种情况下不会有动态调度。
this
只是方法的第一个变量(索引 0)。
javap -c Test.class
):
public final int fold();
Code:
0: aload_0
1: new #2 // class Test
4: dup
5: invokespecial #16 // Method "<init>":()V
8: putfield #18 // Field next:LTest;
11: aload_0
12: getfield #18 // Field next:LTest;
15: astore_0
16: goto 0
static foo(var this: Test): Int {
:start // label for goto jump
// place variable `this` onto the stack:
// 0: aload_0
// create new `Test`
// 1: new #2 // class Test
// invoke `Test` constructor
// 4: dup
// 5: invokespecial #16 // Method "<init>":()V
// assign `this.next` field value
// 8: putfield #18 // Field next:LTest;
this.next = new Test
// place `this.next` onto the stack
// 11: aload_0
// 12: getfield #18 // Field next:LTest;
// assign `this.next` to variable `this`!
// 15: astore_0
this = this.next // we have no reference to the previous `this`!
// 16: goto 0
goto :start
}
this = this.next
我们没有引用以前的
this
在堆栈上或在第一个变量中。和之前的
this
可以通过
GC
删除!
tail.foldLeft(...)
在
Stream#foldLeft
将替换为
this = this.tail, ...; goto :start
.从
this
只是方法
@tailrec
的第一个参数之前
foldLeft
声明是有道理的。
scalac -Xprint:all test.scala
结果:
final def method(a: A, b: B, ...): Res = {
<synthetic> val _$this: ThisType = ThisType.this;
_method(_$this: Test, a: A, b: B, ...){
({
// body
_method(nextThis, nextA, nextB, ...)
}: Res)
}
};
final def method(var this: ThisType, var a: A, var b: B, ...): Res = {
// _method(_$this: Test, a: A, b: B, ...){
:start
// body
// _method(nextThis, nextA, nextB, ...)
this = nextThis
a = nextA
b = nextB
...
goto :start
};
scalac -Xprint:all
之后会得到的在您的
loop
方法,但是
body
将是巨大的。所以在你的情况下:
...
case Some(x) =>
this = this
s = s.tail
acc = accumulate(x, acc)
goto :start
...
s = s.tail
你没有提到流的头部。
关于Scala 流 : how to avoid to keeping a reference to the head (and other elements),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21141853/
我是一名优秀的程序员,十分优秀!