2010-03-23 1 views
6

Smalltalk에는 whileTrue : - 재귀 (VisualWorks) 또는 컴파일러 인라이닝 (Squeak/Pharo)을 통해 구현 된 메시지가 있습니다. 그 중 하나를 사용하지 않고 그러한 방법을 정의 할 수있는 방법이 있습니까? 그렇지 않다면 어딘가에 그 증거가 있습니까?재귀 또는 컴파일러 트릭없이 whileTrue 메시지를 정의하는 메시지 전용 언어의 방법이 있습니까?

대신 재귀 및 컴파일러 트릭을 사용
BlockContext>>myWhileTrue: aBlock 
    | start | 
    start := thisContext pc. 
    self value ifFalse: [^self ]. 
    aBlock value. 
    thisContext pc: start 

은, 위의 코드가 실행 스택에 리플렉션을 사용

답변

4

while 트러스 : & while : 항상 nil을 반환합니다. 예 : 일반 재귀 정의가있는 경우 :

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

ifTrue : 자기 값이 false 인 경우 nil을 반환하고 그래서 값은 항상 nil을해야합니다. 이것은 컴파일러의 최적화에 반영됩니다. 원래 파란색 책 스몰 토크-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] 

그러나 이러한 사고는 VM 모두 슬프게도

언젠가 스택의 상단이 :) 그러나

무엇이든 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] 
5

나는 다음과 같은 솔루션을 제안한다. 루프가 시작되기 전에 메서드는 현재 프로그램 카운터를 임시 변수에 저장하고 마지막에 다시 설정하여 메서드 시작으로 돌아갑니다. 일부 스몰 토크 구현에서는 일부 스몰 토크 사투리가 요청에 따라 스택을 구체화하기 때문에 이러한 접근법은 느릴 수 있지만 Pharo/Squeak에서는이 기법이 매우 실용적입니다.

위의 코드는 #whileTrue : does의 원래 구현으로 마지막 블록 활성화 결과에 응답하지 않습니다. 그것도 쉽게 고칠 수 있어야합니다.

+0

당신은 항상 도움이, 덕분에 루카스 :) –

+0

이 경우에는 문맥을 복원하는 것은 기본적으로 GOTO –

+1

과 같습니다. 무한 반복없이 무한한 수의 구문없이 문맥을 재개하는 능력은 불가능한 것처럼 보인다. –

1

예외 처리기를 사용하여 처음으로 되돌릴 수도 있지만 예외 처리 코드에서 whileTrue : 또는 다른 반복적 인 구문을 어딘가에 사용하면 부정 행위로 간주 될 수 있습니다. 그래서 기본적으로, 문제는 goto 나 재귀없이 루프를 구현할 수 있는지 여부에 달려 있습니다. 그 대답은 no라고 생각합니다. 따라서 재귀가 금지되면, 메소드 pc 설정이나 예외 사용과 같은 기술로 goto를 함께 쓰려고 노력합니다.

1

그냥 수행

BlockClousure >> whileTrue : aBlock

자체 값 ifTrue : [ a 블록 값. thisContext restart. "pharo에서 다시 시작, 폭스 바겐에서 다시 시작"