SqueakByExample:9.5

From 흡혈양파의 번역工房
Revision as of 03:56, 17 August 2012 by Onionmixer (talk | contribs) (SBE CollectionIterators 페이지 추가)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Collection iterators

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


Iterating (do:)

메소드 do:는 기본 켤렉션 iterator입니다. 이것은 그 자체의 인수(단일 인수를 취하는 블록)를 수신자(the receiver)의 각 구성요소에 적용합니다. 다음 예시는 수신자에 들어 있는 모든 문자열을 transcript에 인쇄합니다.

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


변수 do:without:, doWithIndex:와 reverseDo::Do와 같은 변수 계열이 있습니다. Indexed 컬렉션들(Array, OrderedCollection, SortedCollectino)을 위해, 메소드 doWithIndex: 역시 현재 index에 대한 접근을 제공합니다. 이 메소드는 클래스 Number에서 정의되는 to:do와 관련됩니다.

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


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

다음 코드는 흥미로운 메시지 do:separatedBy:를 보여드립니다. 이 메시지는 두 개의 구성요소 사이에 있는 두 번째 블록만을 실행합니다.

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


이 코드는, 중간 문자열을 만들기 때문에, 특별히 효과적이지 않으며, 결과를 버퍼하기 위한 스트림(stream)을 사용하는 것이 나을 것이라는 사실을 주목해 주십시오. (10장을 보십시오)

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


Dictionaries. 메시지 do:가 dicionary로 발송되면 accoount에 취해진 구성요소들은 value들이 되며 association이 되지 않습니다. 사용해야 할 적합한 메소드들은 keysDo:, valuesDo: 와 associationsDo:이며, 이것들은 각각 Keys, values 또는 associations에서 반복실행(iterate)됩니다.

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: 또는 다른 iterator 메소드들 중의 하나를 사용하시는 것이 보다 나은 방법일 것입니다. 이것들 중 대부분을 켈렉션과 그 자체의 서브클래스들 중, 열거 프로토콜(enumerating protocol)에서 발견하실 수 있습니다.

우리가 다른 컬렉션에서 두 배의 구성요소들을 포함한 컬렉션을 원한다고 상상해 보십시오. 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:는 각 구성요소들을 위해 그 자체의 인수 블록(argument block)을 실행하고 결과들을 포함하고 있는 새로운 컬렉션을 리턴합니다. 대신, 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)


두 번째 솔루션의 좀더 많은 장점은, 그것이 bags의 세트들에도 작동될 것이라는 것입니다.

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

메시지 collect: 발송은 수신자(the receiver)로서 동일한 종류의 컬렉션을 리턴한다는 사실에 주의하여 주십시오. 이러한 이유 때문에, 다음 코드가 실패하였습니다. (문자열은 정수 값들을 보유하지 않습니다)

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


대신에 우리는 문자열을 배열 또는 OrderedCollection으로 변환하였습니다.

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


실제로 collect:는 수신자(the receive)와 정확히 동일한 클래스의 컬렉션(collection) 리턴을 보장하지 않으며, 오직 동일한 “종들(species)” 만을 리턴할 뿐입니다. 인터벌의 경우, 실제로 유사종은 배열(Array)입니다 !

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


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

select:는 특별한 조건을 만족시키는 수신자(the receiver)의 구성요소들을 리턴합니다:

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


reject:는 반대되는 작업을 수행합니다:

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


detect:로 구성요소 식별하기

메소드 detect:는 블록 인수(block argument)를 매치하는 수신자(the receive)의 첫 번째 구성요소를 리턴 합니다.

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


메소드 detect:ifNone은 메소드 detect:의 변수입니다. 이것의 두 번째 블록은, 그 블록과 매칭되는 구성요소가 없을 때, 평가됩니다.

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


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

기능적 프로그래밍 언어들은, 종종 몇 가지 바이너리 연산자(binary operator)를 컬렉션의 모든 구성요소에 반복적으로 적용하여 결과를 모으기 위해 fold 또는 reduce라고 지칭되는 좀더 높은 수준의 정렬 기능(higher-order function)을 제공합니다 스퀵에서는 이 기능이 Collection»inject:into:에 의해 수행될 수 있습니다.

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

inject:into:의 사소한 어플리케이션은 숫자들의 컬렉션들의 합을 생산합니다. 스퀵에서는 가우스를 따라하여, 첫 번째 100 개의 정수들의 총합을 구하기 위해 이 표현식을 작성할 수 있습니다:

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


다른 예시는 분수들을 계산하는 다음 1 인수 블록(one-argument block)입니다:

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


다른 메시지들

count: 메시지 count:는 조건식(condition)을 만족시키는 약간의 구성요소들을 리턴합니다. 조건식은 불리언 블록(Boolean block)으로 표시됩니다.

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개의 컬렉션 구성요소가 인수에 의해 제시된 조건식(the condition)을 만족시킬 경우 true로 답변합니다.

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


Notes