Создание списков с использованием yield в Ruby и Python - PullRequest
3 голосов
/ 04 марта 2009

Я пытаюсь найти элегантный способ создания списка из функции, которая выдает значения как в Python, так и в Ruby.

В Python:

def foo(x):
    for i in range(x):
        if bar(i): yield i 
result = list(foo(100))

в рубине:

def foo(x)
  x.times {|i| yield i if bar(i)}
end
result = []
foo(100) {|x| result << x}

Хотя мне нравится работать на обоих языках, меня всегда немного беспокоила версия Ruby, которая должна была инициализировать список, а затем заполнить его. yield Python приводит к простой итерации, и это здорово. Ruby yield вызывает блок, что тоже замечательно, но когда я просто хочу заполнить список, это выглядит как-то неуклюже.

Есть ли более элегантный способ Ruby?

ОБНОВЛЕНИЕ Переработан пример, чтобы показать, что число значений, полученных из функции, не обязательно равно x.

Ответы [ 7 ]

10 голосов
/ 04 марта 2009

Итак, для вашего нового примера попробуйте это:

def foo(x)
  (0..x).select { |i| bar(i) }
end

По сути, если вы не пишете свой собственный итератор, вам не нужно yield очень часто в Ruby. Возможно, вам будет намного лучше, если вы перестанете пытаться писать идиомы Python с использованием синтаксиса Ruby.

7 голосов
/ 04 марта 2009

Для версии Python я бы использовал выражение генератора вроде:

(i for i in range(x) if bar(i))

Или для этого конкретного случая фильтрации значений, еще проще

itertools.ifilter(bar,range(x))
5 голосов
/ 23 сентября 2010

Точный эквивалент вашего кода Python (с использованием Ruby Generators) будет:

def foo(x)
    Enumerator.new do |yielder|
        (0..x).each { |v| yielder.yield(v) if bar(v) }
    end
end

result = Array(foo(100))

В приведенном выше списке список генерируется лениво (как в примере с Python); см:

def bar(v); v % 2 == 0; end

f = foo(100)
f.next #=> 0
f.next #=> 2
1 голос
/ 04 марта 2009

yield означает разные вещи ruby ​​и python. В ruby, вы должны указать блок обратного вызова, если я правильно помню, тогда как генераторы в python могут передаваться и уступать тому, кто их держит.

1 голос
/ 04 марта 2009

Для версии понимания списка Python, опубликованной stbuton, используйте xrange вместо диапазона, если вы хотите генератор. range создаст весь список в памяти.

1 голос
/ 04 марта 2009
def squares(x)
  (0..x).map { |i| i * i }
end

Все, что связано с диапазоном значений, лучше всего обрабатывать с использованием диапазона, а не times и генерации массива.

1 голос
/ 04 марта 2009

Я знаю, что это не совсем то, что вы искали, но более элегантный способ выразить ваш пример в ruby:

result = Array.new(100) {|x| x*x}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...