![](/img/trans.png)
[英]How is Smalltalk's whileTrue message implemented behind the scenes?
[英]Is there a way in a message-only language to define a whileTrue message without recursion or compiler tricks?
Smalltalk有whileTrue:-Message通過遞歸(在VisualWorks中)或通過編譯器內聯(在Squeak / Pharo中)實現。 有沒有辦法在不使用其中一種方法的情況下定義這樣的方法? 如果沒有,是否有可以在某處獲得的證明?
我提出以下解決方案:
BlockContext>>myWhileTrue: aBlock
| start |
start := thisContext pc.
self value ifFalse: [ ^ self ].
aBlock value.
thisContext pc: start
上面的代碼使用了對執行堆棧的反射,而不是使用遞歸和編譯器技巧。 在循環開始之前,該方法將當前程序計數器存儲在臨時變量中,並在最后重置它以跳回到方法的開頭。 在一些Smalltalk實現中,這種方法可能會很慢,因為一些Smalltalk方言僅僅根據需要重新啟用堆棧,但在Pharo / Squeak中,這種技巧非常實用。
注意,上面的代碼沒有回答最后一個塊激活的結果,因為#whileTrue:的原始實現。 雖然應該很容易解決這個問題。
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]
您還可以使用異常處理程序使其返回到開頭,但如果異常處理代碼在某處使用了whileTrue:或其他循環結構,那么這可能會被視為作弊。 所以,基本上,問題歸結為你是否可以實現一個沒有goto或遞歸的循環,我認為答案是否定的。 因此,如果禁止遞歸,那么你就會試圖將諸如設置方法pc或使用異常之類的技術拼湊出來。
做就是了:
BlockClousure >> whileTrue:aBlock
自我值ifTrue:[aBlock值。 thisContext重啟。 “重啟pharo,重置大眾”]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.