SqueakByExample:4.3
메시지 작성(Message composition)
3종류의 메시지는, 우아한 방식으로 메시지가 작성될 수 있도록 해주는 각각 다른 우선권(precedence)을 갖고 있습니다.
- 단항 메시지(Unary messages)는 항상 먼저 발송되며, 그 다음 바이너리 메시지이고, 마지막으로 키워드 메시지가 발송됩니다.
- 괄호 안에 있는 메시지는 어떤 종류의 메시지 보다 먼저 발송됩니다.
- 동일한 종류의 메시지는 왼쪽에서부터 오른쪽으로 평가됩니다.
이 규칙들은 매우 자연스러운 읽기 순서로 이어집니다. 지금, 만약 여러분이, 여러분이 원하는 순서대로 메시지가 발송되기를 원하신다면, 그림 4.3에 보이는 것처럼 언제나 더 많은 괄호들을 넣을 수 있습니다. 이 그림에서, 메시지 yellow는 단항 메시지이며, 메시지 color:는 키워드 메시지이므로, 메시지 보내기(message send) Color yellow가 먼저 발송되었습니다. 괄호에 있는 메시지 보내기(message sends)는 먼저 발송되었음에도 불구하고, Color yellow 주위에 (필요 없는) 괄호를 넣은 것은 단지 그것이 먼저 발송될 것이라는 것을 강조한 것입니다. 나머지 섹션은 이 강조점들을 묘사한 것입니다.
단항(Unary) >바이너리(Binary) > (키워드)Keywords
단항 메시지(Unary messages)는 처음으로 발송되며, 그 다음 바이너리 메시지이고, 마지막으로 키워드 메시지가 발송됩니다. 우리는 또한 다른 종류의 메시지보다 단항 메시지가 더 높은 우선권을 갖고 있다고 말합니다.
Rule One.
단항 메시지(Unary messages)는 처음으로 발송되며, 그 다음 바이너리 메시지이고, 마지막으로 키워드 메시지가 발송됩니다.
Unary > Binary > Keyword
이 예들이 보여주듯이, 스몰토크의 구문 규칙은 일반적으로 메시지 발송이(message sends)가 자연스럽게 읽힐 수 있도록 해줍니다:
1000 factorial / 999 factorial | ⇒ 1000 |
2 raisedTo: 1 + 3 factorial | ⇒ 128 |
안타깝게도, 규칙들은 산술적 메시지 발송(arithmetic message sends)을 하기에는 너무 단순하므로, 바이너리 연산자들에 우선권을 부여하기를 원할 때마다 괄호를 넣을 수 있습니다:
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:)
여기서 우리가 알기 원하는 것은 어떤 불리언 클래스 메소드가 추상적인가[1] 입니다.
우리는 인수 클래스의 메소드 사전 키들을 위해, 몇 가지 인수 클래스인 aClass를 요청하였으며, 추상적인 클래스의 메소드들을 선택하였습니다. 그 다음, 우리는 실체적인 값 불리언에 인수 aClass를(augument aClass)를 묶었습니다. 우리는 단항 메시지 isAbstract를 메소드에 보내기 전에, 클래스에서 메소드를 선택하는 바이너리 메시지 >> 보내는 용도로만 쓰일 괄호가 필요합니다. 결과는 우리에게 어떤 메소드들이, True 또는 False로 나타나는 불리언의 실체적 서브클래스들로, 실행되어야만 할 것인지를 보여줍니다.
Exmaple. 메시지 aPen color: Color yellow 에서, 클래스 Color와 aPen에 발송된 키워드 메시지 Color:에는 오직 하나의 단항 메시지가 존재합니다. 첫 번째로 단항 메시지가 발송되므로, 메시지 발송 Color yellow가 발송됩니다. (1) 이것은 예 4.1에 보이는 것처럼 메시지 aPen color: aColor (2)의 인수로서 패스되는 color 오브젝트입니다. 그림 4.3은 어떻게 메시지가 발송되는지 그림으로 보여줍니다.
예 4.1: aPen color: Color yellow의 평가 분해하기 | |||
aPen color | : Color yellow | ||
(1) | Color yellow | "단항메시지가 먼저 발송되고" | |
⇒ aColor | |||
(2) | aPen color | : aColor | "그 다음 키워드 메시지가 발송됨" |
Exmaple. 메시지 aPen go:100 + 20에서, 바이너리 메시지 +20과 키워드 메시지 go:가 있습니다. 바이너리 메시지는 키워드 메시지 전에 발송되므로, 100+20이 먼저 발송됩니다. (1): 메시지 +20은 오브젝트 100에 발송되며, 숫자 120을 리턴합니다. 그 다음, 메시지 aPen go: 120은 인수로서, 120과 함께 발송됩니다 (2). 예 4.2는 메시지가 어떻게 실행되는지를 보여줍니다.
예 4.2: aPen go: 100 + 20 분해하기 | |||
aPen go | : 100 + 20 | ||
(1) | 100 + 20 | "바이너리 메시지가 첫번째로 전송되고" | |
⇒ 120 | |||
(2) | aPen go | : 120 | "그 다음 키워드 메시지가 전송됩니다" |
그림 4.4: 바이너리 메시지가 키워드 메시지 이전에 발송 되었습니다. | 그림 4.5: Pen new go: 100 + 20 분해 |
Exmaple. 연습으로서, 우리는 여러분이 한 개의 단항메시지와 한 개의 키워드 그리고 한 개의 바이너리 메시지(그림 4.5를 보십시오)로 구성되어 있는 Pen new go:100+20의 평가를 분해 합니다.
괄호 우선
Rule Two. 괄호로 묶인 메시지는 다른 메시지 보다 먼저 발송됩니다.
(메시지)> 단항 메시지>바이너리 메시지>키워드 메시지
1.5 tan rounded asString = (((1.5 tan) rounded) asString) ⇒ true "parentheses not needed here"
3 + 4 factorial ⇒ 27 "(not 5040)"
(3 + 4) factorial ⇒ 5040
여기서 우리는 실행하기 전 lowMajorScaleOn:을 강제로 보내기 전에 괄호가 필요합니다.
(FMSound lowMajorScaleOn: FMSound clarinet) play
"(1) send the message clarinet to the FMSound class to create a clarinet sound.
(2) send this sound to FMSound as argument to the lowMajorScaleOn: keyword
message.
(3) play the resulting sound."
Exmaple. 메시지 (65@325 extent: 134 @ 100) center는 상단 왼쪽 꼭지점이 (65,325)이며 그 크기가 134x100인 직사각형의 center(중심)을 리턴합니다. 예 4.3은 어떻게 메시지가 분해되며 발송되는지를 보여줍니다. 괄호안에 있는 메시지가 제일 먼저 발송됩니다: 65@325 와 134@100, 두 개의 바이너리 메시지를 포함한 괄호로 묶인 메시지가 제일 먼저 발송되며, 이어서 꼭지점들을 리턴하고, 그 다음 키워드 메시지 extent:가 발송되며, 직사각형을 리턴 합니다. 마지막으로 단항 메시지 center가 직사각형에 발송되며, 꼭지점이 리턴 됩니다. 괄호 없는 메시지를 분해하는 작업은 에러를 발생시킵니다. 그 이유는 오브젝트 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. 메시지 보내기 (message sends) Pen new down 모든 메시지는 단항 메시지이므로, 가장 왼쪽의 Pen new가 가장 먼저 발송됩니다. 이것은 그림 4.6에서 보이는 것 처럼, 두 번째 메시지 down 이 발송된 새롭게 만들어진 Pen을 리턴합니다.
그림 4.6: Pen new down 분해하기 |
산술적 불일치
메시지 작성 규칙들은 단순하지만, 바이너리 메시지로 표현된 산술적 메시지 보내기의 실행에 있어 불일치를 낳습니다. 여기서, 우리는 괄호가 필요한 일반적인 상황들에 대해 알아볼 것입니다.
3+4*5 | ⇒ 35 "(not 23) Binary messages sent from left to right" |
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. 메시지 보내기(the message sends) 20+2 * 5에서, 바이너리 메시지는 +와 * 뿐 입니다. 그럼에도 불구하고, 스몰토크에서는 연산 +와 *를 위한 특정 우선권은 존재하지 않습니다. 이것들은 단지 바이너리 메시지 이므로, *는 +에 우선권을 갖지 않습니다. 예시 4.4에서 보이듯이, 여기서는 제일 왼쪽의 +가 발송되었으며 (1) 그 다음 *가 결과에 발송되었습니다.
예시 4.4: 20+2*5 분해하기 | |
"바이너리 메시지들 사이에 우선권이 존재하지 않듯이, 제일 왼쪽의 메시지 +는 , 산술적 규칙 *가 첫 번째로 발송되어야 함에도 불구하고, 첫 번째로 평가됩니다" | |
20 + 2 * 5 | |
(1) | 20 + 2 ⇒ 22 |
(2) | 2 * 5 ⇒ 110 |
그림 4.4에서 보이듯이, 이 메시지 보내기(message send)의 결과는 30이 아니며 110입니다. 이 결과는 아마도 기대하지 못했던 것이지만, 메시지를 보내기 위해 사용된 규칙을 직접 따른 것입니다.
그림 4.7 괄호를 사용하는 등가 메시지들 |
괄호사용은 스몰토크 모델의 단순성을 위해 어느 정도 지불할 필요가 있는 수고입니다.
우리는 정확한 결과를 얻기 위해, 괄호를 사용해야만 합니다. 메시지에 괄호를 넣으면, 첫 번째로 평가됩니다. 그러므로 메시지 보내기 20+(2*5)는 예시 4.5에서 보이는 것과 같은 결과를 리턴합니다.
예시 4.5: 20+(2*5) 분해하기 | ||
"괄호로 싸인 메시지가 처음으로 평가되며, 그러므로 *는 정확한 동작을 유발하는 + 이전에 전송됩니다" | ||
20 + | (2 * 5) | |
(1) | (2 * 5) ⇒ 10 | |
(2) | 20 + | 10 ⇒ 30 |
스몰토크에서는 연산 +와 * 와 같은 산술적 연산자는(arithmetic operator) 다른 우선권을 갖지 않습니다. +와 *는 단지 바이너리 메시지일 뿐이므로, *는 +에 대해 우선권을 갖지 않습니다. 원하는 결과를 얻기 위해 괄호를 사용합니다.
첫 번째 규칙이 바이너리 메시지와 키워드 메시지 이전에 발송된 단항 메시지는 명확한 괄호 넣기를 피한다는 진술에 유의하십시오. 표 4.8은 규칙들이 존재하지 않을 경우, 규칙들과, 동등한 메시지들을 따르며 메시지 보내기(message sends)가 작성됨을 보여줍니다.
그림 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
- ↑ 사실, 우리는 동등물로 작성할 수 있었지만, 보다 더 단순한 표현식으로 작성하였습니다: Boolean methodDict select: #isAbstract thenCollect: #selector