ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Programming Ruby (7)루비 2의 게으른 열거자
    Ruby 2016. 10. 24. 23:48
    [출처] Programming Ruby 
    (본 게시물은 저작권의 문제 발생시 출판사의 요청에 의해 삭제될 수 있습니다.)



    루비2의 게으른 열거자 


    열거자를 통해 무한한 시퀀스를 생성하는 데는 문제가 하나있다. select 와 같은 탐욕적(?)이지 않은 특별한 메서드를 만들어야 한다. 다행히도 루비 2.0에는 이러한 기능이 내장되어 있다.


    어떤 루비 열거자이든 Enumerator#lazy 메서드를 호출하면 Enumerator::Lazy 객체를 반환한다. 다른 열거자와 같은 방식으로 동작하지만 무한한 시퀀스에 대해서도 정상적으로 동작하도록 select는 map과 같은 메서드들이 재정의 되어 있다. 이를 다른 방식으로 표현하자면 게으른 버전의 메서드들은 데이터가 요청되기 전까지는 어떠한 데이터도 사용하지 않는다. 그리고 데이터가 요청되었을 때만 필요한 만큼 사용한다.


    따라서 열거자의 게으른 버전 메서드들은 정상적인 동작을 위해 데이터의 배열들을 반환하지 않고, 자신의 고유한 절차를 담은 새로운 열거자를 반환한다. 

    Integer클래스에 정수 스트림을 생성하는 헬퍼 메서드를 정의하자.


    def Integer.all

         Enumerator.new do |yielder, n: 0|

              loop {yielder.yield(n += 1) }

         end.lazy

    end


    p Integer.all.first(10)


    실행결과

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]



    위 코드에서 살펴봐야 할 점은, 블록에서 키워드 매개 변수가 어떻게 사용되었는지. 즉 n이 어떻게 정의되고 초기화되었는지 보아야 한다. 두번째로는 어떻게 간단한 generator를 lazy 메서드를 통해 게으른 열거자로 만들었는지 살펴봐야 한다. 

    first 메서드를 호출하면 숫자 1부터 10을 반환하지만, 이는 게으른 특징을 가지고 있지는 않다. 앞선 예제 대신 앞에서부터 3의 배소 10개를 가져와보자


    p Integer

         .all

         .select {|i| ( i % 3).zero ? }

         .first(10)

    실행결과

    [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]



    게으른 열거자 없이 select를 사용하면 실제론 아무것도 반환하지 않는다. 생성기로부터 모든 값을 얻어오려 하기 때문이다. 하지만 게으르게 작동하는 select는 주어진 만큼의 값만을 가져오므로 앞에서부터 딱 10개의 값만 가져올수 있다.


    좀더 복잡한 예를 살펴보자. 3의 배수이면서 문자열로는 회문(palindrome) 인 수를 찾아보자.


    def palindrome?(n)

         n = n.to_s

         n == n.reverse

    end


    p Integer

         .all     

         .select { |i| (i% 3).zero?}

         .first (10)


    실행결과

    [3, 6, 9, 33, 66, 99, 111, 141, 171, 222]


    게으른 필터 메서드들은 단지 새로운 열거자 객체를 반환한다는 사실을 기억하자. 이를 통해 앞의 코드를 다음과 같이 쪼갤수 있다.


    multiple_of_three = Integer

         .all

         .select { | i | ( i% 3 ).zero? }


    p multiple_of_three.first(10)


    m3_palindrome = multiple_of_three

         .select { | i | palindrome(i) }


    p m3_palindrome.first(10)


    실행결과

    [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

    [3, 6, 9, 33, 66, 99, 111, 141, 171, 222]




    가독성이나 재사용성이 중요하다면 proc을 통해 코드를 적절하게 구성해서 사용할 수 있다.


    multple_of_three = -> n { (n % 3).zero? }

    palindrome = -> n { n = n.to_s; n == n.reverse }


    p Integer

         .all

         .select(&multiple_of_three)

         .first(10)


    실행결과

    [3, 6, 9, 33, 66, 99, 111, 141, 171, 222]




    게으른 열거자 메서드는 한 번에 복잡한 필터를 만들수 있도록 도와줄 것이다. 




Designed by Tistory.