Smalltalk具有whileTrue:-Message通过递归(在VisualWorks中)或通过编译器内联(在Squeak / Pharo中)实现。有没有办法在不使用其中一种方法的情况下定义这样的方法?如果没有,那么某处有可用的证明吗?
答案 0 :(得分:5)
我提出以下解决方案:
BlockContext>>myWhileTrue: aBlock
| start |
start := thisContext pc.
self value ifFalse: [ ^ self ].
aBlock value.
thisContext pc: start
上述代码不使用递归和编译器技巧,而是使用执行堆栈上的反射。在循环开始之前,该方法将当前程序计数器存储在临时变量中,并在最后重置它以跳回到方法的开头。在一些Smalltalk实现中,这种方法可能会很慢,因为一些Smalltalk方言仅仅根据需要重新启用堆栈,但在Pharo / Squeak中,这个技巧非常实用。
注意,上面的代码没有回答最后一个块激活的结果,因为#whileTrue:的原始实现。应该很容易解决这个问题。
答案 1 :(得分:4)
whileTrue:& whileFalse:总是返回nil。 例如如果存在正常的递归定义:
whileTrue: aBlock
^self value ifTrue: [self whileTrue: aBlock]
ifTrue:如果self值为false,则返回nil,因此值应始终为nil。这反映在编译器的优化中。最初的蓝皮书Smalltalk-80 V2定义是
whileTrue: aBlock
"Evaluate the argument, aBlock, as long as the value
of the receiver is true. Ordinarily compiled in-line.
But could also be done in Smalltalk as follows"
^self value
ifTrue:
[aBlock value.
self whileTrue: aBlock]
所以只需将你的改变为
BlockContext>>myWhileTrue: aBlock
| start |
start := thisContext pc.
self value ifFalse: [ ^ nil ].
aBlock value.
thisContext pc: start
或??
BlockContext>>myWhileTrue: aBlock
| start |
start := thisContext pc.
^self value ifTrue:
[aBlock value.
thisContext pc: start]
但是这两个都在第二次迭代后的某个时候崩溃了,因为在下一次迭代时,这个文本pc不会回答pc,而是堆栈的顶部是:)
但以下情况确实有效:
ContextPart methods for controlling
label
^{ pc. stackp }
goto: aLabel
"N.B. we *must* answer label so that the
top of stack is aLabel as it is when we send label"
pc := aLabel at: 1.
self stackp: (aLabel at: 2).
^aLabel
BlockContext>>myWhileTrue: aBlock
| label |
label := thisContext label.
self value ifFalse: [^nil].
aBlock value.
thisContext goto: label
BlockClosure>>myWhileTrue: aBlock
| label |
label := thisContext label.
^self value ifTrue:
[aBlock value.
thisContext goto: label]
答案 2 :(得分:1)
您还可以使用异常处理程序使其返回到开头,但如果异常处理代码在某处使用了whileTrue:或其他循环结构,那么这可能会被视为作弊。所以,基本上,问题归结为你是否可以实现一个没有goto或递归的循环,我认为答案是否定的。因此,如果禁止递归,那么你就会试图通过设置方法pc或使用异常等方法来拼凑出来。
答案 3 :(得分:1)
只是做:
BlockClousure>> whileTrue:aBlock
自我价值ifTrue:[ aBlock值。 thisContext重启。 “重启pharo,重置大众”]