SqueakByExample:4.3

From 흡혈양파의 번역工房
Jump to navigation Jump to search

메시지 구성하기

3종류의 메시지는, 명쾌한 방식으로 메시지가 작성될 수 있도록 해주는 각각 다른 우선순위를 가지고 있습니다.

  1. 단항 메시지는 항상 먼저 전달되며, 그 다음 바이너리 메시지이고, 마지막으로 키워드 메시지가 보내집니다.
  2. 괄호 안에 있는 메시지는 어떤 종류의 메시지 보다 먼저 보내집니다.
  3. 동일한 종류의 메시지는 왼쪽에서부터 오른쪽으로 계산됩니다.


이 규칙들은 매우 자연스러운 읽기 순서로 이어집니다. 지금, 만약 당신이 원하는 순서대로 메시지가 전달되기를 원한다면, 그림 4.3에서 볼수있듯이 원하는만큼 괄호들을 추가로 사용하면 됩니다. 이 그림에서, 메시지 yellow는 단항 메시지이며, 메시지 color:는 키워드 메시지이므로, 우선순위에 의해 메시지 전송식 Color yellow 가 먼저 전달됩니다. 하지만 굳이 Color yellow 주위에(필요없는) 괄호를 넣은 것은 단지 이 메세지전송식이 먼저 보내진다는것을 강조하는 의미가 있습니다. 나머지 장에서는 이 강조할 점을 설명합니다.

그림 4.3: Color yellow 가 전달되어야 하기때문에 단항메시지(yellow)가 먼저 전송됩니다. 이 단항 메세지는 aPen color:의 메세지로 전달될 color 객체를 반환합니다.


단항 > 바이너리 > 키워드

단항 메시지를 먼저보내고, 그 다음이 바이너리 메시지이며, 마지막으로 키워드 메시지가 전달됩니다. 또한 다른 종류의 메시지보다 단항 메시지가 더 높은 우선순위를 가지게 됩니다.


Rule One.단항 메시지가 먼저 전달되고, 그 다음이 바이너리 메시지이며, 마지막으로 키워드 메시지가 전달됩니다.
Unary > Binary > Keyword


아래의 예제에서 볼 수 있듯이, 스몰토크의 문법 규칙은 일반적으로 보내는 메시지가 자연스럽게 보여질 수 있도록 합니다:

1000 factorial / 999 factorial ⇒ 1000
2 raisedTo: 1 + 3 factorial ⇒ 128


안타깝게도, 보내지는 산술메시지들은 단순하게 처리되기 때문에, 바이너리 연산자들에 대한 우선순위를 원할 때마다 괄호를 넣는것이 좋습니다.

1+2*3 ⇒ 9
1 + (2 * 3) ⇒ 7


약간 더 복잡한 (!) 다음 예는 심지어 복잡한 스몰토크 표현식도 자연스럽게 보여질 수 있다는 것을 훌륭하게 보여줍니다:


[:aClass | aClass methodDict keys select: [:aMethod | (aClass>>aMethod) isAbstract ]]
  value: Boolean ⇒ an IdentitySet(#or: #| #and: #& #ifTrue: #ifTrue:ifFalse: #ifFalse: #not #ifFalse:ifTrue:)


이제 우리가 알아보려는것은 어떤 Boolean클래스의 메서드가 추상요소인가 하는 것입니다[1]. 메서드 사전dictionary의 키를 위해, aClass 라는 인자클래스를 이 클래스의 추상메서드를 선택합니다. 그 다음, Boolean이라는 실체적인 값 에 인자 aClass 를(arugument aClass)를 바인딩합니다. 단항 메시지 isAbstract를 메서드에 보내기 전에, 클래스에서 메서드를 선택하는 >> 이항 메시지를 보내는 용도로만 쓰일 괄호가 필요합니다. 결과적으로 Boolean의 실체적 하위클래스인 True와 False로 구현한 메서드를 볼 수 있습니다.


Exmaple. 메시지 aPen color: Color yellow 메시지는, 클래스 Color로 보내지는 단항메세지와 aPen으로 보내지는 Color: 라는 키워드 메시지를 가지고 있습니다. 예 4.1을 보면 우선순위에 의해 단항 메시지가 먼저 전달되기때문에 Color yellow가 전달됩니다(1번). 이 메시지 전달식은 보신바와 같이 메시지 aPen color: aColor 의 인자로서 전달되는 color 객체입니다(2번). 그림 4.3은 어떻게 메시지가 발송되는지 그림으로 보여줍니다.

예 4.1: aPen color: Color yellow의 처리 분석하기
  aPen color : Color yellow  
(1번)   Color yellow "단항 메시지가 먼저 전덜되고"
    ⇒ aColor  
(2번) aPen color : aColor "그 다음 키워드 메시지가 전달됩니다"


Example. 메시지 aPen go:100 + 20 은, 바이너리 메시지 + 20 과 키워드 메시지 go: 를 가지고 있습니다. 바이너리 메시지는 키워드 메시지 전에 전달되므로, 100 + 20 이 먼저 전달됩니다. (1번): 메시지 + 20 은 100 이라는 객체로 전달되며, 숫자 120을 리턴합니다. 그 다음, 메시지 aPen go: 120 에서, 120은 go: 셀렉터의 인자로서 함께 전달됩니다(2번). 예 4.2는 메시지가 어떻게 실행되는지를 보여줍니다.

예 4.2: aPen go: 100 + 20 분해하기
  aPen go : 100 + 20  
(1번)   100 + 20 "이항 메시지가 먼저 전달되고"
    ⇒ 120  
(2번) aPen go : 120 "그 다음 키워드 메시지가 전달됩니다"


Example. 연습으로 하나의 단항 메시지와 하나의 키워드, 하나의 이항 메시지(그림 4.5 참조)로 구성된 Pen new go: 100 + 20 의 처리과정을 분석해 보겠습니다.

UKeyBinPar 01.png UunKeyBin.png
그림 4.4: 이항 메시지가 키워드 메시지보다 우선적으로 전달 되었습니다. 그림 4.5: Pen new go: 100 + 20 구문의 분해


괄호 우선

Rule Two. 괄호로 묶인 메시지는 다른 메시지 보다 먼저 전달됩니다.
(메시지)> 단항 메시지>이항 메시지>키워드 메시지


1.5 tan rounded asString = (((1.5 tan) rounded) asString)  true "여기에는 괄호가 필요하지 않습니다"
3 + 4 factorial  27 "(5040이 아닙니다)"
(3 + 4) factorial  5040


여기서 play 이전에 lowMajorScaleOn:을 강제로 보내려면 괄호가 필요합니다.

(FMSound lowMajorScaleOn: FMSound clarinet) play
"(1) 클라리넷 소리를 만들기 위해 clarinet 메시지를 FMSound 클래스에 보냅니다.
 (2) FMSound에 인자로 보낸 소리를 lowMajorScaleOn: 키워드 메시지로 보냅니다.
 (3) 결과 소리를 재생합니다."


Exmaple. (65@325 extent: 134 @ 100) center 라는 메시지는 상단 왼쪽 꼭지점이 (65,325)이며 그 크기가 134x100인 직사각형의 중심을 반환합니다. 예 4.3은 어떻게 메시지가 분해되며 전달되는지를 보여줍니다. 먼저 괄호 사이에 있는 메시지를 보냅니다. 여기에는 먼저 보내어 포인트 정보를 돌려 받을 (65@325)(1번) 와 134 X 100(2번) 두 개의 이항 메시지가 있으며, 그 다음에 134 X 100 의 정보를 보내고 사각형 정보를 돌려 받을 extent:(3번) 키워드 메시지가 있습니다. 마지막으로 단항 메시지 center는 직사각형에 보내지며(4번), 점 정보를 돌려 받습니다. 100 이라는 객체는 center라는 메세지를 이해하지 못하기때문에 괄호없이 메시지를 처리하면 오류가 발생합니다

예 4.3: 괄호 예제
  (65 @ 325 extent: 134 @ 100) center  
(1번) 65@325     "binary"
  ⇒ aPoint      
(2번)     134@100 "binary"
      ⇒ anotherPoint  
(3번) aPoint extent: anotherPoint "keyword"
  ⇒ aRectangle      
(4번) aRectangle center "unary"
  ⇒ 132@375      


왼쪽에서 오른쪽으로

이제 우리는 다양한 종류의 메시지와 우선권이 취급되는 방식을 알게 되었습니다. 더 생각해봐야할 마지막 질문은, 동일한 우선권을 가진 메시지가 있는경우 어떤 순서로 전달되는지에 대한것입니다. 동일한 우선권을 가진 메시지들은 왼쪽에서 오른쪽 순서로 전달됩니다. 예 4.3 에서 두 개의 점 정보 생성 메시지 (@)가 다른 것보다 먼저 전달되었던 것을 보셨던 것을 기억해주세요.


Rule Three. 동일한 종류의 메시지가 있을 때, 처리 순서는 왼쪽에서 오른쪽 입니다.


Exmaple. 보낼메시지 Pen new down 에서 모든 메시지는 단항 메시지이므로, 가장 왼쪽의 Pen new가 가장 먼저 처리됩니다. 이것은 그림 4.6에서 볼수있듯이, 두 번째 메시지인 down이 새롭게 만들어진 Pen 객체를 리턴합니다.

UcompoUn.png
그림 4.6: Pen new down 분해하기
UcompoNoBracketPar.png


산술적 비일관성

메시지 작성 규칙들은 단순하지만, 바이너리 메시지로 표현된 산술 메시지를 실행하는경우 비일관성이 생기게 됩니다. 이제부터 괄호가 필요한 일반적인 상황들에 대해 알아볼 것입니다.

3+4*5 ⇒ 35 "(not 23) 왼쪽에서 오른쪽으로 이항 메시지를 보냈습니다"
3 + (4 * 5) ⇒ 23
1 + 1/3 ⇒ (2/3) "and not 4/3"
1 + (1/3) ⇒ (4/3)
1/3 + 2/3 ⇒ (7/9) "and not 1"
(1/3) + (2/3) ⇒ (7/9)


Exmaple. 메시지 보내기 20+2 * 5에서, 바이너리 메시지는 + 와 * 뿐 입니다. 그럼에도 불구하고, 스몰토크에서는 연산 + 와 * 를 위한 특정 우선권은 존재하지 않습니다. 이것들은 단지 바이너리 메시지 이므로, * 는 + 에 대해 우선권을 갖지 않습니다. 예 4.4에서 볼 수 있듯이, 이 경우 제일 왼쪽의 +가 전달되며(1번) 그 다음 *가 해당결과에 전달됩니다(2번).

예 4.4: 20+2*5 분해하기
"바이너리 메시지들 중에서 우선순위는 없기때문에, 산술규칙 *가 첫 번째로 전달되어야 하지만 가장 왼쪽의 메시지인 +가 첫 번째로 처리됩니다"
  20 + 2 * 5
(1번) 20 + 2 ⇒ 22
(2번) 2 * 5 ⇒ 110


그림 4.4에서 볼 수 있듯이, 이 메시지 전송 결과는 30이 아니며 110입니다. 생각했던 결과는 아니지만, 메시지를 보내기 위해 사용된 규칙을 바로 따랐을 것입니다. 결국 스몰토크 모델의 단순성때문에 이런 결과가 나오게 됩니다. 원하는 올바른 결과를 얻으려면 괄호를 사용해야 합니다. 메시지를 괄호로 둘러쌓으면 괄호로 둘러싼 메시지를 먼저 처리합니다. 따라서 보내는 메시지 20 + (2 * 5) 가 반환하는 결과는 예 4.5에서 확인할 수 있습니다.

UcompoNumberBracket.png
UKeyUnBinPar.png
UunKeyBinPar.png
그림 4.7 괄호를 사용하는 등가 메시지


예 4.5: 20+(2*5) 분해하기
"괄호로 둘러 쌓인 메시지를 먼저 처리하므로 * 를 + 보다 우선순위를 주어 올바른 처리를 유도합니다."
  20 + (2 * 5)
(1)   (2 * 5) ⇒ 10
(2) 20 + 10 ⇒ 30


스몰토크에서는 연산 +와 * 와 같은 산술 연산자는 다른 우선권을 갖지 않습니다.
+와 *는 단지 바이너리 메시지일 뿐이므로, *는 +에 대해 우선권을 갖지 않습니다.
원하는 결과를 얻기 위해 괄호를 사용합니다.


참고로 첫 번째 규칙에서 바이너리 메시지와 키워드 메시지 이전에 발송된 단항 메시지는 명확한 구문을 위해 괄호 넣기를 하지 않는다라는 규칙을 기억해주세요. 표 4.8은 규칙을 따라 작성한 메시지전송식과 규칙이 존재하지 않을 경우 의미가 같은 메시지전송식을 보여주고 있습니다. 두 메시지는 같은 결과를 내며 같은 결과를 반환합니다.

그림 4.8: 메시지 보내기와 괄호를 넣은 등가물
aPen color: Color yellow aPen color: (Color yellow)
aPen go: 100 + 20 aPen go: (100 + 20)
aPen penSize: aPen penSize + 2 aPen penSize: ((aPen penSize) + 2)
2 factorial +4 2 factorial + 4 (2 factorial) + 4


Notes

  1. 사실, 비슷한 수준으로 작성할 수 있지만 이지만 더 간단한 표현을 쓰겠습니다: Boolean methodDict select: #isAbstract thenCollect: #selector