Smalltalk80LanguageImplementationKor:Chapter 02

From 흡혈양파의 번역工房
Jump to navigation Jump to search
제 2 장 표현식 구문

표현식 구문

제 1장을 통해 Smalltalk-80 시스템의 기본적인 개념들을 소개하였다. 시스템 구성요소는 객체로 표현된다. 객체는 클래스의 인스턴스다. 객체는 메시지를 전송함으로써 상호작용한다. 메시지로 인해 메서드가 실행된다. 이번 장에서는 객체와 메시지를 설명하기 위한 표현식 구문을 소개하고자 한다. 그리고 다음 장에서는 클래스와 메서드를 서술하기 위한 구문을 소개할 것이다.


표현식은 표현식 값이라 불리는 객체를 설명하는 문자의 시퀀스다. 본 장에 제시한 구문은 어떠한 문자 시퀀스가 합당한 표현식을 구성하는지 설명한다. Smalltalk-80 프로그래밍 언어에는 네 가지 타입의 표현식이 있다.

  1. 리터럴은 숫자나 문자열과 같은 특정 상수 객체를 설명한다.
  2. 변수명은 접근 가능한 변수를 설명한다. 변수명의 값은 해당 이름으로 된 변수의 현재 값이다.
  3. 메시지 표현식은 수신자에게 메시지를 설명한다. 메시지 표현식의 값은 메시지가 호출하는 메서드로 결정된다. 해당 메서드는 수신자의 클래스에서 발견된다.
  4. 블록 표현식은 지연된 활동을 나타내는 객체를 설명한다. 블록은 제어 구조체를 구현하는 데에 사용된다.


표현식은 두 가지 장소, 바로 메서드와 화면에 표시된 텍스트에서 찾을 수 있다. 메시지가 전송되면 수신자의 클래스로부터 메시지가 선택되고 그 표현식이 평가된다. 일부 사용자 인터페이스는 화면에서 표현식이 선택되고 평가되는 것을 허용한다. 화면에 표현식을 선택하고 평가하는 것과 관련된 자세한 사항은 사용자 인터페이스에 해당하는 내용이므로 이번 서적의 범위에서는 벗어난다. 하지만 제 17장을 통해 몇 가지 예제를 제시하겠다.


위에 열거된 네 가지 타입의 표현식 중 변수명은 컨텍스트에 의존적이다. 시스템 내 표현식의 위치는 어떠한 문자 시퀀스가 합당한 변수명인지 결정한다. 메서드의 표현식에서 이용 가능한 변수명 집합은 메서드가 발견되는 클래스에 따라 좌우된다. 가령, Rectangle 내 메서드와 Point 클래스 내 메서드는 서로 다른 변수명 집합으로 접근성을 갖는다. 클래스의 메서드에서 이용 가능한 변수는 제 3, 4, 5장에서 모두 설명하겠다. 화면에서 표현식에 사용할 수 있는 변수명은 표현식이 화면의 어디에 표시되는지에 따라 달라진다. 그 외 표현식 구문의 측면들은 표현식의 위치와는 상관없다.


표현식에 대한 구문은 본 서적의 뒷표지 앞에 실린 다이어그램에 설명되어 있다. 이번 장의 나머지 부분에서는 네 가지 타입의 표현식을 설명하겠다.


리터럴(Literals)

리터럴 표현식에 의해 참조가 가능한 객체 유형에는 다섯 가지가 있다. 리터럴 표현식의 값은 항상 동일한 객체이기 때문에 이러한 표현식들은 리터럴 상수라고도 불린다. 다섯 가지 유형의 리터럴 상수는 다음과 같다.

  1. 숫자(numbers)
  2. 각 문자(individual characters)
  3. 문자열(strings of characters)
  4. 심볼(symbols)
  5. 그 외 리터럴 상수의 배열(arrays of other literal constants)


숫자(Numbers)

숫자는 수학적 결과를 계산하는 메시지에 응답하고 수치 값을 나타내는 객체다. 숫자의 리터럴 표현식은 디지트(digit)의 시퀀스로, 맨 앞에 마이너스 기호가 붙고 (혹은) 그 뒤에 소수점과 다른 디지트의 시퀀스가 따라올 수 있다. 예를 몇 가지 들어보겠다.

3
30.45
-3
0.005
-14.0
13772


숫자 리터럴은 기수 접두사(radix prefix)가 있는 디지트를 맨 앞에 붙여 10진이 아닌 수(nondecimal base)로 표현 가능하다. 기수 접두사는 digit 기수의 값(십진수로 표현) 다음에 "r" 이 따라온다. 아래의 예제는 8진으로 된 숫자와 그에 해당하는 십진수를 명시한 모습이다.

8진수 10진수
8r377 255
8r153 107
8r34.1 28.125
8r-37 -31


밑(base)이 10보다 크면 9보다 큰 숫자에 "A" 로 시작되는 대문자가 사용된다. 아래 예들은 16진으로 된 숫자와 그에 해당하는 십진수를 명시한다.

16진수 10진수
16r106 262
16rF 255
16rAC.DC 172.859
16R-1.C -1.75


숫자 리터럴은 지수 접미사(exponent suffix)가 붙은 값의 자릿수를 따름으로써 과학적인 표기법으로 표시 가능하다. 지수 접미사는 "e" 라는 문자 다음에 지수가 따라온다 (십진으로 표현). 지수 접미사 앞에 명시된 숫자는 기수에 지수가 명시한 수만큼 제곱승을 한 값으로 곱해진다.

과학적 표기법 10진수
1.586e5 158600.0
1.586e-3 0.001586
8r3e2 192
2r11e6 192


문자(Characters)

문자는 각 알파벳의 심볼을 표현하는 객체다. 문자 리터럴 표현식은 달러 부호 뒤에 하나의 문자로 구성되며, 예를 들자면 아래와 같다.

$a
$M
$-
$$
$1


문자열(Strings)

문자열은 문자의 시퀀스를 나타내는 객체다. 문자열은 각 문자로 접근하는 메시지에 응답하고, 하위문자열을 대체하며, 다른 문자열과 비교를 실행한다. 문자열의 리터럴 표현식은 아래와 같이 단일 인용부호로 구분된 문자의 시퀀스다.

'hi'
'food'
'the Smalltalk-80 system'


문자열 리터럴에는 어떤 문자든 포함될 수 있다. 단일 인용부호가 문자열로 포함될 것이라면 구분 문자와의 혼동을 피하기 위해 이중으로 사용되어야 한다. 가령, 아래와 같은 문자열 리터럴은,

'can''t'


다섯 개의 문자, 즉 $c,$a,$n,$', $t 의 문자열을 참조한다.


심볼(Symbols)

심볼은 시스템 내 이름에 사용되는 문자열을 표현하는 객체다. 심볼의 리터럴 표현은 파운드 부호가 앞에 붙은 영숫자(alphanumeric) 문자의 시퀀스로, 예는 다음과 같다.

#bill
#M63


동일한 문자로 된 두 개의 심볼이 절대 있을 수 없는 것이, 각 심볼은 유일하기 때문이다. 따라서 심볼을 효율적으로 비교하는 것이 가능하다.


배열(Arrays)

배열은 1부터 배열 크기에 해당하는 숫자까지 정수 색인으로 참조가 가능한 내용을 가진 간단한 데이터 구조체 객체다. 배열은 그들의 내용으로 접근성을 요청하는 메시지에 응답한다. 배열의 리터럴 표현은 다른 리터럴, 즉 숫자, 문자, 문자열, 심볼, 배열의 시퀀스로서 괄호로 구분되고 앞에는 파운드 부호가 붙는다. 그 외 리터럴은 공백으로 구분된다. 임베딩된(embedded) 심볼과 배열은 파운드 부호로 시작되지 않는다. 3개 숫자의 배열은 아래의 표현식으로 설명된다.

#(1 2 3)


7개 문자열의 배열은 아래의 표현식으로 설명된다.

#('food' 'utilities' 'rent' 'household' 'transportation' 'taxes' 'recreation')


2개의 배열과 2개의 숫자의 배열은 아래 표현식으로 설명된다.

#(('one' 1)('not' 'negative') 0 - 1)


배열이 숫자 1개, 문자열 1개, 문자 1개, 심볼 1개와 또 다른 배열로 이루어진 경우 아래 표현식으로 설명된다.

#(9 'nine' $9 nine (0 'zero' $0 () 'e' $f 'g' $h 'i'))


변수(Variables)

객체에 이용 가능한 메모리는 변수로 구성된다. 이러한 변수들은 대부분 이름을 갖고 있다. 각 변수는 단일 객체를 기억하며, 변수의 이름은 해당 객체를 참조하는 표현식으로 사용할 수 있다. 특정 위치로부터 접근 가능한 객체들은 이용 가능한 변수명에 따라 결정된다. 예를 들어, 객체의 인스턴스 변수 내용은 다른 객체에선 이용할 수 없는데, 그러한 변수들의 이름은 객체 클래스의 메서드 내에서만 사용 가능하기 때문이다.


변수명은 단순한 식별자로서, 글자 시퀀스와 글자로 시작되는 숫자로 구성된다. 변수명 예제를 몇 가지 소개하겠다.:

index
initialIndex
textEditor
bin14
bin14Total
HouseholdFinances
Rectangle
IncomeReasons


시스템에는 두 가지 유형의 변수가 있는데, 변수가 얼마나 널리 접근 가능한지에 따라 구별된다. Private 변수들은 단일 객체에서만 접근 가능하다. 인스턴스 변수는 private하다. 공유 변수는 하나 이상의 객체에서 접근할 수 있다. Private 변수의 이름은 첫 글자가 모두 대문자여야 한다. 위에 표시된 5개의 식별자 예들 중 첫 번째는 private 변수를 나타내고, 마지막 3개는 공유 변수를 나타낸다.


또 눈에 띄는 대문자 규칙으로는, 식별자가 각 단어의 첫 문자만 대문자로 표기되어 여러 단어가 연결되어 있다는 점이다. 시스템에서 강제로 적용한 규칙은 아니다.


할당(Assignments)

리터럴 상수는 항상 동일한 객체를 참조하겠지만, 변수명은 동시에 여러 객체를 참조할 수도 있다. 변수가 참조한 객체는 할당 표현식이 평가될 때마다 변경된다. 할당은 앞서 표현식 타입으로 열거하지 않았는데, 어떤 표현식이든 할당 접두사(assignment prefix)를 포함시켜 할당이 될 수 있기 때문이다.


할당 접두사는 값이 변경될 변수의 이름 다음에 왼쪽 화살표(← )가 붙어서 구성된다. 아래 예제는 리터럴 표현식으로, 할당 접두사를 갖고 있다. 이는 quantity라는 변수는 이제 숫자 19를 나타내는 객체를 참조한다는 의미다.

quantity   19


아래의 예제는 할당 접두사가 포함된 변수명 표현식이다. index라는 변수는 initialIndex로 명명된 변수와 같은 객체를 참조한다는 의미다.

index   initialIndex


할당 표현식의 예를 더 들어보자.:

chapterName   'Expression Syntax'
flavors   #('vanilla' 'chocolate' 'butter pecan' 'garlic')


하나 이상의 할당 접두사가 포함될 수 있으며, 이는 여러 변수의 값이 변경됨을 의미한다.

index   initialIndex   1


이 표현식은 index와 initialIndex 변수 모두 숫자 1을 참조해야 함을 나타낸다. 메시지 표현식과 블록 표현식은 아래에서 확인할 수 있듯이 할당 접두사를 가질 수 있다.


의사 변수명(Pseudo-variable Names)

의사 변수명은 객체를 참조하는 식별자다. 따라서 변수명과 비슷하다. 하지만 의사 변수명은 할당 표현식으로 값을 변경할 수 없는 변수명과는 차이가 있다. 시스템 내 몇 가지 의사 변수는 상수들로, 항상 동일한 객체를 참조한다. 세 가지 중요한 의사 변수명으로 nil, true, false를 들 수 있다.

nil 적절한 객체가 없을 때 변수의 값으로 사용되는 객체를 나타낸다. 그 외에 초기화되지 않은 변수가 nil을 참조한다.
true 논리적 정확도를 표현하는 객체를 나타낸다. 간단한 예-아니오 질의를 표시하는 메시지에 대한 긍정적 응답으로 사용된다.
false 논리적 부정확도를 표현하는 객체를 나타낸다. 간단한 예-아니오 질의를 표시하는 메시지에 대한 부정적 응답으로 사용된다.


true와 false로 명명된 객체는 Boolean 객체라고 부른다. 이들은 예-아니오 질문에 대한 응답을 나타낸다. 예를 들어, 숫자는 해당 숫자가 다른 숫자보다 큰지 여부를 묻는 메시지에 true 또는 false로 응답한다. Boolean 객체는 논리적 함수를 계산하고 조건적 제어 구조체를 실행하는 메시지에 응답한다.


시스템에는 어디서 사용되는지에 따라 값이 달라지는 의사 변수들도 있다 (예: self와 super). 이는 제 3, 4, 5장에서 소개할 것이다.


메시지(Messages)

메시지는 Smalltalk-80 시스템의 구성요소들 간 상호작용을 나타낸다. 메시지는 수신자 쪽에 연산을 요청한다. 메시지 표현식과 그것이 나타내는 상호작용에 대한 예를 소개하겠다.


숫자에 산술 연산을 나타내는 메시지

3 + 4 3과 4의 합을 계산한다.
index + 1 index로 명명된 숫자에 1을 더한다.
index > limit index로 명명된 숫자가 limit로 명명된 숫자보다 큰지 질문한다.
theta sin theta로 명명된 숫자의 사인을 계산한다.
quantity sqrt quantity로 명명된 숫자의 양의 제곱근을 계산한다.


선형 데이터 구조체에 정보의 추가 또는 제거를 나타내는 메시지

list addFirst: newComponent newComponent라는 객체를 list라는 선형 데이터 구조체의 첫 번째 요소로 추가한다.
list removeLast List 의 마지막 요소를 제거하여 반환한다.


연관 데이터 구조체에 (예: 사전) 정보의 추가 또는 제거를 나타내는 메시지

ages at:'Brett Jorgensen' put: 3 'Brett Jorgensen' 문자열을 ages라는 사전의 숫자 3과 연관시켜라.
addresses at:'Peggy Jorgensen' address라는 사전에서 'Peggy Jorgensen' 문자열과 연관된 객체를 검색하라.


직사각형에 그래픽 질의와 계산을 나타내는 메시지

frame center frame이라는 직사각형의 중심 위치를 응답한다.
frame containsPoint: cursorLocation cursorLocation이라는 위치가 frame이라는 직사각형 내부에 있다면 true를, 그 외의 경우 false를 응답한다.
frame intersect: clippingBox frame과 clippingBox라는 두 개의 직사각형의 교집합을 나타내는 직사각형을 계산한다.


재정 내역 기록으로 전송되는 거래 및 질의를 나타내는 메시지

HouseholdFinances spend: 32.50 on: 'utilities' 유틸리티 비용으로 $32.50이 소비되었음을 HouseholdFinances라는 재정 내역으로 알린다.
HouseholdFinances totalSpentFor: 'food' HouseholdFinances에게 식품에 얼마의 금액이 소비되었는지 질문한다.


선택자와 인자

메시지 표현식은 수신자, 선택자, 어쩌면 몇 가지 인자까지 설명한다. 수신자와 인자는 다른 표현식에 의해 설명된다. 선택자는 문자로 명시된다.


메시지의 선택자는 전송자가 수신자에게서 바라는 상호작용의 유형에 대한 이름이다. 가령, 아래와 같은 메시지에서

theta sin


수신자는 theta라는 변수가 참조하는 숫자이고, 선택자는 sin이다. 메시지에 어떻게 응답할 것인지(이번 경우, 그 값의 sine 함수를 계산하는 방법)에 대한 결정은 수신자에게 달려 있다.


아래 두 개의 메시지 표현식에서,

3 + 4


그리고

previousTotal + increment


선택자는 +다. 두 메시지 모두 수신자에게 총액을 계산하고 리턴할 것을 요청한다. 이러한 메시지는 각각 객체를 비롯해 선택자를 (첫 번째 표현식에선 4, 두 번째에서 increment) 포함한다. 메시지 내 추가 객체들은 추가되어야 할 양을 명시하는 인자들이다.


아래 두 메시지 표현식은 같은 유형의 연산을 설명한다. 수신자는 FinancialHistory의 인스턴스로, 특정한 용도에 사용된 금액을 리턴할 것이다. 인자는 관심 용도를 나타낸다. 첫 번째 표현식은 유틸리티 청구서에 소비된 금액을 요청한다.

HouseholdFinances totalSpentOn: 'utilities'


식품에 소비된 금액은 선택자는 같지만 인자가 다른 메시지를 전송함으로써 찾을 수 있다.

HouseholdFinances totalSpentOn: 'food'


메시지의 선택자는 수신자의 연산 중 어떤 것이 호출될 것인지 결정한다. 인자들은 선택된 연산에서 호출될 다른 객체들이다.


❏ 단항 메시지. 인자가 없는 메시지를 단항 메시지라고 부른다. 가령, HouseholdFinances에 따라 현재 이용 가능한 금액은 단항 메시지 표현식의 값이다.

HouseholdFinances cashOnHand


이 메시지들은 하나의 객체, 즉 수신자만 관여되기 때문에 단항이라고 불린다. 단순한 식별자라면 어느 것이든 단항 메시지 선택자가 될 수 있다. 단항 메시지 표현식의 또 다른 예로 다음을 들 수 있다.

theta sin
quantity sqrt
nameString size


❏ 키워드 메시지. 하나 또는 그 이상의 인자를 가진 일반적인 타입의 메시지가 키워드 메시지다. 키워드 메시지의 선택자는 하나 또는 이상의 키워드로 구성되고, 각 인자마다 앞에 키워드가 붙는다. 키워드는 끝에 콜론이 붙은 단순한 식별자다. 단일 키워드 메시지를 설명하는 표현식의 예는 다음과 같다.

HouseholdFinances totalSpentOn: 'utilities'
index max: limit


인자가 2개인 메시지는 두 개의 키워드가 있는 선택자를 가질 것이다. 이중 키워드 메시지를 설명하는 표현식의 예로 다음을 들 수 있다.

HouseholdFinances spend: 30.45 on: 'food'
ages at: 'Brett Jorgensen' put: 3


다중 키워드 메시지의 선택자가 따로 참조되면 키워드는 서로 연결된다. 마지막 메시지 표현식의 선택자는 spend:on: 과 at:put:이다. 메시지에는 키워드 수가 제한되지 않지만 시스템 내 대부분 메시지는 3개 이하의 키워드를 갖는다.


❏ 이항 메시지. 단일 인자를 취하는 또 다른 메시지 표현식이 있는데, 이것이 바로 이항 메시지다. 이항 메시지 선택자는 영숫자가 아닌(nonalphanumeric) 하나 또는 두 개의 문자로 구성된다. 단, 두 번째 문자는 마이너스 부호가 될 수 없다는 점만 제한된다. 이항 선택자는 산술 메시지에 사용되는 경향이 있다. 이항 메시지 표현식의 예는 다음과 같다.

3 + 4
total - 1
total < = max


반환하는 값

Smalltalk-80 메시지는 양방향 통신을 제공한다. 선택자와 인자는 어떤 타입의 응답을 만들 것인지에 관한 정보를 수신자에게 전송한다. 수신자는 메시지 표현식의 값이 되는 객체를 리턴함으로써 정보를 다시 전송한다. 메시지 표현식이 할당 접두사를 포함할 경우, 수신자가 리턴하는 객체는 변수가 참조하는 새로운 객체가 될 것이다. 예를 들어,

sum   3 + 4


위의 표현식은 7을 sum이라는 변수의 새로운 값으로 만든다.

x   theta sin


그리고 위의 표현식은 theta의 사인(sign)을 x라는 변수의 새 값으로 만든다. theta의 값이 1일 경우, x의 새로운 값은 0.841471이 된다. theta의 값이 1.5일 경우 x의 새로운 값은 0.997495가 된다.


index가 참조하는 숫자는 아래 표현식에 의해 증가될 수 있다.

index   index + 1


전송자에게 어떠한 정보도 다시 전달할 필요가 없다 하더라도 수신자는 항상 메시지 표현식에 대한 값을 리턴한다. 값을 리턴한다는 것은 메시지에 대한 응답이 완전하다는 뜻이다. 일부 메시지들은 수신자에게 뭔가를 알려주는 기능만 하도록 되어 있다. 아래의 표현식이 설명하는 금융거래를 기록하기 위한 메시지를 예로 들 수 있다.

HouseholdFinances spend : 32.50 on: 'utilities'
HouseholdFinances receive: 1000 from: 'pay'


이러한 메시지들의 수신자는 전송자에게 거래의 기록이 끝났다는 사실만 알려준다. 그러한 경우 리턴되는 기본값은 주로 수신자 자체가 된다. 따라서, 아래의 표현식은

var   HouseholdFinances spend: 32.50 on: 'utilities'


HouseholdFinances와 동일한 재정 내역을 참조하는 var 가 결과가 된다.


해석(Parsing)

따라서 본문에 표시된 모든 메시지 표현식은 리터럴 또는 변수명으로 인자와 수신자를 설명해왔다. 메시지 표현식의 인자 또는 수신자가 다른 메시지 표현식에 의해 설명되면 표현식이 어떻게 파싱되었는지에 대한 문제가 발생한다. 또 다른 단항 메시지의 수신자를 설명하는 단항 메시지의 예는 다음과 같다.

1.5 tan rounded


단항 메시지는 왼쪽에서 오른쪽으로 파싱된다. 예제에서 첫 번째 메시지는 1.5로 전송된 단항 선택자 tan이다. 해당 메시지 표현식의 값은 (14.1014에 가까운 숫자) 단항 메시지 rounded를 수신하고 가장 가까운 정수, 14를 리턴한다. 숫자 14는 전체 표현식의 값이다.


이항 메시지도 마찬가지로 왼쪽에서 오른쪽으로 파싱된다. 또 다른 이항 메시지의 수신자를 설명하는 이항 메시지 예는 다음과 같다.

index + offset * 2


+ offset 메시지로부터 index가 리턴한 값은 이항 메시지 *2에 대한 수신자다. 모든 이항 선택자들은 동일한 우선순위(precedence)를 가지므로 작성된 순서만 중요하다. 따라서 Smalltalk-80 언어에서 수학적 표현식은 곱셈과 나눗셈이 덧셈과 뺄셈보다 우선하는 여러 다른 언어의 수학적 표현식과는 차이가 있다. 괄호를 이용해 평가의 순서를 변경할 수 있다. 괄호 내 메시지는 괄호 밖에 있는 어떤 메시지보다 먼저 전송된다. 앞의 예제를 아래와 같이 다시 쓰면 덧셈보다 곱셈이 먼저 실행될 것이다.

index + (offset * 2)


단항 메시지는 이항 메시지보다 우선한다. 단항 메시지와 이항 메시지가 함께 나타나면 단항 메시지가 먼저 전송될 것이다.

frame width + border width * 2


위의 예제에서 프레임 너비의 값은 선택자가 +이고 인자가 테두리 너비 값인 이항 메시지의 수신자가 된다. + 메시지의 값은 이항 메시지 *2의 수신자다. 표현식은 아래와 같이 괄호로 표기된 것처럼 파싱된다.

((frame width) + (border width)) * 2


단항 메시지보다 먼저 이항 메시지를 전송할 때는 괄호를 이용할 수 있다. 아래의 표현식은

2 * theta sin


theta 의 사인을 두 번 계산하는 반면, 아래의 표현식은

(2 * theta) sin


theta를 2로 곱한 값의 사인을 계산한다.


괄호로 표기되지 않은 메시지에서 키워드가 나타날 때마다 그들은 하나의 선택자를 구성한다. 이러한 연결(concatenation)로 인해 키워드 메시지에는 좌측에서 우측으로의 파싱 규칙이 없다. 키워드 메시지가 다른 키워드 메시지의 인자 또는 수신자로서 사용될 경우 괄호로 표기되어야 한다. 아래의 표현식은

frame scale: factor max: 5


선택자가 scale:max:인 2-인자 키워드 메시지 하나를 설명한다. 그리고 아래의 표현식은

frame scale: (factor max: 5)


선택자가 scale: 과 max: 인 단일 키워드 메시지 두 개를 설명한다. 표현식 factor max:5 의 값은 frame에 전송되는 scale: 메시지에 대한 인자다.


이항 메시지는 키워드 메시지보다 우선한다. 단항, 이항, 키워드 메시지가 괄호 없이 같은 표현식에 표시될 경우, 단항 메시지, 이항 메시지, 키워드 메시지 순으로 전송된다. 아래 예제는

bigFrame width: smallFrame width * 2


아래와 같이 괄호로 표기된 것처럼 평가된다.:

bigFrame width: ((smallFrame width) * 2)


아래 예제에서는 단항 메시지가 키워드 메시지의 수신자를 설명하고 이항 메시지는 인자를 설명한다.

OrderedCollection new add: value * rate


따라서 파싱 규칙을 요약하자면 다음과 같다.

  1. 단항 표현식은 왼쪽에서 오른쪽으로 파싱된다.
  2. 이항 표현식은 왼쪽에서 오른쪽으로 파싱된다.
  3. 이항 표현식은 키워드 표현식보다 우선한다.
  4. 단항 표현식은 이항 표현식보다 우선한다.
  5. 괄호로 된 표현식은 단항 표현식보다 우선한다.


포매팅 규칭(Formatting Conventions)

프로그래머는 스페이스, 탭, 캐리지 리턴(carriage return)을 이용해 다양한 방식으로 표현식을 포맷팅할 수 있다. 예를 들어, 다수의 키워드 메시지는 아래와 같이 여러 행에서 각 키워드-인자 쌍으로 쓰인다.

ages at: 'Brett Jorgensen'
    put: 3


아니면 다음과 같이 쓰이기도 한다.

HouseholdFinances
    spend: 30.45
    on: 'food'


스페이스, 탭 또는 캐리지 리턴이 표현식의 의미에 영향을 미치는 유일한 때는 그것이 없을 경우 두 개의 글자나 두 개의 숫자가 붙어버리는 경우다.


캐스케이딩(Cascading)

동일한 객체로 다수의 메시지를 명시하는 캐스케이딩이라는 특별한 구문적 형태가 있다. 어떤 메시지 시퀀스든 캐스케이딩이 없이 표현 가능하다. 하지만 캐스케이딩은 종종 변수를 사용해야 하는 필요성을 줄여준다. 캐스케이딩된 메시지 표현식은 수신자의 설명과 함께 세미콜론으로 구분된 여러 메시지로 구성된다. 예를 들면 다음과 같다.

OrderedCollection new add: 1; add: 2; add:3


3개의 add: 메시지가 OrderedCollection new 결과로 전송된다. 캐스케이딩이 없이는 4개의 표현식과 하나의 변수가 필요했을 것이다. 가령, 공백으로 구분된 아래 4개의 표현식은 위에서 캐스케이딩된 표현식과 동일한 결과를 얻는다.

temp   OrderedCollection new.
temp add: 1.
temp add: 2.
temp add: 3


블록(Blocks)

블록은 Smalltalk-80 시스템에서 많은 제어 구조체에 사용되는 객체다. 블록은 액션의 지연된 시퀀스를 나타낸다. 블록 표현식은 마침표로 구분되고 사각 괄호로 분리된 표현식의 시퀀스로 구성된다. 두 가지를 예로 들어보겠다.

[index   index + 1]


또는 다음과 같이 구성될 수 있다.

[index   index +1.
array at: index put: 0]


마침표 다음에 마지막 표현식이 오면 아래와 같이 무시된다.

[expenditures at: reason.]


블록 표현식에 마주치면 괄호 안에 감싼 문(statement)들은 즉시 실행되지 않는다. 블록 표현식의 값은 추후 요청이 있으면 이렇게 괄호 안의 표현식을 실행할 수 있는 객체다. 가령, 아래의 표현식은

actions at: 'monthly payments'
	put: [HouseholdFinances spend: 650 on: 'rent'.
		HouseholdFinances spend: 7.25 on: 'newspaper'.
		HouseholdFinances spend: 225.50 on: 'car payment']


HouseholdFinances로 어떠한 spend:on: 메시지도 전송하지 않는다. 단순히 'monthly payments' 문자열과 블록을 연관시킬 뿐이다. 블록이 설명하는 액션의 시퀀스는 블록이 단항 메시지 value를 수신하면 발생할 것이다. 예를 들어,

index   index + 1


위의 표현식과 아래 표현식의 효과는 동일하다.

[index   index +1] value


아래의 표현식이 참조하는 객체는

actions at: 'monthly payments'


3개의 spend:on: 메시지를 포함하는 블록이다. 그리고 아래 표현식을 실행하면,

(actions at: 'monthly payments') value


HouseholdFinances로 3개의 spend:on: 메시지가 전송되는 결과를 야기한다.


블록은 변수로도 할당이 가능하다. 따라서

incrementBlock   [index   index + 1]


위의 표현식이 실행되면

incrementBlock value
increments index.


value 메시지가 블록으로 전송된 다음에 반환되는 객체는 시퀀스에서 마지막 표현식의 값이다. 따라서,

addBlock   [index + 1]


위의 표현식이 실행되면, index를 증가시키는 또 다른 방법은 아래 표현식을 평가하는 것이다.

index   addBlock value


표현식을 전혀 포함하지 않는 블록은 value 메시지를 전송하면 nil을 리턴한다. 아래의 표현식은

[] value


nil 이라는 값을 갖는다.


제어 구조체(Control Structures)

제어 구조체는 일부 활동의 순서를 결정한다. Smalltalk-80 언어에서 기본적인 제어 구조체는 표현식 시퀀스가 순차적으로 평가되도록 제공된다. 블록을 이용하면 많은 비순차적 제어 구조체를 구현할 수도 있다. 이러한 제어 구조체들은 블록으로 메시지를 전송하거나 하나 또는 그 이상의 블록을 인자로 가진 메시지를 전송함으로써 호출된다. 이러한 제어 구조체 메시지로의 응답은 그것이 블록(들)에 전송하는 value 메시지의 패턴을 이용해 활동의 순서를 결정한다.


아래의 표현식 시퀀스의 평가를 조사하면 블록이 작동하는 방식에 대한 실례를 제공한다.

incrementBlock   [index   index + 1].
sumBlock   [sum + (index * index)].
sum   0.
index   1.
sum   sumBlock value.
incrementBlock value.
sum   sumBlock value


이러한 표현식의 시퀀스를 평가한 결과로 간주되는 15가지 액션은 다음과 같다.

  1. 블록을 incrementBlock으로 할당한다.
  2. 블록을 sumBlock으로 할당한다.
  3. 숫자 0을 sum으로 할당한다.
  4. 숫자 1을 index로 할당한다.
  5. value 메시지를 블록 sumBlock으로 전송한다.
  6. * 1 메시지를 숫자 1로 전송한다.
  7. + 1 메시지를 숫자 0으로 전송한다.
  8. 숫자 1을 to sum에 할당한다.
  9. value 메시지를 블록 IncrementBlock으로 전송한다.
  10. + 1 메시지를 숫자 1로 전송한다.
  11. 숫자 2 를 index에 할당한다.
  12. value 메시지를 블록 sumBlock으로 전송한다.
  13. * 2 메시지를 숫자 2로 전송한다.
  14. + 4 메시지를 숫자 1로 전송한다.
  15. 숫자 5를 sum으로 할당한다.


이러한 블록을 이용해 구현된 제어 구조체의 예로, timesRepeat: 을 선택자로 하고 블록을 인자로 하여 정수로 전송되는 메시지에 의해 표현되는 간단한 반복(repetition)을 들 수 있겠다. 정수는 자신의 값이 나타내는 value 메시지 수만큼 블록으로 전송함으로써 응답할 것이다. 가령, 아래의 표현식은 amount라는 변수의 값을 네 번 두 배로 곱한다.

4 timesRepeat: [amount   amount + amount]


조건부(Conditionals)

블록을 이용해 구현되는 두 가지 일반적인 제어 구조체는 "conditional selection(조건 선택)" 과 "conditional repetition(조건 반복)" 이다. 조건 선택은 Algol와 같은 언어에서 if-then-else문과 유사하고, 조건 반복은 while 과 until문과 비슷하다. 이러한 조건부 제어 구조체는 의사 변수에 관한 절에서 설명한 true와 false라는 두 개의 Boolean 객체를 사용한다. Boolean은 단순한 예-아니오 식의 질문을 하는 메시지로부터 리턴된다 (예: 크기 비교 메시지: <, =, <=, >, >=, ~=).


❏ 조건 선택. 활동의 조건 선택은 ifTrue:ifFalse: 선택자와 두 개의 블록을 인자로 하여 Boolean으로 전송되는 메시지에 의해 제공된다. IfTrue:ifFalse: 메시지를 이해하는 유일한 객체는 true와 false다. 이 둘은 서로 상반된 응답을 하는데, true는 첫 번째 인자 블록에 value를 전송하고 두 번째 인자 블록은 무시하는 반면, false는 두 번째 인자 블록에 value를 전송하고 첫 번째는 무시한다. 예를 들어, 아래의 표현식은 number 변수의 값이 2로 나누어지는지 여부에 따라 parity 변수로 0 또는 1을 할당한다. 이항 메시지 \\는 modulus 또는 나머지 함수를 계산한다.

(number \\ 2) = 0
	ifTrue: [parity   0]
	ifFalse: [parity   1]


ifTrue:ifFalse: 로부터 반환된 값은 실행된 블록의 값이다. 위의 예는 아래와 같이 쓸 수도 있다.

parity   (number \\ 2) = 0 ifTrue: [0] ifFalse: [1]


ifTrue:ifFalse: 외에도 하나의 조건부 결과(consequent)만 명시하는 두 개의 단일 키워드 메시지가 있다. 이러한 메시지들의 선택자는 ifTrue:와 ifFalse:다. 이러한 메시지들은 하나의 인자가 빈 블록일 경우 ifTrue:ifFalse: 메시지와 동일한 효과를 가진다. 예를 들어,

index < = limit
	ifTrue: [total   total + (list at: index)]


위의 표현식과 아래의 표현식의 효과는 동일하다.

index < = limit
	ifTrue: [total   total + (list at: index)]
	ifFalse: []


빈 블록의 값은 nil이기 때문에 아래 표현식은 index가 limit보다 클 경우 lastElement를 nil로 설정할 것이다.

lastElement   index > limit itFalse: [list at: index]


❏ 조건 반복. 활동의 조건 반복은 whileTrue: 을 선택자로 하고 또 다른 블록을 인자로 하여 블록으로 전송된 메시지에 의해 제공된다. 수신자 블록은 스스로에게 value 메시지를 전송하고, 응답이 true라면 다른 블록 value를 전송한 후 다시 스스로에게 value를 전송한다. value에 대한 수신자의 응답이 false라면 반복을 중단하고 whileTrue: 메시지로부터 리턴한다. 예를 들어, 조건 반복은 list라는 배열의 모든 요소를 초기화하는 데에 사용할 수 있다.

index   1.
[index < = list size]
	whileTrue: [list at: index put: 0.
		index   index + 1]


블록은 수신자 값이 false인 한 인자 블록의 실행을 반복하는 whileFalse:를 선택자로 둔 메시지도 이해한다. 따라서 아래의 표현식은 위의 표현식과 동일하다.

index   1.
[index > list size]
	whileFalse: [list at: index put: 0.
		index   index + 1]


프로그래머는 반복의 의도를 가장 명확하게 하는 메시지를 마음대로 선택해도 좋다. WhileTrue:와 whileFalse:가 반환하는 값은 항상 nil이다.


블록 인자(Block Arguments)

비순차적 제어 구조체를 표현하기 쉽게 만들기 위해 블록은 하나 또는 이상의 인자를 취할지도 모른다. 블록 인자는 블록 앞에 콜론을 붙인 식별자를 포함시킴으로써 명시된다. 블록 인자는 블록을 구성하는 표현식으로부터 수직 막대에 의해 구분된다. 아래의 예제와

[:array | total   total + array size]


아래의 예는 인자가 하나인 블록을 설명한다.

[:newElement |
    index   index + 1.
    list at: index put: newElement]


인자가 있는 블록은 데이터 구조체의 모든 요소로 적용시킬 함수를 구현하는 데에 사용되는 것이 보통이다. 가령, 여러 유형의 데이터 구조체를 나타내는 많은 객체들이 1인자 블록을 인자로 취하는 do: 메시지에 응답한다. do: 메시지를 수신하는 객체는 데이터 구조체에 포함된 요소마다 블록을 한 번씩 평가한다. 각 요소는 블록을 한 번 평가할 때마다 블록 인자의 값으로 만들어진다. 아래 예제는 처음부터 5개 소수들을 제곱하여 그 합을 계산한다. 그 결과는 sum의 값이 된다.

sum   0.
#(2 3 5 7 11) do: [ :prime | sum   sum + (prime * prime)]


collect: 메시지는 수신자의 요소들과 제공될 때 블록이 생성한 값의 컬렉션을 생성한다. 아래 표현식의 값은 처음부터 5개 소수들의 제곱의 배열이다.

#(2 3 5 7 11) collect: [ :prime | prime * prime]


이러한 제어 구조체를 구현하는 객체들은 value: 메시지를 블록으로 전송함으로써 블록 인자의 값을 제공한다. 하나의 블록 인자를 가진 블록은 블록 인자를 value: 의 인자로 설정하고 블록에서 표현식을 실행함으로써 value: 에 응답한다. 예를 들어, 아래 표현식을 평가하면 7의 값을 가진 total 변수가 생긴다.

sizeAdder   [ :array | total   total + array size].
total   0.
sizeAdder value: #(a b c).
sizeAdder value: #(1 2).
sizeAdder value: #(e f)


블록은 하나 이상의 인자를 취할 수 있다.

[ :x :y | (x * x) + (y * y)]


위와 아래를 예로 들 수 있다.

[ :frame :clippingBox | frame intersect: clippingBox]


블록을 평가하기 위해선 메시지에 value: 키워드의 개수와 동일한 수의 블록 인자를 가져야 한다. 위의 두 블록은 value:value: 를 선택자로 취한 키워드가 두 개인 메시지를 이용해 평가될 것이다. 메시지의 두 인자는 두 개의 블록 인자 값을 순서대로 명시한다. 블록이 그것이 취하는 블록 인자의 개수와 다른 인자 수를 가진 평가 메시지를 수신하면 오류가 보고될 것이다.


용어 정리

표현식의 구문은 본 서적의 뒷표지 앞에 요약되어 있다.

expression 객체를 설명하는 문자의 시퀀스.
literal 숫자나 문자열처럼 상수를 설명하는 표현식.
symbol 문자의 시퀀스가 다른 심볼의 시퀀스와 다르도록 보장된 문자열.
array 요소가 정수 색인과 연관된 데이터 구조체.
variable name 변수의 현재 값을 설명하는 표현식.
assignment 변수의 값의 변경을 설명하는 표현식.
pseudo-variable name 변수명과 비슷한 표현식. 하지만 변수명과 달리 의사 변수명의 값은 할당에 의해 변경될 수 없다.
receiver 메시지를 수신하는 객체.
message selector 메시지가 수신자에게 요청하는 연산 타입의 이름.
message argument 연산에 대한 추가 정보를 명시하는 객체.
unary message 인자가 없는 메시지.
keyword 끝에 콜론이 붙는 식별자.
keyword message 선택자가 하나 또는 이상의 키워드로 구성되고 인자가 하나 또는 그 이상인 메시지.
binary message 선택자가 하나 또는 두 개의 특수 문자로 구성되고 인자가 하나인 메시지.
cascading 단일 표현식에서 하나의 객체로 전송되는 여러 메시지에 대한 설명.
block 지연된 액션의 시퀀스에 대한 설명.
block argument 특정 블록이 평가될 때 제공되어야 하는 매개변수.
value 블록에게 그것이 나타내는 액션 집합을 실행하도록 요청하는 메시지.
value: 블록 인자를 가진 블록으로 전송되는 메시지에 사용되는 키워드로, 해당 메시지는 블록이 그 액션 집합을 실행하도록 요청한다.
ifTrue:if False: Boolean에게 조건 선택을 요청하는 메시지.
if False:if True: Boolean에게 조건 선택을 요청하는 메시지.
ifTrue: Boolean에게 조건 선택을 요청하는 메시지.
if False: Boolean에게 조건 선택을 요청하는 메시지.
whileTrue: Boolean에게 조건 선택을 요청하는 메시지.
whileFalse: Boolean에게 조건 선택을 요청하는 메시지.
do: 컬렉션에게 모든 요소의 열거를 요청하는 메시지.
collect: 컬렉션에게 모든 요소의 변형(transformation)을 요청하는 메시지.


Notes