2010-10-20 7 views
1

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

Вот некоторые примеры (нерабочим) код, чтобы проиллюстрировать то, что я пытаюсь достичь:

def yielder 
    yield 1 
    yield 2 
    yield 3 
end 

def user(block) 
    block.call { |x| puts x } 
end 

# later... 
user(&yielder) 

$ ruby x.rb 
x.rb:2:in `yielder': no block given (yield) (LocalJumpError) 
from x.rb:12:in `<main>' 

FWIW, на мой реальный код, yielder и пользователь в разных классах.


Update

Спасибо за ваши ответы. Как упоминал Эндрю Гримм, я хочу, чтобы метод итератора принимал параметры. Мой оригинальный пример оставил эту деталь. Этот фрагмент предоставляет итератор, который подсчитывает до заданного числа. Чтобы он работал, я сделал внутренний блок явным. Он делает то, что я хочу, но это немного уродливо. Если кто-то может улучшить это, мне было бы очень интересно посмотреть, как это сделать.

def make_iter(upto) 
    def iter(upto, block) 
    (1 .. upto).each do |v| 
     block.call(v) 
    end 
    end 
    lambda { |block| iter(upto, block) } 
end 

def user(obj) 
    obj.call Proc.new { |x| puts x } 
end 

# later... 
user(make_iter(3)) 
+0

Ну, рабочий образец будет намного лучше. – Reactormonk

+0

@Tass: Я думаю, что показанный код epicsmile является адекватным для наших целей. –

+0

После некоторых экспериментов с помощью уже полученных ответов я придумал рабочий пример и добавил его в качестве ответа ниже. – epicsmile

ответ

3

Это не использует лямбда или несвязанный метод, но это самый простой путь ...

def f 
    yield 1 
    yield 2 
end 

def g x 
    send x do |n| 
    p n 
    end 
end 

g :f 
+0

Мне нравится минималистский подход. – epicsmile

1

Когда вы пишете &yielder, вы звоните yielder, а затем пытается примените оператор & (преобразование в Proc) на результат. Конечно, вызов yielder без блока - это не-go. Вы хотите получить ссылку на сам метод. Просто измените эту строку на user(method :yielder), и она будет работать.

+0

Что бы вы сделали, если 'yielder' приняли аргументы? –

+0

@ Андрю Гримм: Основываясь на этой спецификации, и ничего больше, трудно сказать, каким будет лучший способ. Очевидный маршрут будет иметь «пользователь» передать соответствующие аргументы во время вызова, как и любой другой вызов метода. Если 'user' принимает метод без аргументов, и мы хотим передать метод, который принимает аргументы, тогда нам лучше знать, какие аргументы нужно передать. В этом случае мы можем создать функцию-обертку, такую ​​как 'user (lambda {| & block | yielder (some_argument, & block)}'. – Chuck

+0

@Andrew Grimm: хороший улов. Я не указал свои требования. Я действительно хочу, чтобы yielder был Я не хочу, чтобы пользователь() должен был знать о любых аргументах для yielder() - идея состоит в том, что это непрозрачный итератор. Я думаю, что я мог бы построить proc с закрытием, чтобы фиксировать правильные значения и затем передайте это. – epicsmile

1

Я думаю, что это может быть по линии того, что вы хотите сделать:

def yielder 
    yield 1 
    yield 2 
    yield 3 
end 

def user(meth) 
    meth.call { |x| puts x } 
end 

# later... 
user(Object.method(:yielder)) 

Некоторые связанные информация здесь: http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html

Смежные вопросы