SqueakByExample:9.5: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(SBE CollectionIterators 페이지 추가)
 
(용어수정)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
==Collection iterators==
==컬렉션의 반복자(Collection iterators)==
 
스몰토크에서 반복문이나 조건문은, 컬렉션, 정수, 블록과같은 객체에 대한 단순한 메시지 전송입니다(3 장을 참고해주세요). 스몰토크의 콜렉션계층은, 초기값부터 마지막값까지 변화하는 수치를 인자로 블록을 평가하는 to:do: 같은 저수준 메시지를 포함한 여러가지 고수준의 반복자<sup>iterator</sup><ref name="역자주1">Iterator 는 한국어로 대부분 반복자라고 부릅니다만, wikipedia 의 경우에는 객체지향 프로그래밍에서 container 에 제공되는것이라고 언급합니다. 이런 컨테이너는 사실 smalltalk 에서는 컬렉션이라고 할만하죠. 여기서는 일반적으로 한국에서 사용하듯이 '''반복자''' 라는 용어를 사용하도록 하겠습니다.</ref>를 제공합니다. 이런 반복자의 사용은, 프로그램의 코드를 좀 더 튼튼하고 간결하게 만들어 줍니다.


스몰토크 루프(loops)와 조건식들은(conditionals) 단순히 컬렉션들에 발송된 메시지들이고 또는 정수들 또는 블록들과 같은 다른 오브젝트들 입니다. (3장을 보십시오) 처음 숫자부터 마지막 숫자에 걸쳐 있는 인수로 블록을 평가하는 to:do:와 같은 저급 레벨(low-level) 메시지 뿐만 아니라 스몰토크 컬렉션 계층도 (the Smalltalk collection hierarchy)는 다양한 고급 레벨(high-level) iterators를 제공합니다. 이러한 iterators를 사용은, 여러분의 코드를 좀더 견고하고 간결하게 만들어 드릴 것입니다.




===Iterating (do:)===
===Iterating (do:)===


메소드 do:는 기본 켤렉션 iterator입니다. 이것은 그 자체의 인수(단일 인수를 취하는 블록)를 수신자(the receiver)의 구성요소에 적용합니다. 다음 예시는 수신자에 들어 있는 모든 문자열을 transcript에 인쇄합니다.  
do: 메서드는 기본적인 컬렉션 반복자 메서드입니다. do: 메서드는 그 자체의 인자(단일 인자를 취하는 블록)를 수신자의 요소에 순서대로 적용합니다. 다음 예제는 수신자에 들어 있는 모든 문자열을 Transcript 에 print 합니다.  
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
#('bob' 'joe' 'toto') do: [:each | Transcript show: each; cr].
#('bob' 'joe' 'toto') do: [:each | Transcript show: each; cr].
Line 12: Line 13:




'''변수''' do:without:, doWithIndex:reverseDo::Do와 같은 변수 계열이 있습니다. Indexed 컬렉션들(Array, OrderedCollection, SortedCollectino)을 위해, 메소드 doWithIndex: 역시 현재 index에 대한 접근을 제공합니다. 이 메소드는 클래스 Number에서 정의되는 to:do와 관련됩니다.
'''변종(Variants)'''  
 
do: 메서드에는 많은 변종이 있는데, 예를들면 do:without:, doWithIndex:, reverseDo: 등이 해당되겠죠. indexed 컬렉션(Array, OrderedCollection, SortedCollectino) 을 위한 doWithIndex: 메서드는 현재 index 에 대한 접근등을 제공합니다. 이 doWithIndex: 메서드는 Number 클래스에서 정의된 to:do: 와 관련이 있습니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
#('bob' 'joe' 'toto') doWithIndex: [:each :i | (each = 'joe') ifTrue: [ ↑ i ] ]    ⇒    2
#('bob' 'joe' 'toto') doWithIndex: [:each :i | (each = 'joe') ifTrue: [ ↑ i ] ]    ⇒    2
</syntaxhighlight>
</syntaxhighlight>


orderd collection 의 reverseDo:는 컬렉션의 정렬을 역순으로 처리합니다.


reverseDo:는 정렬된 컬렉션들에 작용하여, 컬력션을 반대 순서로 나타나게 합니다.


다음 코드는 흥미로운 메시지 do:separatedBy:를 보여드립니다. 이 메시지는 두 개의 구성요소 사이에 있는 두 번째 블록만을 실행합니다.
아래의 코드에서 do:separatedBy: 라는 재미있는 메서드를 볼 수 있습니다. 이 메시지는 두개의 요소 사이를 대상으로 두번째 인자로 주어진 블록을 실행합니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
res := ''.
res := ''.
Line 28: Line 33:




이 코드는, 중간 문자열을 만들기 때문에, 특별히 효과적이지 않으며, 결과를 버퍼하기 위한 스트림(stream)사용하는 것이 나을 것이라는 사실을 주목해 주십시오. (10장을 보십시오)
위의 방법은 효율적이라고 볼 수는 없으며, 결과를 buffer 에 쓰기 위해서는 stream 을 사용하는게 더 좋은 방법이라는걸 주의해주세요(10 장을 참고).
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
String streamContents: [:stream | #('bob' 'joe' 'toto') asStringOn: stream delimiter: '.' ]    ⇒    'bob.joe.toto'
String streamContents: [:stream | #('bob' 'joe' 'toto') asStringOn: stream delimiter: '.' ]    ⇒    'bob.joe.toto'
Line 34: Line 40:




'''Dictionaries.''' 메시지 do:가 dicionary로 발송되면 accoount에 취해진 구성요소들은 value들이 되며 association이 되지 않습니다. 사용해야 할 적합한 메소드들은 keysDo:, valuesDo: associationsDo:이며, 이것들은 각각 Keys, values 또는 associations에서 반복실행(iterate)됩니다.
'''Dictionaries.'''  
 
메시지 do: 가 dictionary 로 전송될때 검사되는 요소는 value 가 되며 association<sup>연관</sup>은 되지 않습니다.이런 경우 keysDo:, valuesDo: associationsDo: 등을 사용하는것이 더 적당하며, 각 메서드들은 Keys, values 또는 associations 에 대해 반복 실행됩니다.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
colors := Dictionary newFrom: { #yellow --> Color yellow. #blue --> Color blue. #red --> Color red }.
colors := Dictionary newFrom: { #yellow -> Color yellow. #blue -> Color blue. #red -> Color red }.
colors keysDo: [:key | Transcript show: key; cr].                                    "displays the keys"
colors keysDo: [:key | Transcript show: key; cr].                                    "displays the keys"
colors valuesDo: [:value | Transcript show: value;cr].                              "displays the values"
colors valuesDo: [:value | Transcript show: value;cr].                              "displays the values"
colors associationsDo: [:value | Transcript show: value;cr].                        "displays the associations"
colors associationsDo: [:value | Transcript show: value;cr].                        "displays the associations"
</syntaxhighlight>
</syntaxhighlight>




===Collecting results (collect:)===
===Collecting results (collect:)===


만약 여러분이 do:를 사용하는 것 보다,  컬렉션의 구성요소들을 처리 하고, 결과로서 새로운 컬렉션을 만드시기를 좀 더 원하신다면, 아마도 collect: 또는 다른 iterator 메소드들 중의 하나를 사용하시는 것이 보다 나은 방법일 것입니다. 이것들 중 대부분을 켈렉션과 그 자체의 서브클래스들 중, 열거 프로토콜(enumerating protocol)에서 발견하실 수 있습니다.
콜렉션의 요소를 처리하고, 그 결과를 새로운 컬렉션으로 만들기를 원한다면, do: 를 사용하는것보다 collect: 또는 그 외의 반복자 메서드를 이용하는것이 보다 좋은 방법이 될겁니다. 이런 메서드의 대부분은, Collection 이나 서브클래스에서 열거<sup>enumerating</sup> 프로토콜쪽에서 찾을 수 있습니다.
 
예를들어 각 요소의 2 배로 하는 새로운 컬렉션을 만든다고 생각해 보겠습니다. do: 를 사용한다면 다음과 같은 코드가 될겁니다.


우리가 다른 컬렉션에서 두 배의 구성요소들을 포함한 컬렉션을 원한다고 상상해 보십시오. Do:를 사용하여, 우리는 반드시 다음 표현식을 작성해야만 합니다. 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
double := OrderedCollection new.
double := OrderedCollection new.
Line 55: Line 66:




메소드 collect:구성요소들을 위해 그 자체의 인수 블록(argument block)을 실행하고 결과들을 포함하고 있는 새로운 컬렉션을 리턴합니다. 대신, collect:를 사용하면 코드는 훨씬 단순해 집니다:  
collect: 메서드는, 인자로 건네받은 블록을 요소에 대해 실행하고, 결과를 수용하는 새로운 콜렉션을 반환합니다. do: 대신에 collect: 를 사용하면 코드는 매우 간결해지게 됩니다:
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
#(1 2 3 4 5 6) collect: [:e | 2 * e]    ⇒    #(2 4 6 8 10 12)
#(1 2 3 4 5 6) collect: [:e | 2 * e]    ⇒    #(2 4 6 8 10 12)
Line 61: Line 73:




do: 에 비교해 볼 때, collect:의 장점들은 우리가 정수들의 컬렉션을 취하고 이 정수들의 절대 값들의 컬렉션을 결과로서 만들어낸 다음 예시에서 좀더 극명하게 보입니다.
do: 와 비교되는 collect: 의 장점은 아래의 예제에서 더 극적으로 확인되는데, 예제는 정수의 콜렉션을 준비하고, 그 절대값을 결과로 받는경우가 되겠습니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
aCol := #( 2 --3 4 --35 4 --11).
aCol := #( 2 -3 4 -35 4 -11).
result := aCol species new: aCol size.
result := aCol species new: aCol size.
1 to: aCol size do: [ :each | result at: each put: (aCol at: each) abs].
1 to: aCol size do: [ :each | result at: each put: (aCol at: each) abs].
Line 70: Line 83:




위의 표현식과는 반대로 다음 표현식은 훨씬 단순합니다.  
위의 표현식과 대비되는 아래의 간결한 표현식을 비교해 보시기 바랍니다.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
#( 2 --3 4 --35 4 --11) collect: [:each | each abs ]    ⇒    #(2 3 4 35 4 11)
#( 2 -3 4 -35 4 -11) collect: [:each | each abs ]    ⇒    #(2 3 4 35 4 11)
</syntaxhighlight>
</syntaxhighlight>




두 번째 솔루션의 좀더 많은 장점은, 그것이 bags의 세트들에도 작동될 것이라는 것입니다.
두 번째 방법이 더 좋은 이유는, 이런 방법이 Set 이나 Bag 에서도 동일하게 작동할거라는 점입니다.
 
일반적으로 컬렉션의 각 요소들에 메시지를 발송하는것을 원하지 않는다면, do: 사용을 피하는 것이 바람직합니다.


여러분이 일반적으로 컬렉션의 각 구성요소들에 메시지를 발송하기를 원하시지 않는다면, do: 사용을 피하시는 것이 바람직합니다.
메시지 collect: 를 송신하면 수신자와 동일한 종류의 컬렉션을 반환한다는 사실에 주의해주세요. 이러한 이유 때문에, 다음 코드는 실패하게 됩니다. (String 은 Integer 값들을 가지고 있지 않습니다.)


메시지 collect: 발송은 수신자(the receiver)로서 동일한 종류의 컬렉션을 리턴한다는 사실에 주의하여 주십시오. 이러한 이유 때문에, 다음 코드가 실패하였습니다. (문자열은 정수 값들을 보유하지 않습니다)
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
'abc' collect: [:ea | ea asciiValue ] "error!"
'abc' collect: [:ea | ea asciiValue ] "error!"
Line 86: Line 101:




대신에 우리는 문자열을 배열 또는 OrderedCollection으로 변환하였습니다.
그대신 String 을 Array 또는 OrderedCollection 으로 변환해서 처리할 수 있습니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
'abc' asArray collect: [:ea | ea asciiValue ]    ⇒    #(97 98 99)
'abc' asArray collect: [:ea | ea asciiValue ]    ⇒    #(97 98 99)
</syntaxhighlight>
</syntaxhighlight>


실제로 collect: 는 수신자와 정확히 동일한 클래스에 대한 컬렉션 반환을 보장하지 않으며 오직 동일한 "종(species)" 만을 반환합니다. Interval 의 경우 유사한 종류는 사실 Array 입니다.


실제로 collect:는 수신자(the receive)와 정확히 동일한 클래스의 컬렉션(collection) 리턴을 보장하지 않으며, 오직 동일한 “종들(species)” 만을 리턴할 뿐입니다. 인터벌의 경우, 실제로 유사종은 배열(Array)입니다 !
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
(1 to: 5) collect: [ :ea | ea * 2 ]    ⇒    #(2 4 6 8 10)
(1 to: 5) collect: [ :ea | ea * 2 ]    ⇒    #(2 4 6 8 10)
Line 98: Line 114:




===구성요소들을 선택하고 거부하기 (Selecting and rejecting elements)===


select:특별한 조건을 만족시키는 수신자(the receiver)의 구성요소들을 리턴합니다:
===요소들을 선택하고 거부하기===
 
select: 메서드는 특별한 조건을 만족시키는 수신자의 요소들을 반환합니다:
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
(2 to: 20) select: [:each | each isPrime]    ⇒    #(2 3 5 7 11 13 17 19)
(2 to: 20) select: [:each | each isPrime]    ⇒    #(2 3 5 7 11 13 17 19)
Line 106: Line 124:




reject:반대되는 작업을 수행합니다:
reject: 메서드는 select: 와는 반대되는 작업을 수행합니다:
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
(2 to: 20) reject: [:each | each isPrime]    ⇒    #(4 6 8 9 10 12 14 15 16 18 20)
(2 to: 20) reject: [:each | each isPrime]    ⇒    #(4 6 8 9 10 12 14 15 16 18 20)
Line 112: Line 131:




===detect:로 구성요소 식별하기===


메소드 detect:블록 인수(block argument)를 매치하는 수신자(the receive)의 첫 번째 구성요소를 리턴 합니다.  
===detect: 로 요소를 식별하기===
 
detect: 메서드는 수신자의 요소중에서 주어진 블록 인자를 만족하는 첫번째 요소를 반환합니다.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
'through' detect: [:each | each isVowel]    ⇒    $o
'through' detect: [:each | each isVowel]    ⇒    $o
Line 120: Line 141:




메소드 detect:ifNone은 메소드 detect:의 변수입니다. 이것의 두 번째 블록은, 그 블록과 매칭되는 구성요소가 없을 때, 평가됩니다.
메서드 detect:ifNone 은 메서드 detect: 의 변종입니다. 이 메서드의 두 번째 인자(블록)은, 첫번째 블록에서 일치되는 요소가 없을 때, 실행됩니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Smalltalk allClasses detect: [:each | '*java*' match: each asString] ifNone: [ nil ]    ⇒    nil
Smalltalk allClasses detect: [:each | '*java*' match: each asString] ifNone: [ nil ]    ⇒    nil
</syntaxhighlight>
</syntaxhighlight>




===inject:into:로 결과들을 모으기===
===inject:into:로 결과들을 모으기===


기능적 프로그래밍 언어들은, 종종 몇 가지 바이너리 연산자(binary operator)를 컬렉션의 모든 구성요소에 반복적으로 적용하여 결과를 모으기 위해 fold 또는 reduce라고 지칭되는 좀더 높은 수준의 정렬 기능(higher-order function)을 제공합니다 스퀵에서는 이 기능이 Collection»inject:into:에 의해 수행될 수 있습니다.
함수형 프로그래밍 언어들은, 자주 몇 가지 이항 연산자를 컬렉션의 모든 구성요소에 반복적으로 적용해서 결과를 받기 위해 fold 또는 reduce 라고 지칭되는 좀 더 고수준의 정렬 기능을 제공합니다. 스퀵에서 이런 기능은 Collection>>inject:into: 를 사용하면 됩니다.
 
이 메서드의 첫 번째 인자는 초기 값이며, 두 번째 인자는 지금까지의 결과와 각각의 구성 요소에 차례대로 적용된 2 개의 인자 블록이 됩니다.


첫 번째 인수는 초기 값이며 두 번째 인수는 지금까지의 결과와 각각의 구성요소들에 차례대로 적용된 2개의 인수 블록입니다.  
inject:into: 의 간단한 예로서 숫자들의 컬렉션에 대한 총합을 계산해 보겠습니다. 가우스(Gauss) 처럼, 스퀵에서는 1 에서 부터 시작한 100 까지의 정수<sup>Integer</sup>들의 총합을 구하기 위해 아래와 같은 표현식을 작성하면 됩니다:


inject:into:의 사소한 어플리케이션은 숫자들의 컬렉션들의 합을 생산합니다.
스퀵에서는 가우스를 따라하여, 첫 번째 100 개의 정수들의 총합을 구하기 위해 이 표현식을 작성할 수 있습니다:
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
(1 to: 100) inject: 0 into: [:sum :each | sum + each ]    ⇒    5050
(1 to: 100) inject: 0 into: [:sum :each | sum + each ]    ⇒    5050
Line 139: Line 162:




다른 예시는 분수들을 계산하는 다음 1 인수 블록(one-argument block)입니다:
또다른 예로서, 분수들을 계산할때 1 개의 인자 블록을 쓴다면 다음과 같이 작성할 수 있겠죠:
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
factorial := [:n | (1 to: n) inject: 1 into: [:product :each | product * each ] ].
factorial := [:n | (1 to: n) inject: 1 into: [:product :each | product * each ] ].
Line 146: Line 170:




===다른 메시지들===


'''count:''' 메시지 count:는 조건식(condition)을 만족시키는 약간의 구성요소들을 리턴합니다. 조건식은 불리언 블록(Boolean block)으로 표시됩니다.  
===그 외의 메시지들===
 
'''count:''' 메시지 count: 는 조건식을 만족시키는 요소의 개수를 반환합니다. 조건식은 Boolean 블록으로 작성합니다.  
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Smalltalk allClasses count: [:each | 'Collection*' match: each asString ]    ⇒    2
Smalltalk allClasses count: [:each | 'Collection*' match: each asString ]    ⇒    2
Line 154: Line 180:




'''includes:''' 메시지 includes:는 인수가 컬렉션에 포함되었는지의 여부를 점검합니다.
'''includes:''' 메시지 includes: 는 인자가 컬렉션에 포함되었는지의 여부를 점검합니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
colors := {Color white . Color yellow. Color red . Color blue . Color orange}.
colors := {Color white . Color yellow. Color red . Color blue . Color orange}.
Line 161: Line 188:




'''anySatisfy:''' 메시지 anySatisfy:는 만약 적어도 1개의 컬렉션 구성요소가 인수에 의해 제시된 조건식(the condition)을 만족시킬 경우 true로 답변합니다.  
'''anySatisfy:''' 메시지 anySatisfy: 는 만약 최소 1 개의 컬렉션 요소가 인자에 의해 제시된 조건식을 만족시킬 경우 true 로 답변합니다.  
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
colors anySatisfy: [:c | c red > 0.5]    ⇒    true
colors anySatisfy: [:c | c red > 0.5]    ⇒    true
</syntaxhighlight>
</syntaxhighlight>





Latest revision as of 19:34, 16 September 2013

컬렉션의 반복자(Collection iterators)

스몰토크에서 반복문이나 조건문은, 컬렉션, 정수, 블록과같은 객체에 대한 단순한 메시지 전송입니다(3 장을 참고해주세요). 스몰토크의 콜렉션계층은, 초기값부터 마지막값까지 변화하는 수치를 인자로 블록을 평가하는 to:do: 같은 저수준 메시지를 포함한 여러가지 고수준의 반복자iterator[1]를 제공합니다. 이런 반복자의 사용은, 프로그램의 코드를 좀 더 튼튼하고 간결하게 만들어 줍니다.


Iterating (do:)

do: 메서드는 기본적인 컬렉션 반복자 메서드입니다. do: 메서드는 그 자체의 인자(단일 인자를 취하는 블록)를 수신자의 각 요소에 순서대로 적용합니다. 다음 예제는 수신자에 들어 있는 모든 문자열을 Transcript 에 print 합니다.

#('bob' 'joe' 'toto') do: [:each | Transcript show: each; cr].


변종(Variants)

do: 메서드에는 많은 변종이 있는데, 예를들면 do:without:, doWithIndex:, reverseDo: 등이 해당되겠죠. indexed 컬렉션(Array, OrderedCollection, SortedCollectino) 을 위한 doWithIndex: 메서드는 현재 index 에 대한 접근등을 제공합니다. 이 doWithIndex: 메서드는 Number 클래스에서 정의된 to:do: 와 관련이 있습니다.

#('bob' 'joe' 'toto') doWithIndex: [:each :i | (each = 'joe') ifTrue: [  i ] ]        2

orderd collection 의 reverseDo:는 컬렉션의 정렬을 역순으로 처리합니다.


아래의 코드에서 do:separatedBy: 라는 재미있는 메서드를 볼 수 있습니다. 이 메시지는 두개의 요소 사이를 대상으로 두번째 인자로 주어진 블록을 실행합니다.

res := ''.
#('bob' 'joe' 'toto') do: [:e | res := res, e ] separatedBy: [res := res, '.'].
res        'bob.joe.toto'


위의 방법은 효율적이라고 볼 수는 없으며, 결과를 buffer 에 쓰기 위해서는 stream 을 사용하는게 더 좋은 방법이라는걸 주의해주세요(10 장을 참고).

String streamContents: [:stream | #('bob' 'joe' 'toto') asStringOn: stream delimiter: '.' ]        'bob.joe.toto'


Dictionaries.

메시지 do: 가 dictionary 로 전송될때 검사되는 요소는 value 가 되며 association연관은 되지 않습니다.이런 경우 keysDo:, valuesDo: associationsDo: 등을 사용하는것이 더 적당하며, 각 메서드들은 Keys, values 또는 associations 에 대해 반복 실행됩니다.

colors := Dictionary newFrom: { #yellow -> Color yellow. #blue -> Color blue. #red -> Color red }.
colors keysDo: [:key | Transcript show: key; cr].                                    "displays the keys"
colors valuesDo: [:value | Transcript show: value;cr].                               "displays the values"
colors associationsDo: [:value | Transcript show: value;cr].                         "displays the associations"


Collecting results (collect:)

콜렉션의 요소를 처리하고, 그 결과를 새로운 컬렉션으로 만들기를 원한다면, do: 를 사용하는것보다 collect: 또는 그 외의 반복자 메서드를 이용하는것이 보다 좋은 방법이 될겁니다. 이런 메서드의 대부분은, Collection 이나 서브클래스에서 열거enumerating 프로토콜쪽에서 찾을 수 있습니다.

예를들어 각 요소의 2 배로 하는 새로운 컬렉션을 만든다고 생각해 보겠습니다. do: 를 사용한다면 다음과 같은 코드가 될겁니다.

double := OrderedCollection new.
#(1 2 3 4 5 6) do: [:e | double add: 2 * e].
double        an OrderedCollection(2 4 6 8 10 12)


collect: 메서드는, 인자로 건네받은 블록을 각 요소에 대해 실행하고, 결과를 수용하는 새로운 콜렉션을 반환합니다. do: 대신에 collect: 를 사용하면 코드는 매우 간결해지게 됩니다:

#(1 2 3 4 5 6) collect: [:e | 2 * e]        #(2 4 6 8 10 12)


do: 와 비교되는 collect: 의 장점은 아래의 예제에서 더 극적으로 확인되는데, 예제는 정수의 콜렉션을 준비하고, 그 절대값을 결과로 받는경우가 되겠습니다.

aCol := #( 2 -3 4 -35 4 -11).
result := aCol species new: aCol size.
1 to: aCol size do: [ :each | result at: each put: (aCol at: each) abs].
result        #(2 3 4 35 4 11)


위의 표현식과 대비되는 아래의 간결한 표현식을 비교해 보시기 바랍니다.

#( 2 -3 4 -35 4 -11) collect: [:each | each abs ]        #(2 3 4 35 4 11)


두 번째 방법이 더 좋은 이유는, 이런 방법이 Set 이나 Bag 에서도 동일하게 작동할거라는 점입니다.

일반적으로 컬렉션의 각 요소들에 메시지를 발송하는것을 원하지 않는다면, do: 사용을 피하는 것이 바람직합니다.

메시지 collect: 를 송신하면 수신자와 동일한 종류의 컬렉션을 반환한다는 사실에 주의해주세요. 이러한 이유 때문에, 다음 코드는 실패하게 됩니다. (String 은 Integer 값들을 가지고 있지 않습니다.)

'abc' collect: [:ea | ea asciiValue ] "error!"


그대신 String 을 Array 또는 OrderedCollection 으로 변환해서 처리할 수 있습니다.

'abc' asArray collect: [:ea | ea asciiValue ]        #(97 98 99)

실제로 collect: 는 수신자와 정확히 동일한 클래스에 대한 컬렉션 반환을 보장하지 않으며 오직 동일한 "종(species)" 만을 반환합니다. Interval 의 경우 유사한 종류는 사실 Array 입니다.

(1 to: 5) collect: [ :ea | ea * 2 ]        #(2 4 6 8 10)


요소들을 선택하고 거부하기

select: 메서드는 특별한 조건을 만족시키는 수신자의 요소들을 반환합니다:

(2 to: 20) select: [:each | each isPrime]        #(2 3 5 7 11 13 17 19)


reject: 메서드는 select: 와는 반대되는 작업을 수행합니다:

(2 to: 20) reject: [:each | each isPrime]        #(4 6 8 9 10 12 14 15 16 18 20)


detect: 로 요소를 식별하기

detect: 메서드는 수신자의 요소중에서 주어진 블록 인자를 만족하는 첫번째 요소를 반환합니다.

'through' detect: [:each | each isVowel]        $o


메서드 detect:ifNone 은 메서드 detect: 의 변종입니다. 이 메서드의 두 번째 인자(블록)은, 첫번째 블록에서 일치되는 요소가 없을 때, 실행됩니다.

Smalltalk allClasses detect: [:each | '*java*' match: each asString] ifNone: [ nil ]        nil


inject:into:로 결과들을 모으기

함수형 프로그래밍 언어들은, 자주 몇 가지 이항 연산자를 컬렉션의 모든 구성요소에 반복적으로 적용해서 결과를 받기 위해 fold 또는 reduce 라고 지칭되는 좀 더 고수준의 정렬 기능을 제공합니다. 스퀵에서 이런 기능은 Collection>>inject:into: 를 사용하면 됩니다.

이 메서드의 첫 번째 인자는 초기 값이며, 두 번째 인자는 지금까지의 결과와 각각의 구성 요소에 차례대로 적용된 2 개의 인자 블록이 됩니다.

inject:into: 의 간단한 예로서 숫자들의 컬렉션에 대한 총합을 계산해 보겠습니다. 가우스(Gauss) 처럼, 스퀵에서는 1 에서 부터 시작한 100 까지의 정수Integer들의 총합을 구하기 위해 아래와 같은 표현식을 작성하면 됩니다:

(1 to: 100) inject: 0 into: [:sum :each | sum + each ]        5050


또다른 예로서, 분수들을 계산할때 1 개의 인자 블록을 쓴다면 다음과 같이 작성할 수 있겠죠:

factorial := [:n | (1 to: n) inject: 1 into: [:product :each | product * each ] ].
factorial value: 10        3628800


그 외의 메시지들

count: 메시지 count: 는 조건식을 만족시키는 요소의 개수를 반환합니다. 조건식은 Boolean 블록으로 작성합니다.

Smalltalk allClasses count: [:each | 'Collection*' match: each asString ]        2


includes: 메시지 includes: 는 인자가 컬렉션에 포함되었는지의 여부를 점검합니다.

colors := {Color white . Color yellow. Color red . Color blue . Color orange}.
colors includes: Color blue.        true


anySatisfy: 메시지 anySatisfy: 는 만약 최소 1 개의 컬렉션 요소가 인자에 의해 제시된 조건식을 만족시킬 경우 true 로 답변합니다.

colors anySatisfy: [:c | c red > 0.5]        true


Notes

  1. Iterator 는 한국어로 대부분 반복자라고 부릅니다만, wikipedia 의 경우에는 객체지향 프로그래밍에서 container 에 제공되는것이라고 언급합니다. 이런 컨테이너는 사실 smalltalk 에서는 컬렉션이라고 할만하죠. 여기서는 일반적으로 한국에서 사용하듯이 반복자 라는 용어를 사용하도록 하겠습니다.