2010-03-23 4 views

Respuesta

4

whileTrue: & whileFalse: always return nil. p. si hay una definición recursiva normales:

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

la ifTrue: volverá nula si el valor de uno mismo es falso y lo que el valor debe ser siempre cero. Eso se refleja en la optimización del compilador. El libro azul original de Smalltalk-80 V2 definición es

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] 

Así que acaba de cambiar su de a

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

o ??

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

Pero, por desgracia ambos accidente de la máquina virtual en algún momento después de la segunda iteración debido thisContext pc no responde a la PC en la siguiente iteración, sino que cualquiera que sea la parte superior de la pila es :)

Sin embargo, el Cómo funciona el siguiente:

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

propongo la siguiente solución:

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

En lugar de utilizar la recursividad y trucos de compilación, el código anterior utiliza la reflexión sobre la pila de ejecución. Antes de que comience el ciclo, el método almacena el contador del programa actual en una variable temporal y lo restablece al final para volver al principio del método. En algunas implementaciones de Smalltalk, este enfoque puede ser lento, ya que algunos dialectos de Smalltalk reifican la pila bajo demanda solamente, pero en Pharo/Squeak este truco es bastante practicable.

Nota: el código anterior no responde al resultado de la última activación de bloque ya que la implementación original de #whileTrue: does. Sin embargo, debería ser fácil solucionarlo.

+0

Siempre es una ayuda, gracias Lukas :) –

+0

En este caso, restaurar el contexto es básicamente el equivalente a GOTO –

+1

sin recursión infinita, sin un número infinito de declaraciones y sin la capacidad de reanudar un contexto parece ser imposible. –

1

También podría usar un manejador de excepciones para que vuelva al principio, pero eso podría contar como hacer trampa si el código de manejo de excepciones usó un whileTrue: u otro constructo de bucle en alguna parte. Entonces, básicamente, la pregunta se reduce a si puedes implementar un ciclo sin goto o recursión, y creo que la respuesta es no. Por lo tanto, si la recursión está prohibida, se trata de improvisar un conjunto de técnicas como establecer el método de PC o usar una excepción.

1

Just do:

BlockClousure >> whileTrue: unBloque

valor propio if True: [ aBlock value. thisContext restart. "restart on pharo, reset on VW"]

Cuestiones relacionadas