2009-04-20 3 views
5

Supposons que vous faites cela en Ruby:faire un objet se comporte comme un tableau d'affectation parallèle à Ruby

ar = [1, 2] 
x, y = ar 

Puis, x == 1 et y == 2. Y at-il une méthode que je peux définir dans mon propres classes qui produiront le même effet? par exemple.

rb = AllYourCode.new 
x, y = rb 

Jusqu'à présent, tout ce que je suis capable de faire avec une mission comme celui-ci est de faire x == rb et y = nul. Python a une fonction comme ceci:

>>> class Foo: 
...  def __iter__(self): 
...    return iter([1,2]) 
... 
>>> x, y = Foo() 
>>> x 
1 
>>> y 
2 
+0

parfait, merci, c'est exactement ce dont j'ai besoin à cette seconde. –

Répondre

7

Oui. Définir #to_ary. Cela permettra à votre objet d'être traité comme un tableau pour l'affectation.

irb> o = Object.new 
=> #<Object:0x3556ec> 
irb> def o.to_ary 
     [1, 2] 
    end 
=> nil 
irb> x, y = o 
=> [1,2] 
irb> x 
#=> 1 
irb> y 
#=> 2 

La différence entre #to_a et #to_ary est que #to_a est utilisé pour essayer de convertir un objet donné à un tableau, alors que #to_ary est disponible si nous pouvons traiter l'objet donné comme un tableau. C'est une différence subtile.

+0

Subtile en effet; c'est une distinction similaire entre == et eql? ce que je n'aime vraiment pas. Pourquoi est-ce une bonne idée d'avoir to_a ET to_ary ??? – allyourcode

+1

to_ary est pour les objets qui sont effectivement des tableaux. Si quelque chose qui a vraiment besoin d'utiliser un tableau peut utiliser votre objet à la place d'un tableau et que votre objet est censé être un tableau, alors il doit implémenter to_ary. to_a est pour les choses qui sont convertibles en tableaux, par ex. les objets avec (terminant) chaque méthode ont une implémentation évidente de to_a. Par exemple. un fichier pourrait avoir une méthode to_a, mais vous ne pouvez pas le traiter comme un tableau, et il ne modélise pas un tableau, donc il n'aurait pas de méthode to_ary. –

1

Vous ne pouvez pas redéfinir l'affectation, car il est un opérateur au lieu d'une méthode. Mais si votre classe AllYourCode devait hériter de Array, votre exemple fonctionnerait. Lorsque Ruby rencontre une affectation, il regarde sur le côté droit et s'il y a plus d'une valeur, il les recueille dans un tableau. Ensuite, il regarde sur le côté gauche. S'il y a une valeur l, le tableau lui est affecté.

def foo 
    return "a", "b", "c" # three rvalues 
end 

x = foo # => x == ["a", "b", "c"] 

S'il y a plus d'un lvalue (plus précisément, si elle voit une virgule), il attribue rvalues ​​successivement et les rejets ceux supplémentaires.

x, y, z = foo # => x == "a", y == "b", z == "c" 
x, y = foo # => x == "a", y == "b" 
x, = foo  # => x == "a" 

Vous pouvez effectuer une affectation parallèle si un tableau est retourné, comme vous l'avez découvert.

def bar 
    ["a", "b", "c"] 
end 

x = bar  # => x == ["a", "b", "c"] 
x, y, z = bar # => x == "a", y == "b", z == "c" 
x, y = bar # => x == "a", y == "b" 
x, = bar  # => x == "a" 

Donc, dans votre exemple, si rb est un tableau ou hérite de Array, x et y seront affectés ses 2 premières valeurs.

+0

Je pense que vous vouliez mettre des parenthèses autour de "a", "b", "c" dans votre premier exemple. Quand je l'ai essayé, j'ai eu une erreur de syntaxe. – allyourcode

+0

En fait j'ai oublié le "retour". Fixé maintenant –

2

Presque:

class AllYourCode 
    def to_a 
    [1,2] 
    end 
end 

rb = AllYourCode.new 
x, y = *rb 
p x 
p y 

Splat va essayer d'invoquer to_ary, et puis essayer d'invoquer to_a. Je ne suis pas sûr pourquoi vous voulez faire cela cependant, c'est vraiment une fonctionnalité syntaxique qui arrive à utiliser Array dans son implémentation, plutôt qu'une fonctionnalité de Array.

En d'autres termes, les cas d'utilisation pour affectations multiples sont des choses comme:

# swap 
x, y = y, x 

# multiple return values 
quot, rem = a.divmod(b) 

# etc. 
name, age = "Person", 100 

En d'autres termes, la plupart du temps l'objet attribué à partir (le Array) est même pas apparente.

+2

C'est un peu utile, mais je déteste quand je pose une question et les gens croient que je n'ai pas de bonne raison de faire ce que j'essaie de faire. Si vous pensez que c'est une mauvaise idée, expliquez pourquoi !! J'apprécie vos exemples, mais montrer des utilisations typiques n'explique pas ce qui ne va pas avec ce que j'essaie de faire:/ – allyourcode

+0

Je n'ai pas dit que vous n'aviez pas une bonne raison, je viens de dire que je ne sais pas ce que votre la raison est. Je ne peux pas exprimer une opinion sur le fait de savoir si c'est bon ou pas tant que je ne sais pas ce que c'est. Je ne peux certainement pas dire qu'il n'y a jamais de circonstances où vous voudriez faire cela, mais si vous me permettez ma curiosité, j'apprécierais. –

Questions connexes