在没有递归或编译器技巧的情况下,在仅消息语言中是否有一种方法来定义whileTrue消息?

时间:2010-03-23 14:02:51

标签: smalltalk

Smalltalk具有whileTrue:-Message通过递归(在VisualWorks中)或通过编译器内联(在Squeak / Pharo中)实现。有没有办法在不使用其中一种方法的情况下定义这样的方法?如果没有,那么某处有可用的证明吗?

4 个答案:

答案 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,重置大众”]