Почему у `Enumerable` есть `first`, а не `last`?

Enumerable есть first:

(3..5).to_enum.first
# => 3

но у него нет last:

(3..5).to_enum.last
# => NoMethodError: undefined method `last' for #<Enumerator: 3..5:each>

Почему это?


person sawa    schedule 20.03.2014    source источник
comment
Вам нужно было бы полностью перечислить и отменить, возможно, они хотели, чтобы накладные расходы были более очевидными (если люди задавались вопросом, почему сначала было так быстро, а теоретическое последнее, казалось, заняло некоторое время)   -  person Jason Sperske    schedule 20.03.2014
comment
У @bjhaid Enumerable тоже нет last   -  person Victor Moroz    schedule 20.03.2014
comment
@VictorMoroz вопрос относится к Kernel#to_enum, который генерирует Enumerator, а не Enumerable   -  person bjhaid    schedule 20.03.2014
comment
@bjhaid Вопрос неоднозначный, но в первой строке написано Enumerable   -  person Victor Moroz    schedule 20.03.2014
comment
@VictorMoroz Вопрос не двусмысленный. Созданный объект является Enumerator, который не имеет first, но наследует его от Enumerable.   -  person sawa    schedule 20.03.2014
comment
Сообщение об этой проблеме от сентября 2010 г..   -  person Matheus Moreira    schedule 20.03.2014


Ответы (4)


Это потому, что не все перечисляемые объекты имеют последний элемент.

Самый простой пример:

[1, 2, 3].cycle

# (an example of what cycle does)
[1,2,3].cycle.first(9) #=> [1, 2, 3, 1, 2, 3, 1, 2, 3]

Даже если элементы перечислителя конечны, нет простого способа получить последний элемент, кроме перебора его до конца, что было бы крайне неэффективно.

person BroiSatse    schedule 20.03.2014
comment
Не объясняет, почему Enumerable определяет, например. map или max, с [1].cycle тоже не работает. Почему last особенный? - person Victor Moroz; 20.03.2014
comment
@VictorMoroz - он особенный, поскольку он будет работать в большинстве случаев, но будет неэффективным, а в некоторых случаях может фактически сломать перечислитель (например, перебор объектов ввода-вывода). Похоже, создатели ruby ​​думали о добавлении этого метода, но решили, что он будет широко использоваться не по назначению. - person BroiSatse; 20.03.2014
comment
Самый простой пример на самом деле loop. - person Marc-André Lafortune; 20.03.2014
comment
Не кажется веской причиной. Бесконечные элементы нарушают многие популярные методы Enumeable, такие как #to_a, #min, #all? и #reduce. - person Matthew; 26.02.2021

Потому что не все Enumerable имеют последний элемент, и это может быть или не быть, потому что Enumerable не содержит элемента.

Рассмотрим следующее Enumerable:

a = Enumerator.new do |yielder|
  while true
    yielder << 1
  end
end

Это бесконечное Enumerable.

Enumerable — это механизм для повторения последовательности элементов. Для некоторых итерационных процессов это может выполняться только один раз. Чтобы получить последний элемент (если он действительно есть), он должен оценить весь итерационный процесс и получить последний элемент. После этого Enumerable недействителен.

person Arie Xiao    schedule 20.03.2014

Единственная причина, о которой я могу думать, это то, что Enumerables могут быть бесконечными потоками.

infinity = Float::INFINITY
range = 1..infinity

range.to_enum.first
# => 1

range.to_a.last # will never finish
person Matheus Moreira    schedule 20.03.2014
comment
Я не думаю, что это причина, потому что Float::INFINITY может появиться как начальный элемент, и в этом случае first вызовет ошибку, а не будет неопределенным: (Float::INFINITY..5).to_enum => #<Enumerator: Infinity..5:each>. Но (Float::INFINITY..5).to_enum.first # => TypeError: can't iterate from Float. - person sawa; 20.03.2014
comment
@sawa, это проблема с диапазоном, который я использовал для иллюстрации своей точки зрения, а не с концепцией бесконечных потоков. - person Matheus Moreira; 20.03.2014

Я не согласен с мнением, что не все Enumerable имеют последний элемент. Я думаю, что несколько методов Enumerator не могут завершить loop, отвечая на метод to_a. Поэтому Enumerable точно знает первый элемент, но не может определить его последний элемент.

Enumerator#each
each_enum = (0..1).each
#=> #<Enumerator: 0..1:each> 
each_enum.to_a
#=> [0, 1] 

В то время как

Enumerator#cycle
cycle_enum = (0..1).cycle
#=> #<Enumerator: 0..1:cycle> 
cycle_enum.to_a

продолжает помещать элемент next в массив, что приводит к бесконечному циклу.

См. эту ссылку(цикл) и эту ссылка(каждая) для небольшого выполнения кода, чтобы наблюдать, что я хочу сказать.

person Alok Anand    schedule 20.03.2014
comment
Я не согласен с мнением, что не все Enumerable имеют последний элемент. Итак, в этом случае, что является последним элементом cycle_enum? - person Sergio Tulentsev; 31.08.2016