2010-10-20 46 views
1

Je voudrais écrire une méthode qui donne des valeurs à un endroit et les passe en paramètre à une autre méthode qui l'invoquera avec un bloc. Je suis convaincu que cela peut être fait mais je n'arrive pas à trouver la bonne syntaxe.comment passer un itérateur Ruby en tant que paramètre?

Voici quelques exemples de code (non-travail) pour illustrer ce que je suis en train de réaliser:

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, dans mon code réel, Yielder et l'utilisateur sont dans différentes classes.


Mise à jour

Merci pour vos réponses. Comme Andrew Grimm l'a mentionné, je veux que la méthode de l'itérateur prenne des paramètres. Mon exemple original a laissé ce détail. Cet extrait fournit un itérateur qui compte jusqu'à un nombre donné. Pour que cela fonctionne, j'ai rendu le bloc interne explicite. Ca fait ce que je veux, mais c'est un peu moche. Si quelqu'un peut améliorer cela, je serais très intéressé de voir comment.

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

Eh bien, un échantillon de travail serait beaucoup mieux. – Reactormonk

+0

@Tass: Je pense que le code epicsmile est adéquat pour nos besoins. –

+0

Après quelques expérimentations à l'aide des réponses déjà données, j'ai trouvé un exemple de travail et je l'ai ajouté comme réponse ci-dessous. – epicsmile

Répondre

3

Cela ne veut pas utiliser un lambda ou une méthode non liée, mais il est le moyen d'aller plus simple ...

def f 
    yield 1 
    yield 2 
end 

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

g :f 
+0

J'aime l'approche minimaliste. – epicsmile

1

Lorsque vous écrivez &yielder, vous appelez yielder puis essayer de appliquez l'opérateur & (convert-to-Proc) sur le résultat. Bien sûr, appeler yielder sans un bloc est un non-go. Ce que vous voulez, c'est obtenir une référence à la méthode elle-même. Changez simplement cette ligne en user(method :yielder) et cela fonctionnera.

+0

Que feriez-vous si 'yielder' acceptait les arguments? –

+0

@ Andrew Grimm: Sur la base de cette spécification et rien d'autre, il est difficile de dire quelle serait la meilleure façon. La voie la plus évidente serait que 'user' passe les arguments appropriés au moment de l'appel, comme n'importe quel autre appel de méthode. Si 'user' utilise une méthode sans argument et que l'on veut passer une méthode qui prend des arguments, alors nous ferions mieux de savoir quels arguments doivent être passés. Dans ce cas, nous pouvons créer une fonction wrapper comme 'user (lambda {| & block | yielder (un_argument, & block)}' – Chuck

+0

@Andrew Grimm: bonne capture. Je ne veux pas que user() connaisse des arguments pour yielder() - l'idée est que c'est un itérateur opaque, je pense que je pourrais construire un proc avec une fermeture pour capturer les bonnes valeurs et puis passez ça. – epicsmile

1

Je pense que cela pourrait être le long des lignes de ce que vous voulez faire:

def yielder 
    yield 1 
    yield 2 
    yield 3 
end 

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

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

Quelques informations connexes ici: http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html