Итератор в стиле Ruby C ++ - PullRequest
       27

Итератор в стиле Ruby C ++

4 голосов
/ 21 марта 2011

Я хотел бы перебрать различные массивы ruby ​​(и, возможно, хэши).Я действительно не хочу поддерживать индекс, чтобы отслеживать положение, в котором я нахожусь для каждого массива.Это не потому, что я ленив, но я привык использовать итератор в C ++, который, на мой взгляд, менее подвержен ошибкам.

Так что есть способ получить итератор в ruby, как мы это делаем вc ++ (этот пример мало что делает, но только ради примера):

std::set< MyObject >::iterator iter1 = set1.begin();
std::set< MyObject >::iterator iter2 = set2.begin();

while(iter1 != set1.end() && iter2 != set2.end()
{
  if (iter1->timestamp > iter2->timestamp)
    ++iter2;
  else
     ++iter1;
}

Ответы [ 3 ]

8 голосов
/ 21 марта 2011

Методы Enumerable повторяются только в том случае, если вы предоставляете блок, в противном случае они возвращают итератор , аналогичный C ++. Например, в irb:

>> e = [1,2,3,4].each
=> #<Enumerator: [1, 2, 3, 4]:each>
>> e.next
=> 1

Хитрость в том, что .next во многом похож на e++ в C ++, так как возвращает текущее значение и увеличивает итератор. Существует метод .rewind, но он сбрасывает итератор в начало, а не назад только на один шаг.

Я не знаю удобного способа определения конца итератора (кроме перехвата исключения StopIteration) или определения размера итератора.

Предположительно, вы должны захватить итератор, передать его какому-либо методу, и метод выполняет iter.each { |x| something_interesting(x) } некоторого рода.

Итак, существуют итераторы, но вы не можете транслитерировать ваш C ++ прямо в Ruby. OTOH, вы не должны транслитерировать ваш C ++ на Ruby, вы должны написать Ruby на Ruby и C ++ на C ++.

4 голосов
/ 21 марта 2011

Мне не совсем понятно, что именно точно должно быть результатом вашего примера, поэтому я не могу проверить, соответствует ли это вашим спецификациям, но, похоже, это примерно то, что вы ищете:

iter1 = set1.each
iter2 = set2.each

loop do
  if iter1.peek.timestamp > iter2.peek.timestamp
    iter2.next
  else
    iter1.next
  end
end

Enumerator#peek примерно эквивалентно разыменованию итератора в C ++ (хотя он смотрит на значение next вместо current , что означает, что в моем коде может быть ошибка забора). Enumerator#next увеличивает перечислитель и возвращает следующее значение. Конец перечислителя сигнализируется с помощью исключения StopIteration, которое, однако, обрабатывается автоматически и правильно с помощью Kernel#loop.

0 голосов
/ 04 апреля 2013

Относительно утверждения Йерга:

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

тривиально протестировать в версии Ruby, содержащей Enumerator # peek (1.8.7 не отображается)

e = [10,20,30].each => #<Enumerator: [10, 20, 30]:each>
e.peek => 10

Секрет в том, что, пока вы не получите первую запись с помощью .next, итератор"перед" началом, поэтому .peek покажет вам, что элемент .next вернется.

Таким образом, нет ошибки забора столба ...

То, что я не могу найти, это способпроверить истощение, не полагаясь только на исключение.Было бы неплохо иметь .финиш?способ ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...