2010-03-23 7 views
6

स्मॉलटाक में थोड़ी देर है: - पुनरावृत्ति (विजुअलवर्क्स में) या कंपाइलर-इनलाइनिंग (स्क्वाक/फारो में) के माध्यम से मैसेज लागू किया गया। क्या उनमें से किसी एक का उपयोग किए बिना ऐसी विधि को परिभाषित करने का कोई तरीका है? यदि नहीं, तो क्या उस जगह के लिए कोई सबूत है?क्या एक समय-समय पर परिभाषित करने के लिए संदेश-केवल भाषा में कोई तरीका है बिना रिकर्सन या कंपाइलर चाल के ट्राउ संदेश?

BlockContext>>myWhileTrue: aBlock 
    | start | 
    start := thisContext pc. 
    self value ifFalse: [^self ]. 
    aBlock value. 
    thisContext pc: start 
इसके बजाय प्रत्यावर्तन और संकलक चाल का उपयोग करने का

, ऊपर कोड निष्पादन ढेर पर प्रतिबिंब का उपयोग करता है:

उत्तर

4

जबकि ट्रेस: ​​& जबकि गलत: हमेशा शून्य वापस आते हैं। उदा। अगर वहाँ एक सामान्य पुनरावर्ती परिभाषा है:

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

ifTrue: अगर आत्म मूल्य गलत है शून्य वापस आ जाएगी और इतने मूल्य हमेशा नहीं के बराबर होना चाहिए। यह संकलक के अनुकूलन में परिलक्षित होता है। मूल नीले पुस्तक स्मालटाक-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] 

लेकिन इन दुर्घटना वी एम के दोनों अफसोस कुछ समय दूसरी यात्रा क्योंकि thisContext पीसी अगले चरण पर पीसी का उत्तर न मिले, लेकिन इसके बजाय जो कुछ भी ढेर के शीर्ष है :)

हालांकि बाद निम्नलिखित कार्य करता है:

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] 
5

मैं निम्नलिखित समाधान प्रस्तावित। लूप शुरू होने से पहले विधि वर्तमान प्रोग्राम काउंटर को अस्थायी चर में संग्रहीत करती है और विधि की शुरुआत में वापस कूदने के लिए इसे अंत में रीसेट करता है। कुछ स्मॉलटाक कार्यान्वयन में ऐसा दृष्टिकोण धीमा हो सकता है क्योंकि कुछ स्मॉलटाक बोलियां केवल मांग पर ढेर को दोबारा सुधारती हैं, लेकिन फारो/स्क्वाक में यह चाल काफी व्यावहारिक है।

नोट, उपर्युक्त कोड अंतिम ब्लॉक सक्रियण के परिणाम को #whileTrue के मूल कार्यान्वयन के रूप में उत्तर नहीं देता है: करता है। हालांकि इसे ठीक करने के लिए यह आसान होना चाहिए।

+0

तुम हमेशा एक मदद कर रहे हैं, धन्यवाद लुकास :) –

+0

इस मामले संदर्भ बहाल करने में मूल रूप से अनंत प्रत्यावर्तन के बिना गोटो –

+1

के बराबर है, बयान की एक अनंत संख्या के बिना और बिना एक संदर्भ फिर से शुरू करने की क्षमता असंभव प्रतीत होता है। –

1

तुम भी इसे वापस शुरुआत करने के लिए जाना बनाने के लिए अपवाद संचालक इस्तेमाल कर सकते हैं, लेकिन वह धोखा दे अपवाद हैंडलिंग कोड का इस्तेमाल किया है, तो एक whileTrue के रूप में शामिल कर सकता है: या अन्य पाशन कहीं का निर्माण। तो, मूल रूप से, सवाल यह बताता है कि क्या आप बिना किसी गेटो या रिकर्सन के लूप को कार्यान्वित कर सकते हैं, और मुझे लगता है कि इसका उत्तर नहीं है। तो अगर रिकर्सन वर्जित है, तो आप विधि पीसी को सेट करने या अपवाद का उपयोग करने जैसी तकनीकों से बाहर निकलने की कोशिश कर रहे हैं।

1

बस कार्य करें:

BlockClousure >> whileTrue: aBlock

आत्म मूल्य ifTrue: [ aBlock मूल्य। यह कॉन्टेक्स्ट पुनरारंभ करें। "Pharo पर पुनः आरंभ, VW पर रीसेट"]