2007년 5월 7일 월요일

4장 컨테이너,블록,반복자

  배열(Array)
Array 클래스는 객체 참조를 콜렉션(Collection)으로 저장합니다. 배열의 기본 특징은 다음과 같습니다.
  • []를 사용하여 위치를 지정합니다.
  • 위치는 정수로 지정을 합니다.
  • 인덱스는 0부터 시작합니다.
  • 정수로 지정하면 해당 위치의 객체를 반환하고, 없는 경우에는 nil을 리턴합니다.
  • 음수로 지정하는 경우에는 뒤에서부터 찾습니다.
  • 배열 인덱스를 [start, count]를 사용하여 새로운 배열을 반환할 수 있습니다.
    a = [1,2,3,4,5]
    a[1,4]  -> [2,3,4,5]
  • 또한 범위(.. ...)를 사용하여서 새로운 배열을 반환할 수 있습니다.
    a = [1,2,3,4,5]
    a[1..3]  ->  [2,3,4]   # ..는 마지막 위치를 포함합니다.
    a[1...3]  ->  [2,3]    # ...는 마지막 위치를 포함하지 않습니다.
  해시(Hash)
배열과 흡사합니다. 배열의 인덱스는 정수이나, 해시의 인덱스는 어떠한 종류의 객체도 사용할 수 있습니다. 키 => 값의 쌍을 중괄호 사이에 써서 해시를 정의합니다. 해시에는 원소에 순서가 없기 때문에, 스택이나 큐로는 쓰기 어렵습니다.
 
   h = {'dong' =>'canine', 'cat' =>'filine', 'donkey'=>'asinine'}

  SongList 컨테이너 구현하기
class SongList
   def initialize
      @songs = Array.new
   end
   def [](index)
      @songs[index]
   end
end

  블록과 반복자
class SongList
   def with_title(title)
      @songs.find{|song| title == song.name}    # title이 song(songs의 element)의 name과 같은지 비교
   end
end
블록은 메소드 호출 바로 뒤에만 나타납니다.
블록 안의 코드는 나타나자마자 실행되지 않으며, 블록이 나타날 때의 문맥을 기억하고 메소드로 들어갑니다.
def three_times
   yield "A"
   yield "B"
   yield "C"
end
three_times {|name| puts name}
또한, 위의 코드처럼 매개변수를 넘기고 이를 넘겨 받을 수도 있습니다.
블록은 {}로도 정의할 수 있으나, do...end를 사용하여 정의할 수 있습니다. 이 경우에는 {}보다 우선순위가 더 낮습니다.

inject 메소드는 컬렉션의 원소를 하나씩 반복하며 모아서 새로운 값을 만듭니다.
   [1,3,5,7].inject(0) {|sum,element| sum + element}   -> 16

inject 메소드에서 sum의 값은 0이 됩니다.(매개변수가 됩니다.) element의 경우에는 [1,3,5,7] 배열의 첫 번째인자가 됩니다. 두 번째 실행될 때는 바로 전에 실행된 블록의 반환 값이 sum이 됩니다.
매개변수가 없는 경우에는 [1,3,5,7] 배열의 첫 번째 값이 인자가 됩니다.

  트랜잭션을 위한 블록
class File
   # *args: 실제로 메소드에서 넘겨받은 매개변수를 모아서 배열로 만들고 args로 이름을 붙인다.
   def File.open_and_process(*args)
      # ["test", "r"]을 배열로 받아서 File.open으로 넘긴 다음에 블록을 실행합니다.
      f = File.open(*args)
      yield f
      f.close()
   end
end

File.open_and_process("test","r") do|file|
   while line = file.gets
      puts line
   end
end

  블록은 클로저이기도 하다.
Button을 상속받아 새로운 Button을 만드는 경우, 새로운 버튼을 만들기 위해서 많은 수의 하위클래스가 필요해지고, 실제 버튼을 눌렀을 때 동작하는 부분은 버튼이 아니라 버튼에 의해서 실행되는 코드가 책임을 가지고 있다.

songlist = SongList.new
class JukeboxButton < Button
   # &를 붙일 경우, 코드 블록이 있는지 살펴본 다음에 결합된 코드는 Proc 클래스 인스턴스로 변환되어
   # 형식인수에 할당됩니다.
   def initialize(label, &action)
      super(label)
      @action = &action
   end
   def button_pressed
      @action.call(self)
   end
end
start_button = JukeboxButton.new("Start") { songList.start }
pause_button = JukeboxButton.new("Pause") { songList.pause }

Proc객체를 만들어 얻는 것은, Proc이 정의된 시점에서의 모든 문맥 정보를 함께 갖게 된다는 점입니다. self값과 범위내의 메소드, 변수, 상수를 포함하게 됩니다.(closure)

def n_times(thing)
   return lamda {|n| thing *n}   # 블록을 Proc객체로 변환시켜 줍니다.
end
p1 = n_imes(23)
p1.call(3)

댓글 없음:

댓글 쓰기