2013-04-10 2 views
2

발굴이 여기에 멋진 Enumerator (게으른 순서)는 포함되어 있습니까 : 우리는 순서의 전면을 잡아 수있는 방법에이유를 열거 (가장 큰이 <code>Float</code> 루비 나타낼 수) 1 ~ Enumerable에서

1.9.3-p327 :014 > e = (1..Float::INFINITY).each 

봐 :

1.9.3-p327 :015 > e.first 
=> 1 
1.9.3-p327 :016 > e.take(2) 
=> [1, 2] 

그게 좋은 물건입니까? 나도 그렇게 생각해. 하지만 다음 :

1.9.3-p327 :017 > e.drop(2).first 

랄라 랜드로 이동합니다. 그리고 그것이 5 초 이내에 돌아 오지 않는다는 것을 의미합니다.

1.9.3-p327 :020 > p e.method(:drop) 
#<Method: Enumerator(Enumerable)#drop> 

열거 (e)가 Enumerable (모듈)로부터 #drop 방법을 가지고 있음을 표시 Enumerator (클래스)에 혼합 : 여기

오 단서입니다. 이제 세계에서 루비가 가서 EnumerableEnumerator에 넣으면 어떨까요? 나도 몰라. 그러나 그곳에는 Enumerator in Ruby 1.9.3Enumerator in Ruby 2.0에 문서화되어 있습니다.

내가보기에 문제는 Enumerable에 정의 된 일부 메소드가 Enumerator에서 작동한다는 것입니다. 예 : #first#take입니다. 다른 하나 이상 : #drop이 작동하지 않습니다.

Enumerable을 포함한 Enumerator은 버그입니다. 어떻게 생각해? 항상 게으른로 Enumerable 방법의 무리를 정의 루비 2.0 Enumerator::Lazy (Enumerator의 서브 클래스)를 정의

PS 사항. 뭔가 생선 냄새가납니다. 비유 축계와 경우에 따라서는 (Enumerator)의 하위 클래스 ()에서 지연 옵션을 제공하기 위해 혼합해야하는 이유는 무엇입니까? 첫 번째 부분에 대응

1.9.3-p327 :018 > p e.method(:first) 
#<Method: Enumerator(Enumerable)#first> 
1.9.3-p327 :020 > p e.method(:drop) 
#<Method: Enumerator(Enumerable)#drop> 
+0

'e = (1.Float :: INFINITY) .each'의'each'는 아무런 차이가 없습니다. 정확히 무엇을 원 하느냐에 따라, 그것을 버려두거나'게으른'로 대체해야합니다. –

+0

Marc-André에 감사드립니다. 그러나 Ruby 1.9.3에는 Enumerable # lazy가 없습니다. 2.0에서만 사용 가능합니다. 내 주요 오해 중 하나는 #drop이 열거자를 전혀 반환하지 않는다고 가정하는 것입니다. 웬일인지 #drop과 #take는 사소한 열거 자 구현이 가능하지만 둘 다 열거자를 반환하지는 않습니다! [Ruby Bug # 7715 "게으른 열거 자들이 게으른 체재하고 싶다"] (https://bugs.ruby-lang.org/issues/7715)에 대한 수정조차도이를 수정하지 못합니다. 생각 해보니, 배열을 반환하는 사람들에게 의존하는 코드를 깨면 "고정"될 수 없습니다! –

+1

사실,'drop'은 "열심히"입니다. BTW,'require 'backports/2.0.0/enumerable/lazy''with와 함께 Ruby의 모든 버전에서'lazy'로 게임 할 수 있습니다. –

답변

1

:

도 참조하십시오.

는 "라라의 땅으로갑니다 그리고으로 나는 덜 5 초에 반환하지 않는 것을 의미한다. " 행동은 그 방법을 어떻게해야하는 것과 일치 보인다

: 당신은 그냥 돌아 N까지 반복 할 필요가 의미

take(n) → array # Returns first n elements from enum. 

.

drop(n) → array # #Drops first n elements from enum, and returns rest elements in an array. 

즉, 나머지 요소를 반환 할 수 있어야합니다. 그리고 당신은 상한이 Float::INFINITY이기 때문에 그것처럼 행동합니다.

출처 : Enumerable뿐만 아니라 다른 많은 컬렉션 프레임 워크에 공통되는 설계 선택의

+0

감사합니다. fmendez. 네, #take와 #drop가 열거자를 반환한다고 완전히 가정했습니다. 이제 그게 왜 나쁜지 이해합니다. 많은 코드는 배열을 반환하는 코드에 의존합니다. –

+0

블록을 사용하지 않는 오래된 (1.9 이전) Enumerable 메서드는 호출자가 Enumerator를 다시 원한다는 신호를 보낼 수있는 방법이 없었기 때문에 열거자를 반환하도록 "업그레이드"될 수 없습니다. Enumerable # take와 #drop는 블록을 사용하지 않으므로 열거자를 반환 할 수 없습니다. 물론 Enumerator :: Lazy # take 및 #drop는 열거자를 예상대로 반환합니다. –

3

.

루비의 컬렉션 작업은 유형을 보존하지 않습니다. 그들은 항상은 어떤 컬렉션 유형에 관계없이 Array을 반환합니다. 또한 유형이 항상 IEnumerable 인 경우를 제외하고는 .NET과 동일합니다. 더 유용한 것은 IEnumerable (예 : 무한 시퀀스)보다 Array으로 표시 될 수 있고 동시에 유용하지 않기 때문입니다 (IEnumerable의 인터페이스가 Array의 인터페이스보다 훨씬 작기 때문에 더 적은 작업을 수행 할 수 있습니다).

이렇게하면 Ruby의 모음 작업을 번으로 중복없이 구현할 수 있습니다. 단지 each, 믹스 인 Enumerable를 구현하고 작업이 완료 :

그것은 또한 루비의 컬렉션 프레임 워크에 자신의 컬렉션을 통합하는 것은 매우 쉬운 것을 의미한다. Ruby의 이후 버전에 새로운 컬렉션 메소드가 추가 된 경우 (예 :, Ruby 1.9) 아무 것도하지 않고 일뿐입니다. 컬렉션과도 호환됩니다.

다른 디자인 선택은 모든 컬렉션 작업을 유형 보존으로 만드는 것입니다. 따라서 모든 콜렉션 오퍼레이션은 호출 된 타입을 리턴한다.

일부 언어가 있습니다. 그러나 모든 컬렉션 메서드를 모든 컬렉션 클래스에 붙여 넣을 복사본 (대용량 코드 중복 포함)으로 &이 구현됩니다.

이것은 컬렉션 프레임 워크에 고유 컬렉션을 추가하려는 경우 컬렉션 프로토콜의 모든 단일 메소드를 구현해야한다는 것을 의미합니다. 그리고 이후 버전의 언어에 새로운 메소드가 추가되면 컬렉션의 새 버전을 릴리스해야합니다.

Scala 2.8의 컬렉션 프레임 워크는 코드 복제없이 형식 보존 작업을 수행하는 방법을 처음으로 알아 냈습니다. 그러나 Ruby의 콜렉션 프레임 워크가 설계된 지 오래되었습니다. Ruby의 콜렉션 프레임 워크가 설계되었을 때, 코드 중복없이 타입 유지 보수 작업을 수행하는 방법은 아직 알려지지 않았으며 Ruby 디자이너는 복제를 선택하지 않았습니다.

루비 1.9부터는 실제로 중복이 있습니다. 일부 Hash 메서드는 Array 대신 Hash 개를 반환하도록 복제되었습니다. 이미 Ruby 2.0의 Enumerator::Lazy에 대해 언급 했으므로 많은 Enumerable 메서드를 복제하여 Enumerator::Lazy을 반환합니다.

Scala가 Ruby에서 사용하는 것과 동일한 트릭을 사용할 수는 있지만 기존 프레임 워크 구현을 모두 쓸모 없게 만드는 컬렉션 프레임 워크를 완전히 다시 작성해야합니다. 스칼라는 당시 사용자 기반이 거의 없었기 때문에이 작업을 수행 할 수있었습니다.

+0

평소처럼 좋은 대답입니다. 나는 "열거 자"를 반환 할 수있는 것처럼 "항상 * 배열을 반환합니다"라고 말하지 않습니다. '게으른'부분은 약간 오도하는 것입니다. lazily 행동하는 방법은 전문화되어 있고 다른 것은 그렇지 않습니다. 반면에,'Lazy # to_enum'은 전문적이며,이 좋은 속임수는'Enumerator'를 반환하는'Enumerable' 메쏘드는 전문화없이'Lazy'에서 호출 될 때'Enumerator :: Lazy'를 반환한다는 것을 의미합니다. bugs.ruby-lang.org/issues/7715의 세부 정보 –

관련 문제