(본 게시물은 저작권의 문제 발생시 출판사의 요청에 의해 삭제될 수 있습니다.)
루비에는 컬렉션을 다루기 위한 배열과 해시라는 두개의 내장클래스가 준비되어 있다.
4.1. 배열
배열의 인덱스는 0 부터 시작한다. 배열의 위치에 아무것도 없다면 nil을 반환한다.
음수로 위치를 지정하면 배열의 뒤에서부터 위치를 계산해 해당하는 위치의 값을 반환한다.
a = [ 1, 3, 5, 7, 9 ]
a[-1] # => 9
a[-2] # => 7
a[-99] # => nil
배열 인덱스를 [start, count] 처럼 숫자 쌍으로 지정할 수도 있다. 이는 시작점(start)에서 count 만큼의 객체 참조를 뽑아서 새로운 배열을 만들어 반환한다.
a = [ 1, 3, 5, 7, 9 ]
a[1, 3] # => [3, 5, 7]
a[3, 1] # => [7]
a[-3, 2] # => [5, 7]
인덱스에서 범위를 사용할 수도 있는데, 시작 위치와 끝 위치를 점 두 개 또는 세 개로 구분해서 적어주면 된다.
점 두 개를 사용하는 형식은 마지막 경곗값을 포함하고, 세 개를 사용하는 형식에는 포함하지 않는다.
a = [ 1, 3, 5, 7, 9 ]
a[1..3] # => [3, 5, 7]
a[1…3] # => [3, 5]
a[3..3] # => [7]
a[-3..-1] # => [5, 7, 9]
[ ]연산자에 대응하는 [ ]= 연산자도 있다. 이 연산자를 이용해 배열의 특정 위치에 값을 대입할 수 잇다.
인덱스 사이에 간격이 생기면 이 사이의 값은 nil 로 채워진다.
a = [ 1, 3, 5, 7, 9] #=> [1, 3, 5, 7, 9]
a[1] = ‘bat’ #=> [1, “bat”, 5, 7, 9]
a[-3] = ‘cat’ #=> [1, “bat”, “cat”, 7, 9]
a[3] = [9, 8] #=> [1, “bat”, “cat”, [9, 8] , 9]
a[6] = 99 #=> [1, “bat”, “cat”, [9, 8] , 9, nil, 99]
[ ]= 연산자에 쓰인 인덱스가 두 개(시작 위치와 길이)거나 범위라면, 원래 배열의 해당하는 위치에 있는 원소들이 대입문의 오른쪽 편에 있는 값으로 바뀐다. 길이가 0이라면 오른편의 값이 시작 위치 바로 앞에 삽입될 것이다.
a = [ 1, 3, 5, 7, 9] #=> [1, 3, 5, 7, 9]
a[2, 2] = ‘cat’ #=> [1, 3, “cat”, 9]
a[2, 0] = ‘dog’ #=> [1, 3, “dog”, “cat”, 9]
a[1, 1] = [ 9, 8, 7] #=> [1, 9, 8, 7, “dog”, “cat”, 9]
a[0..3] = [ ] #=> ["dog", "cat", 9]
a[5..6] = 99, 98 #=> ["dog", "cat", 9, nil, nil, 99, 98]
push와 pop을 사용하면 배열의 맨 뒤에 요소를 추가하거나 맨 뒤의 요소를 제거하여 배열을 스택으로 사용할 수 있다.
stack = []
stack.push "red"
stack.push "green"
stack.push "blue"
stack # => ["red", "green", "blue"]
stack.pop # => "blue"
stack.pop # => "green"
stack.pop # => "red"
stack = []
비슷하게 unshift와 shift를 사용하면 배열 맨 앞의 요소를 추가하거나 삭제할 수 있다. shift와 push를 조합하면 배열을 선입선출(FIFO) 큐로 사용할 수 있다.
queue = []
queue.push "red"
queue.push "green"
queue.shift # => "red"
queue.shift # => "green"
first와 last 메서드는 배열의 맨 앞이나 맨 뒤에서 n개의 요소를 반환한다. 이때 앞선 메서드들과 달리 배열의 요소를 삭제하지는 않는다.
array = [ 1, 2, 3, 4, 5, 6, 7 ]
array.first(4) # => [1, 2, 3, 4]
array.last(4) # => [4, 5, 6, 7]
4.2. 해시
해시(연관, 맵, 사전이라고도 불린다)는 객체 참조가 색인된 컬렉션이라는 점에서 배열과 비슷하다. 하지만 배열은 정수를 인덱스로 사용하는 반면에, 해시의 인덱스로는 심벌, 문자열, 정규표현식, 심지어 어떤 객체라도 사용할 수 있다. 해시에 하나의 값을 저장할 때 두 개의 객체가 필요하다. 하나는 인덱스로 흔히 키라고 불리는 것이고, 나머지 하나는 이 키에 대응하는 값 객체다.
해시 리터럴은 중괄호 사이에 키와 값을 쌍으로 입력해 사용할 수 있다.
h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
h.length # => 3
h['dog'] # => "canine"
h['cow'] = 'bovine'
h[12] = 'dodecine'
h['cat'] = 99
h # => {"dog"=>"canine", "cat"=>99, "donkey"=>"asinine", "cow"=>"bovine",
# => .. 12=>"dodecine"}
루비 1.9부터는 키가 심벌인 경우에 한해서 축약 표현을 사용할 수 있다.
h = { :dog => 'canine', :cat => 'feline', :donkey => 'asinine' }
위와 같이 키가 심벌인 경우 심벌 앞의 콜론(:)을 지우고 => 를 콜론(:) 으로 대체해 다음과 같이 사용할 수 있다.
h = { dog: 'canine', cat : 'feline', donkey : 'asinine' }
배열과 비교해서 해시의 가장 큰 장점은 인덱스로 어떤 객체라도 사용할 수 있다는 점이다. 또한 루비의 해시는 요소를 추가한 순서를 기억한다...!!! 따라서 해시를 반복할 때 루비는 요소를 추가한 순서대로 반복한다.
해시와 배열을 사용한 단어 출현 빈도 계산
특정 텍스트에서 단어의 출현 빈도를 세는 프로그램을 작성해보자.
1) 문자열을 단어 리스트로 분할
2) 단어를 인덱스로 사용하고 대응하는 출현 빈도를 기록.
문자열을 단어로 나누는 메서드
def words_from_string(string)
string.downcase.scan(/[\w']+/)
end
downcase - 문자열을 소문자로 변환
scan - 주어진 패턴에 매치되는 부분 문자열을 배열로 반환
/[\w']+/ 패턴은 단어 문자와 작은따옴표를 포함하는 문자열에 매치된다.
p words_from_string("But I didn't inhale, he said (emphtically)"
실행결과
["but", "i", "didnt't", "inhale", "he", "said", "emphatically" ]