Ruby Lazy Enumerable flat_map не очень ленивый

Изменить: поскольку я написал вопрос с неправильным примером и не описал свои проблемы, я сделаю это снова!

Мне кажется, что #flat_map, хотя и является частью класса Enumerator::Lazy, сам по себе не очень поддающийся перечислению.

Этот пример правильно работает:

(1..Float::INFINITY).flat_map { |s| [s,s] }.take(4).to_a

Ленивая реализация также будет работать:

(1..Float::INFINITY).flat_map { |s| [s,s] }.take(4).to_a

Это будет учитывать только то, что массив, сгенерированный в блоке, конечен. Но они также будут полностью оценены перед вызовом take(4). Что не очень лениво.

Следовательно, это не удастся:

(1..Float::INFINITY).lazy.flat_map { |i| (i..Float::INFINITY).map(&:to_i) }.take(4).force

Потому что «бесконечный диапазон для массива» будет полностью оценен до того, как произойдет ленивый вызов. Я бы ожидал, что он будет «ленивым по умолчанию». Я имею в виду, я понимаю, в чем заключается загадка, но я ожидаю, что это произойдет следующим образом: flat_map оценивает каждый экземпляр лениво, знает, что результатом будет массив (или, по крайней мере, перечисляемый), и применяет ленивый механизм в теме. следовательно, (i..Float::INFINITY).map(&:to_i) будет лазифицирован (что не очень совместимо, поскольку вызов map(&:to_i) "заставит" его вычислить).


person ChuckE    schedule 09.12.2014    source источник
comment
Это может быть ошибка в реализации.   -  person tadman    schedule 09.12.2014
comment
@tadman: ОП flat_mapпингует Range, а не Enumerator::Lazy. Здесь просто нет лени, и вы не ожидаете, что она будет.   -  person Jörg W Mittag    schedule 10.12.2014
comment
Господи, только сейчас заметил отсутствующий вызов #lazy... Мне придется вернуться к моему неудачному примеру, чтобы снова увидеть, в чем была моя первоначальная проблема.   -  person ChuckE    schedule 10.12.2014
comment
Извините, у меня возникла проблема при упаковке ленивых коллекций. попробую переписать   -  person ChuckE    schedule 10.12.2014
comment
Разве первые две строки кода не одинаковы? Почему вы говорите, что один ленивый, а другой нет   -  person Wand Maker    schedule 01.12.2015


Ответы (1)


На самом деле вы не используете ленивый счетчик. Чтобы преобразовать обычный перечислитель в ленивый, используйте метод Enumerable#lazy. Чтобы вернуть результаты, я бы рекомендовал использовать метод Enumerable::Lazy#force вместо to_a, потому что я думаю, что он более четко показывает намерение.

(1..Float::INFINITY).lazy.flat_map { |s| [s,s] }.take(4).force
#=> [1, 1, 2, 2]
person Patrick Oscity    schedule 09.12.2014