2011년 6월 2일 목요일

Implementing a SongList Container

Implementing a SongList Container

SongList에 필요한 기본 메서드 목록을 정의해 본다.

append( aSong ) » list
Append the given song to the list.

deleteFirst() » aSong
Remove the first song from the list, returning that song.

deleteLast() » aSong
Remove the last song from the list, returning that song.

[ anIndex ] » aSong
Return the song identified by anIndex, which may be an integer index or a song title.

노래 리스트를 끝에 추가하고 양 끝에서 제거하는 것은 배열을 가지고 쉽게 구현할 수 있다. 또 정수로 위치를 지정하고 그 위치의 노래를 반환하는 기능도 역시 배열에서 제공한다.

SongList 클래스의 initialize 메서드를 구현해보도록 하자

class SongList
 def initialize
   @songs = Array.new
 end
end
노래를 담을 새로운 Array을 만들고 변수 @songs을 지정해 준다.

class SongList
 def append(aSong)
   @songs.push(aSong)
   self
 end
end

append 메서드는 주어진 노래를 @songs 배열 끝에 추가한다. 그리고 현재 사용중인 SongList 반환을 위해 self을 반환한다.

class SongList
 def deleteFirst
   @songs.shift
 end
 
 def deleteLast
   @songs.pop
 end
end
deleteFirst, deleteLast 메서드는 Array#shift, Array#pop으로 구현한다.

* Array#shift : 배열의 첫번째 인자를 반환하고 두번째부터 앞으로 하칸씩 옮긴다.
args = [ "-m", "-q", "filename" ]
args.shift»"-m"
args»["-q", "filename"]



*Array#pop : 배열의 마지막 인자를 반환하고 배열에서 삭제한다.
a = [ "a", "m", "z" ]
a.pop»"z"
a»["a", "m"]



class SongList
 def [](key)
   if key.kind_of?(Integer)
     @songs[key]
   else
     # ...
   end
 end
end

Index로 노래를 찾아오는 메서드를 위와 같이 @songs 배열에 위임하는 방식으로 구현한다.
구현 된 전체 SongList 클래스와 테스트 결과는 아래와 같다.

# Song 클래스
class Song
 @@totalPlays = 0
 attr_reader :name, :artist, :duration
 def initialize(name, artist, duration)
@name = name
@artist   = artist
@duration = duration
@plays = 0
 end
 
 def play
@plays += 1
@@totalPlays += 1
"This song(#@name) : #@plays plays, Total #@@totalPlays plays"
 end
 
 def to_s
"Song: #{@name}--#{@artist} (#{@duration})"
 end
 
end

#SongList 클래스
class SongList
 def initialize
@songs = Array.new
 end
 
 def append(aSong)
@songs.push(aSong)
self
 end
 
 def deleteFirst
@songs.shift
 end
 
 def deleteLast
@songs.pop
 end
 def [](key)
if key.kind_of?(Integer)
  @songs[key]
end
 end
end

#Test Code
list = SongList.new
list.
 append(Song.new('title1', 'artist1', 1)).
 append(Song.new('title2', 'artist2', 2)).
 append(Song.new('title3', 'artist3', 3)).
 append(Song.new('title4', 'artist4', 4))

puts list[0]  
puts list[2]  
puts list[9]

puts list.deleteFirst  
puts list.deleteFirst  
puts list.deleteLast
puts list.deleteLast  
puts list.deleteLast

#Console 출력 내용
Song: title1--artist1 (1)
Song: title3--artist3 (3)
nil
Song: title1--artist1 (1)
Song: title2--artist2 (2)
Song: title4--artist4 (4)
Song: title3--artist3 (3)
nil

Blocks and Iterators

위의 SongList#[] 메서드에서 Array의 Index 말고 노래의 Title로도 검색이 가능하게 바꿔 보기로 한다.

class SongList
 def [](key)
   if key.kind_of?(Integer)
     return @songs[key]
   else
     for i in 0...@songs.length
       return @songs[i] if key == @songs[i].name
     end
   end
   return nil
 end
end

for문을 이용해서 배열의 각 항목을 조회하고 주어진 key와 Song 인스턴스의 name이 같을 경우 리턴하도록 한다. 위 방법은 잘 동작하나 배열에서 원하는 Object를 찾기 위해 계속해서 반복해야 하는 문제가 있다. 루비에서는 배열에서 직접 해당 Object을 찾을수 있는 방법을 제공한다.

class SongList
 def [](key)
   if key.kind_of?(Integer)
     result = @songs[key]
   else
     result = @songs.find { |aSong| key == aSong.name }
   end
   return result
 end
end

*Enumerable#find(Enumerable#detect)
enumObj.detect {| obj | block } : enumObj에서 block 조건에 맞는 object를 찾는다. 해당되는 object가 없으면 nil를 반환한다.
(1..10).detect  {|i| i % 5 == 0 and i % 7 == 0 }»nil
(1..100).detect {|i| i % 5 == 0 and i % 7 == 0 }»35



find 메서드가 코드 블록을 반복해서 수행하는 반복자(iterator)이다. 반복자와 코드 블록은 루비가 가지는 중요 특징 중에 하나이다.

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...