<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://trans.onionmixer.net/wiki/index.php?action=history&amp;feed=atom&amp;title=SmalltalkBestPracticePatterns%3A5.3</id>
	<title>SmalltalkBestPracticePatterns:5.3 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://trans.onionmixer.net/wiki/index.php?action=history&amp;feed=atom&amp;title=SmalltalkBestPracticePatterns%3A5.3"/>
	<link rel="alternate" type="text/html" href="https://trans.onionmixer.net/wiki/index.php?title=SmalltalkBestPracticePatterns:5.3&amp;action=history"/>
	<updated>2026-05-02T01:27:28Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>https://trans.onionmixer.net/wiki/index.php?title=SmalltalkBestPracticePatterns:5.3&amp;diff=3543&amp;oldid=prev</id>
		<title>Onionmixer: SBPP 5.3	컬렉션 프로토콜 페이지 추가</title>
		<link rel="alternate" type="text/html" href="https://trans.onionmixer.net/wiki/index.php?title=SmalltalkBestPracticePatterns:5.3&amp;diff=3543&amp;oldid=prev"/>
		<updated>2013-07-29T08:46:56Z</updated>

		<summary type="html">&lt;p&gt;SBPP 5.3	컬렉션 프로토콜 페이지 추가&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;;5.3	컬렉션 프로토콜(Collection Protocol)&lt;br /&gt;
&lt;br /&gt;
==컬렉션 프로토콜(Collection Protocol)==&lt;br /&gt;
&lt;br /&gt;
컬렉션 클래스들에 대한 프로토콜이 획일화되었다는 점은 컬렉션 프로토콜의 가장 큰 강점들 중 하나이다. 고객의 코드는 공통 메시지 집합을 사용하여 어떻게 컬렉션을 저장하는지에 관한 결정으로부터 효과적으로 분리된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
많은 스몰토크 프로그래머들은 모든 컬렉션 프로토콜을 학습하는 데에 오랜 시간을 투자한다. do:, add:, size와 같은 소수의 메시지부터 시작해 오랜 시간 그 메시지들을 붙들고 시간을 보낸다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
필자도 처음 스몰토크에서 프로그래밍을 시작했을 때 어쩔 줄 몰라했던 기억이 난다. 기본적인 기능을 수행하는 데만 해도 얼마나 많은 것을 학습해야 했던지. 하지만 컬렉션 프로토콜은 시스템에서 가장 영향력이 큰 부분들 중 하나다. 이것을 잘 활용할수록 더 빠르게 코딩하고 타인의 코드를 더 빠르게 읽을 수 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
해당 절에서는 컬렉션 레퍼토리(repertoire)에서 가장 중요한 메시지들을 강조하고자 한다. 물론 완전한 목록은 아니다. 패턴에 대한 메시지들 중 가장 자주 누락되거나 잘못 사용되는 메시지들만 선정하였다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이러한 메시지의 사용을 숙달하고 나면 하루 정도 시간을 내어 어떤 컬렉션 클래스에서 어떤 메시지를 이용할 수 있는지 철저히 연구해보라. 머지않아 전문가처럼 컬렉션을 코딩할 수 있을 것이다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===IsEmpty===&lt;br /&gt;
&lt;br /&gt;
* 컬렉션이 비어있는지는 어떻게 시험하는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이와 관련된 문제를 언급할 것이라고 생각을 못했지만 반복적으로 아래와 같은 코드를 목격했기 때문에 소개하고자 한다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
...aCollection size = 0 ifTrue: ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
또는&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
...aCollection size &amp;gt; 0 ifTrue: ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
크기가 0 또는 그 이상의 값인지 확인하는 것은 더 높은 수준의 개념 구현, 즉 &amp;quot;해당 컬렉션이 비어있는가?&amp;quot;를 의미한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
컬렉션 프로토콜은 컬렉션이 비어있는지 검사하는 단순한 메시지들을 제공한다. 이를 사용 시 가독성이 조금 더 뛰어난 코드가 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 컬렉션이 비어있는지 시험하려면 (요소가 없는지) isEmpty를 전송하라. 컬렉션에 요소가 있는지를 시험하려면 notEmpty를 사용하라.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 모든 스몰토크는 사용자가 답을 입력(type)하도록 요청하는 대화창을 띄울 수 있는 간단한 기능을 가진다. 답란이 비어 있다면 동작을 진행하지 말아야 함을 의미한다. 아래는 일반적인 코드이다 (VisualWorks 스타일):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
| answer |&lt;br /&gt;
answer := Dialog request: &amp;#039;Number, please?&amp;#039;.&lt;br /&gt;
answer isEmpty ifTrue: [^self].&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
VisualSmalltalk와 VisualAge는 notEmpty를 정의하지만 VisualWorks 2는 그렇지 않으므로 자신이 추가해야 한다는 사실을 주목한다. isEmpty의 값은:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
aCollection size = 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
위의 코드와 아래의 코드 사이에:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
aCollection isEmpty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
물론 어느 정도 차이는 있지만 그렇게 큰 차이가 있다는 의미는 아니다. 중요한 점은 관용구버전을 즉시 읽을 수 있다는 사실이다. 크기를 명시적으로 확인하는 코드를 보면 잠시 동작을 멈추고, 확실하진 않지만 특별한 일이 일어나고 있는지 확인한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
모든 문화는 고유의 어휘를 발전시킨다. 자동차 수리점에서 &amp;quot;너비를 조정하는 들숙날쑥한 휠이 달린 렌치 좀 건네줘,&amp;quot;라는 말을 들으면 별로 차를 맡기고 싶지 않을 것이다. 정비공이라면 몽키 스패너 정도는 알아야 할테니 말이다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Includes:===&lt;br /&gt;
&lt;br /&gt;
* 컬렉션에서 어떻게 특정 요소를 검색하는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
많은 사람들은 검색을 구현 시 열거 프로토콜을 사용해야 한다고 가장 먼저 답할 것이다. 그들은 다음과 같은 코드를 작성할 것이다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
| found |&lt;br /&gt;
found := false.&lt;br /&gt;
aCollection do: [:each | each = anObject ifTrue: [found :=&lt;br /&gt;
true]].&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
좀 더 경험이 많다면 열거 메시지도 좀 더 복잡해진다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
| found |&lt;br /&gt;
found := (aCollection&lt;br /&gt;
    detect: [:each | each = anObject]&lt;br /&gt;
    ifNone: [nil]) notNil.&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
컬렉션 프로토콜은 정확히 이러한 일을 수행하는 메시지, 즉 includes:를 제공한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
위의 코드는 includes:를 이용해 아래와 같이 변경될 것이다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
| found |&lt;br /&gt;
found := aCollection includes: anObject&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
심지어 임시 변수를 제거하고 행에 표현식을 바로 사용할 수도 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
중요한 건 includes:를 최적화에 이용할 수 있다는 점이다. Set와 같은 일부 컬렉션 종류는 컬렉션의 크기와 무관하게 일정한 시간으로 includes:를 실행한다. 위에서 includes:에 대한 대안방법도 있지만 컬렉션의 크기에 비례해 항상 소요 시간이 증가할 것이다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* includes:를 전송하고, 검색할 객체를 전달하라. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
수신자가 사람을 고용할 경우 Collection Accessor Method의 예제는 true를 리턴한다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
employs: aPerson&lt;br /&gt;
    ^employees includes aPerson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
includes는 두 개의 컬렉션의 교집합(intersection)을 계산하는 데에 일반적으로 사용된다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
collection1 select: [:each | collection2 includes: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;정체성 대신 내용을 기반으로 자신만의 객체를 검색할 경우 객체에 대해 Equality Method(p.124)와 Hashing Method(p.126)을 구현해야 할 것이다.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Concatentation===&lt;br /&gt;
&lt;br /&gt;
* 두 개의 컬렉션을 어떻게 합치는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Concatentation은 대부분 언어에서 중간 관용구(intermediate idiom)들 중 하나이다. 이것 없이도 살 수는 있지만 언젠간 학습해야 한다. 일반적인 concatentation 관용구는 아래와 같이 작동한다:&lt;br /&gt;
&lt;br /&gt;
# 결과에 충분할 만큼 큰 컬렉션을 생성한다. &lt;br /&gt;
# 첫 번째 컬렉션을 결과의 첫 번째 부분에 복사한다. &lt;br /&gt;
# 두 번째 컬렉션을 결과의 두 번째 부분에 복사한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
때때로 2, 3번에 해당하는 복사를 대신해주는 라이브러리 함수가 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스몰토크는 이것을 첫 번째 컬렉션으로 보내는 단일 메시지, 즉 &amp;quot;,&amp;quot; (콤마)로 간소화하여 두 번째 컬렉션을 argument로서 취한다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
많은 프로그래밍 언어들은 문자열을 특별하게 취급한다. 종종 &amp;quot;+&amp;quot;(더하기) 연산자(operator)가 문자열 결합(string concatenation)에 사용된다. 스몰토크에서 Strings는 문자의 집합체이다. 모든 일반적인 컬렉션 프로토콜이 작동한다. 문자열을 결합하기 위해서는, Arrays 또는 OrderedCollections를 다룰 때와 마찬가지로 &amp;quot;,&amp;quot;를 사용한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 번째 컬렉션을 argument로서 취하고 첫 번째 컬렉션으로 &amp;quot;,&amp;quot;를 전송함으로써 두 컬렉션을 결합하라. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Concatentation은 String로부터 메시지와 일부 arguments를 구성할 때 주로 사용된다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self error: anInteger printString , &amp;#039; is too many objects for &amp;#039; ,&lt;br /&gt;
aString&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;다수의 컬렉션을 합칠 경우 Concatenating Stream(p.165)를 사용할 필요가 있을 것이다.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===열거(Enumeration)===&lt;br /&gt;
&lt;br /&gt;
어떻게 컬렉션에 걸쳐 코드를 실행하는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
컬렉션이 있다면 당신은 해당 컬렉션으로 어떠한 행동을 취해야 한다. 컬렉션에 걸친 계산의 예제를 들자면 다음과 같다:&lt;br /&gt;
&lt;br /&gt;
* Account는 그것의 모든 Transactions 값을 합산함으로써 잔고를 계산한다. &lt;br /&gt;
* Composite Visual은 그것의 모든 구성요소(component)를 표시함으로써 정보를 보여준다. &lt;br /&gt;
* Directory를 삭제 시 그것의 Files이 모두 제거된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
절차적 프로그래머들은 이와 같은 코드를 작성하기 위한 관용구를 모은 도구상자를 개발한다. C 프로그래머에게 배열을 반복해주길 요청하면 대부분은 그들이 입력하는 속도만큼 빠르게 프로그래밍을 할 수 있을 것이다. 그러한 코드의 시각적 인식 또한 자동이 된다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
스몰토크에서 반복의 세부내용은 모든 컬렉션이 이해하는 획일화된 메시지 집합 뒤에 숨긴다. 스몰토크에서는 하나, 둘 또는 세 개의 행으로 된 관용구들보단 하나의 단어로 된 메시지를 사용한다. 이러한 메시지를 이용해 작성되는 코드는 올바르게 쓰기도, 읽기도 쉽다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
열거 메시지에는 여러 변형(variation) 형태가 있기 때문에 일부 스몰토크 프로그래머들은 하나 또는 두 개만 학습하고 나머지는 수동으로 코딩한다. 결국 그 곳에 있는 메시지를 사용할 때보다 크기는 더 크고 오류가 발생하기 쉬우며 읽기 힘든 코드를 작성하는 데에 더 많은 시간을 낭비한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 컬렉션에 걸쳐 계산을 퍼뜨리기 위해서는 열거 메시지를 사용하라. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
열거 메시지들은 빈 컬렉션에도 정상적으로 작동한다. 컬렉션이 빈 경우 이를 이용해 특수 case 코드를 피할 수 있다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
printChildren&lt;br /&gt;
    children isEmpty ifTrue: [^self].&lt;br /&gt;
    self children do: [:each | each print]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
위의 코드는 아래와 정확히 동일하다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
printChildren&lt;br /&gt;
    self children do: [:each | each print]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Collection을 반복하는 동시에 요소를 추가하거나 삭제하는 경우, 문제가 발생할 것이다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
aSet do: [:each | aSet remove: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이러한 유형의 코드를 쓰기 위한 호출이 없는 경우가 종종 있다. 불가피하게 호출해야 하는 경우 컬렉션의 복사본이 열거(enumerate)하도록 만들어라:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
aSet copy do: [:each | aSet remove each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;단순한 열거를 위해선 Do(p.146)를 사용하라. 컬렉션의 요소를 변형하기 위해선 Collect(p.147)를 사용하라. 컬렉션의 특정 부분만 선택하려면 Select/Reject(p.149)를 사용하라. Detect(p.151)를 이용해 요소를 검색하라. 컬렉션에 걸쳐 누계(running total)를 유지하려면 Inject:Into: (p.152)를 사용하라.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Do===&lt;br /&gt;
&lt;br /&gt;
* 컬렉션 내 각 요소에 대한 코드를 어떻게 실행하는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이것은 기본적 메시지로서, 나머지 열거 메시지들이 이로부터 빌드된다. 만일 절차적 언어라면 컬렉션을 통해 반복되는 관용구의 작은 집합을 가지고, 연결 리스트용으로 하나, 배열용 하나, 해시 테이블용으로 하나가 있을 것이다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
열거를 목적으로 하는 경우, 스몰토크에선 컬렉션 클래스 간 차이가 없다. 프로그래머로서 당신은 루프 카운터의 반복 또는 리스트를 따르는 walking pointer를 절대 명시적으로 다루지 않는다. 단지 &amp;quot;do:&amp;quot;만 전송하면 마술이 펼쳐진다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
do:의 단순성에도 불구하고 필자는 종종 누군가 이전에 가진 습관에 따라 작성한 코드를 목격하곤 한다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
index := 1.&lt;br /&gt;
[index &amp;lt;= aCollection size] whileTrue:&lt;br /&gt;
    [...aCollection at: index...&lt;br /&gt;
    index := index + 1]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
변명의 여지가 없다. do:를 이용한 코드가 훨씬 더 짧고 읽기 쉽다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
aCollection do: [:each | ...each...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
두 코드에는 작은 성능적 차이가 존재한다. Windows 3.0.1용 VisualSmalltalk에서 개방 코딩(open code)된 반복(iteration)을 1,000 element Array에 1 밀리 초로 측정한 반면 do:를 전송 시 1.7 밀리 초가 소요되었다. 어떠한 처리든 요소에서 실행 중이라면 루프 오버헤드(loop overhead)는 즉시 사라질 것이며, 추후에 다시 들어가 소수의 반복을 개방 코딩하더라도 별 문제가 되지 않는다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 컬렉션의 요소를 반복하도록 컬렉션에 do:를 전송하라. one argument 블록을 argument로서 do: 로 전송하라. 각 요소마다 한 번씩 평가될 것이다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
파라미터의 Collection에 대해서도 정의되는 Collection&amp;gt;&amp;gt;add: 와 같은 연산은 (Collection&amp;gt;&amp;gt;addAll:) 종종 do:를 비롯해 그보다 더 간단한 연산으로 구현된다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
Collection&amp;gt;&amp;gt;addAll: aCollection&lt;br /&gt;
    aCollection do: [:each | self add: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
필자는 Pascal과 C를 사용하며 자랐다. 그 중에서도 루프를 작성하고, 인덱스 증가(index increment)를 올바른 장소에 넣는 데에 소질이 있었다. 하지만 스몰토크를 사용한지 6개월 정도가 지났을 때 손으로 열거를 작성해야 하는데 루프 인덱스를 수동으로 업데이트하는 법을 잊은 적이 있다. 항상 증가를 넣는 것을 잊어버리거나 증가 대신 감소(decrement)를 해버리기 일쑤였다. 그럴 때마다 당혹스러움을 감출 수 없었다. 그 이후 그런 일은 거의 없어서 기쁘지만, 훌륭한 기술인데 비해 잊어버리기 쉽다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;블록에 Simple Enumeration Parameter(p.182)를 사용하라.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Collect===&lt;br /&gt;
&lt;br /&gt;
* 컬렉션 내 각 객체로 전송되는 메시지의 결과를 어떻게 작동시키는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이것이 오래된 절차적 프로그래밍이었다면 우리는 아마 열거 블록이 요소로 메시지를 보내고낸 그 이후 계산에 메시지의 결과를 사용하는 코드를 작성할 것이다. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self children do: [:each | self passJudgement: each&lt;br /&gt;
hairStyle]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
헤어스타일을 처리하길 원하는 또 다른 코드도 위와 유사한 코드를 가질 것이다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self children do: [:each | self trim: each hairStyle]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이는 다시 &amp;#039;꼭 한 번만&amp;#039; 규칙에 위배된다. 두 개의 코드 조각(fragment)은 자녀의 헤어스타일에 실행되는 연산을 제외하면 매우 유사해 보인다. &lt;br /&gt;
우리는 본래 컬렉션의 각 요소로 전송되는 메시지의 결과를 포함하는 중간 컬렉션을 생성함으로써 코드의 공통성(commonality)을 포착할 수 있다. 그리고 우리는 새 컬렉션에 열거(enumerate over)할 수 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
코드의 새로운 명확성을 위해서는 변형된 요소를 보유하는 새 컬렉션을 생성하고 컬렉션에 두 번 반복(iterate over)하는 수고를 해야 한다: 한 번은 그것을 변형하고 한 번은 그것으로 계산. 중간 컬렉션으로부터 발생하는 성능 문제를 발견하는 경우, 이 문제는 후에 쉽게 수정된다. 정보 전달은 확실히 그만큼 비용을 들일 가치가 있으며, 이를 수정하고 나면 성능 문제는 절대 발생하지 않을 가능성이 크다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 본래 컬렉션의 각 요소를 이용해 collect: 로 전달된 블록을 평가한 결과를 요소로 하는 새 컬렉션을 collect:를 이용해 생성하라. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
위 코드의 공통 부분을 포착하기 위해 collect:와 함께 Composed Method를 이용할 수 있다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
childrenHairStyles&lt;br /&gt;
    ^self children collect: [:each | each hairStyle]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
그리고 코드 조각을 간소화할 수 있다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self childrenHairStyles do: [:each | self passJudgement: each]&lt;br /&gt;
self childrenHairStyles do: [:each | self trim: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;어쩔 수 없다면 특수 Enumeration Method(p.144)와 함께 collect:를 이용해 코드의 성능을 개선하라. 블록 argument에서 Simple Enumeration Parameter(p.182)를 사용하라.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Select/Reject===&lt;br /&gt;
&lt;br /&gt;
* 컬렉션의 일부를 필터링하는 방법은?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
절차적 해결방법은 두 가지 작업을 하는 열거 블록을 가지는 것이다 - 하나는 요소의 &amp;quot;흥미로움&amp;quot;을 검사하고, 하나는 흥미로운 요소들에 대해 일부 액션을 조건적으로 실행하는 일이다. 이러한 스타일로 작성된 코드는 아래와 같은 모습을 한다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self children do: [:each | each isResponsible ifTrue: [self&lt;br /&gt;
buyCar: each]]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이와 같은 코드는 처음 작성할 때는 괜찮지만 동일한 필터를 다른 곳에 위치시키길 원할 가능성이 크다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self children do: [:each | each isResponsible ifTrue: [self&lt;br /&gt;
addToWill: each]]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
꼭 한 번씩만 말해야 한다는 규칙을 기억하는가? 위의 코드 두 조각은 이 규칙에 위배된다. 두 번째 꺾쇠괄호까지는 모든 것이 정확히 동일하다. 그러한 공통성을 어떻게서든 포착하고자 한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이에 대한 해결책으로는, 흥미로운 요소들만 포함하는 컬렉션을 생성하여 그 곳에서 작동시키는 방법이다. 새 컬렉션에는 비용이 든다ㅡ원본 컬렉션과 따로 생성되어야 한다. 좀 더 표현적인 코드를 만들지 못한다면 garbage 컬렉션이 얼마나 효율적인들 무슨 의미가 있겠는가. 중간 컬렉션을 생성하는 비용이 너무 비싸다면 후에 쉽게 수정할 수 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 흥미로운 요소들만 포함하는 새 컬렉션을 리턴하기 위해선 select:와 reject:를 사용하라. 새 컬렉션을 열거하라. 둘 다 부울값(Boolean)을 리턴하는 one argument Block을 취하라. Select:는 Block이 true를 리턴하는 요소들을 제공하고, reject:는 Block이 false를 리턴하는 요소들을 제공한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
위의 두 코드 조각의 공통성을 확인하려면 select:와 함께 Composed Method를 이용해 책임이 있는 자녀(responsible children)를 리턴하는 메서드를 생성하라:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
responsibleChildren&lt;br /&gt;
    ^self children select: [:each | each isResponsible]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
그리고 두 개의 코드 조각을 간소화할 수 있다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self responsibleChildren do: [:each | self buyCar: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self responsibleChildren do: [:each | self addToWill: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;생성 비용을 피하기 위해 특수 목적용 Enumeration Method(열거 메소드)를 이용할 수 있다. 블록 argument에 Simple Enumeration Parameter를 이용하라. 성능을 최적화하려면 Lookup Cache를 사용하라.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Detect===&lt;br /&gt;
&lt;br /&gt;
* 컬렉션을 어떻게 검색하는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
또 다른 공통된 컬렉션 관용구로, 특정 기준에 부합하는 요소의 검색을 들 수 있다. 물론 이것은 do:를 이용하고, 열거 블록 내에서 기준을 검사하며, 조건적으로 일부 코드를 실행하여 구현할 수 있다. 아래 코드는 책임이 있는 첫 자녀에게 자동차 키를 제공한다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self children do: [:each | each isResponsible ifTrue: [each&lt;br /&gt;
giveKeys: self carKeys. ^self]]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
조건부 코드가 한 번만 실행되어야 한다는 점을 주목하라. 이를 복합 루프에서 관리하면 따르기가 매우 힘든 코드가 발생할 수 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 컬렉션에 detect:를 전송함으로써 컬렉션을 검색하라. 블록 argument가 true로 평가하는 첫 번째 요소가 리턴될 것이다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
검색은 one argument 블록을 argument로 취하는데 이는 true 또는 false를 평가한다. 이는 블록이 true라고 평가하는 첫 번째 요소를 리턴한다. 위의 코드는 아래와 같이 된다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
(self children detect: [:each | each isResponsible])&lt;br /&gt;
giveKeys: self carKeys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
detect:의 변형형태로, detect:ifNone:이 있는데, 이는 추가 제로 파라미터 Block을 argument로 취한다. 어떤 요소가 발견될지 확실하지 않을 경우 이를 이용하라. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Detect:ifNone:은 기발한 (&amp;quot;적어도 처음엔 읽기 힘들다&amp;quot;) 관용구를 발생시킨다. 어떤 요소든 기준에 충족할 경우 메서드로부터 true를 리턴하고 나머지는 false를 리턴하길 원한다면 어떻게 할 것인가?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
hasResponsibleChild&lt;br /&gt;
    self children&lt;br /&gt;
        detect: [:each | each isResponsible]&lt;br /&gt;
        ifNone: [^false].&lt;br /&gt;
    ^true&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이를 종종 이용하긴 하지만 그다지 자랑할만한 일은 아니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;첫 번째 블록 argument에 Simple Enumeration Parameter(p.182)를 사용하라. 성능을 최적화하기 위해 Lookup Cache(p.161)를 사용하라.&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Inject:into:===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;실행 중인 값을 유지하는 Enumeration(p.144)가 필요하다. &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Collection을 반복하는 동안 어떻게 실행 중인 값을 유지하는가?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
모두가 학습하는 첫 번째 절차적 프로그래밍 패턴들 중 하나는 누계(running total)를 유지하는 방법이다. &lt;br /&gt;
&lt;br /&gt;
# 합계를 초기화하라. &lt;br /&gt;
# 컬렉션의 각 요소마다 합계를 수정하라 (총계, 최소값, 최대값 등). &lt;br /&gt;
# 결과를 이용하라.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
따라서 아래와 같은 스몰토크 코드를 많이 목격할 것이다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
| max |&lt;br /&gt;
max := 0.&lt;br /&gt;
self children do: [:each | max := max max: each value].&lt;br /&gt;
^max&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
사실 이것은 매우 흔한 코드라 이 작업을 대신 해주는 메시지도 있다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
그렇다면 이렇게 신비하고 강력한 메시지를 모든 사람이 사용하지 않는 이유는 뭘까? 그 이유는 바로 이름이 파격적이기 때문이다. 이 이름을 본 사람들은 &amp;quot;이 메시지가 하는 일을 알아낼 방법이 없겠는걸. 그냥 두는 편이 낫겠어,&amp;quot;라고 생각한다. 그렇다 하더라도 메시지의 사용을 피하기 위한 구실로 삼을 수는 없다. &amp;quot;해당 컬렉션에 실행되는 값을 유지하기&amp;quot;를 원한다면 이 이상한 이름으로 된 메시지를 사용해야 한다. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 실행 중인 값을 유지하려면 inject:into:를 사용하라. 첫 번째 argument를 초기 값으로 하라. 두 번째 argument는 2 요소(two element) 블록으로 만들어라. 블록 arguments를 &amp;quot;sum&amp;quot;과 &amp;quot;each&amp;quot;라고 불러라. 블록이 실행 값의 다음 값을 평가하도록 하라. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
위의 코드는 아래와 같은 모습이 된다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
^self children&lt;br /&gt;
    inject: 0&lt;br /&gt;
    into: [:sum :each | sum max: each]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
이는 필자가 학습한 inject:into:를 현명하게 사용한 것이다. 필자는 보통 &amp;quot;현명&amp;quot;하단 말을 모욕을 줄 때 사용하지만 여기서는 사용하지 않을 수가 없다. 문제는 컬렉션 내에 근접한 쌍들(pairs)을 반복하는 데에 있다. 즉, 일부 코드에서 요소 1과 2, 요소 2와 3 순으로 평가하고자 한다. 여기에 inject:into:를 사용하는 방법은 다음과 같다:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;smalltalk&amp;quot;&amp;gt;&lt;br /&gt;
self children&lt;br /&gt;
    inject: nil&lt;br /&gt;
    into:&lt;br /&gt;
        [:eachPrevious :eachNext |&lt;br /&gt;
        eachPrevious notNil ifTrue: [...].&lt;br /&gt;
        eachNext]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
처음 루프를 통과할 때 eachPrevious는 nil이고 eachNext는 컬렉션의 첫 번째 요소이다. 조건부 코드는 평가되지 않는다. 전체 블록은 컬렉션의 첫 번째 요소로 평가한다. 두 번째 루프를 통과 시, eachPrevious는 컬렉션의 첫 번째 요소이고 eachNext는 두 번째 요소이다. 조건부 코드가 실행된다. 이는 eachPrevious가 마지막에서 두 번째 요소가 되고 eachNext가 마지막 요소가 될 때까지 계속된다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Notes==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SmalltalkBestPracticePatterns]]&lt;/div&gt;</summary>
		<author><name>Onionmixer</name></author>
	</entry>
</feed>