在仅消息语言中是否有一种方法可以定义 whileTrue 消息而无需递归或编译器技巧?

发布于 2024-08-26 09:25:58 字数 131 浏览 8 评论 0原文

Smalltalk 有 whileTrue:-Message 通过递归(在 VisualWorks 中)或通过编译器内联(在 Squeak/Pharo 中)实现。有没有一种方法可以定义这样的方法而不使用其中之一?如果没有,是否有证据可以证明这一点?

Smalltalk has the whileTrue:-Message implemented through recursion (in VisualWorks) or through compiler-inlining (in Squeak/Pharo). Is there a way to define such a method without using one of them? If not, is there a proof for that avaiable somewhere?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无需解释 2024-09-02 09:25:58

我提出以下解决方案:

BlockContext>>myWhileTrue: aBlock 
    | start |
    start := thisContext pc.
    self value ifFalse: [ ^ self ].
    aBlock value.
    thisContext pc: start

上面的代码不使用递归和编译器技巧,而是使用执行堆栈上的反射。在循环开始之前,该方法将当前程序计数器存储在临时变量中,并在结束时重置它以跳回方法的开头。在某些 Smalltalk 实现中,这种方法可能会很慢,因为某些 Smalltalk 方言仅按需具体化堆栈,但在 Pharo/Squeak 中,此技巧非常实用。

请注意,上面的代码并不像 #whileTrue: 的原始实现那样回答最后一个块激活的结果。不过解决这个问题应该很容易。

I propose the following solution:

BlockContext>>myWhileTrue: aBlock 
    | start |
    start := thisContext pc.
    self value ifFalse: [ ^ self ].
    aBlock value.
    thisContext pc: start

Instead of using recursion and compiler tricks, the above code uses reflection on the execution stack. Before the loop starts the method stores the current program counter in a temporary variable and resets it at the end to jump back to the start of the method. In some Smalltalk implementations such an approach might be slow as some Smalltalk dialects reify the stack on demand only, but in Pharo/Squeak this trick is quite practicable.

Note, the above code does not answer the result of the last block activation as the original implementation of #whileTrue: does. It should be easy enough to fix that though.

救星 2024-09-02 09:25:58

whileTrue: & whileFalse:总是返回nil。
例如,如果有一个正常的递归定义:

whileTrue: aBlock
    ^self value ifTrue: [self whileTrue: aBlock]

如果 self 值为 false,则 ifTrue: 将返回 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

or??

BlockContext>>myWhileTrue: aBlock 
    | start |
    start := thisContext pc.
    ^self value ifTrue:
        [aBlock value.
         thisContext pc: start]

但可惜的是,这两个方法都会在第二次迭代后的某个时间使虚拟机崩溃,因为 thisContext 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]

whileTrue: & whileFalse: always return nil.
e.g. if there is a normal recursive definition:

whileTrue: aBlock
    ^self value ifTrue: [self whileTrue: aBlock]

the ifTrue: will return nil if self value is false and so the value should always be nil. That's reflected in the compiler's optimization. The original blue book Smalltalk-80 V2 definition is

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]

So just change your's to

BlockContext>>myWhileTrue: aBlock 
    | start |
    start := thisContext pc.
    self value ifFalse: [ ^ nil ].
    aBlock value.
    thisContext pc: start

or??

BlockContext>>myWhileTrue: aBlock 
    | start |
    start := thisContext pc.
    ^self value ifTrue:
        [aBlock value.
         thisContext pc: start]

But alas both of these crash the VM sometime after the second iteration because thisContext pc doesn't answer the pc on the next iteration, but instead whatever the top of stack is :)

However the following does work:

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]
刘备忘录 2024-09-02 09:25:58

您还可以使用异常处理程序使其返回到开头,但如果异常处理代码在某处使用 whileTrue: 或其他循环构造,则可能会被视为作弊。所以,基本上,问题归结为是否可以在没有 goto 或递归的情况下实现循环,我认为答案是否定的。因此,如果禁止递归,您就只能尝试使用设置方法 pc 或使用异常等技术拼凑出 goto。

You could also use an exception handler to make it go back to the beginning, but that might count as cheating if the exception handling code used a whileTrue: or other looping construct somewhere. So, basically, the question boils down to whether you can implement a loop without either goto or recursion, and I think the answer to that is no. So if recursion is forbidden, you're left trying to cobble together a goto out of techniques like setting the method pc or using an exception.

花落人断肠 2024-09-02 09:25:58

只需执行:

BlockClousure>>whileTrue: aBlock

self value ifTrue: [
a块值。
thisContext 重新启动。 “在 pharo 上重新启动,在 VW 上重置”]

Just do:

BlockClousure>>whileTrue: aBlock

self value ifTrue: [
aBlock value.
thisContext restart. "restart on pharo, reset on VW" ]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文