Je suis en train d'essayer de comprendre les blocs et les rendements et la façon dont ils travaillent en Ruby. Comment un rendement utilisé? De nombreuses applications rails que j'ai regardé, utilisez le rendement d'une manière bizarre. Quelqu'un peut m'expliquer ou me montrer où aller pour comprendre.
Réponses
Trop de publicités?Oui, c'est un peu déroutante au premier abord.
En Ruby, les méthodes peuvent recevoir un bloc de code pour effectuer des segments de code arbitraire.
Lorsqu'une méthode attend un bloc, il l'invoque par l'appel de la yield
fonction.
C'est très pratique, par exemple, pour itérer sur une liste ou de fournir un algorithme personnalisé.
Prenons l'exemple suivant:
Je vais définir un Person
classe initialisée avec un nom, et de fournir un do_with_name
méthode que lorsqu'il est invoqué, serait tout simplement passer l' name
d'attribut, le bloc reçu.
class Person
def initialize( name )
@name = name
end
def do_with_name
yield( @name )
end
end
Ceci nous permettra d'appeler la méthode et de passer un bloc de code arbitraire.
Par exemple, pour imprimer le nom que l'on ferait:
person = Person.new("Oscar")
#invoking the method passing a block
person.do_with_name do |name|
puts "Hey, his name is #{name}"
end
Serait d'impression:
Hey, his name is Oscar
Avis, le bloc reçoit comme paramètre une variable nommée name
(N. B. vous pouvez appeler cette variable tout ce que vous voulez, mais il est logique de l'appeler name
). Lorsque le code invoque yield
il remplit ce paramètre avec la valeur de @name
.
yield( @name )
Nous pourrions fournir un autre bloc pour effectuer une action différente. Par exemple, l'inverse de ce nom:
#variable to hold the name reversed
reversed_name = ""
#invoke the method passing a different block
person.do_with_name do |name|
reversed_name = name.reverse
end
puts reversed_name
=> "racsO"
Nous avons utilisé exactement la même méthode (do_with_name
) - c'est juste un autre bloc.
Cet exemple est trivial. Plus intéressant, les usages sont de filtrer tous les éléments dans un tableau:
days = ["monday", "tuesday", "wednesday", "thursday", "friday"]
# select those which start with 't'
days.select do | item |
item.match /^t/
end
=> ["tuesday", "thursday"]
Ou, nous pouvons également fournir une coutume algorithme de tri, par exemple en fonction de la taille de la chaîne:
days.sort do |x,y|
x.size <=> y.size
end
=> ["monday", "friday", "tuesday", "thursday", "wednesday"]
J'espère que cela vous aide à mieux le comprendre.
BTW, si le bloc est facultatif vous pouvez l'appeler comme:
yield(value) if block_given?
Si ce n'est pas une option, il suffit d'invoquer.
En Ruby, les méthodes de vérifier pour voir si ils ont été appelés d'une façon telle qu'un bloc a été fourni, en plus des arguments. Typiquement, cela se fait à l'aide de l' block_given?
de la fonction (vous pouvez également inclure un bloc dans une liste d'arguments par l'apposition d'une esperluette (&
) avant le dernier argument de nom, de cette façon, vous aurez une référence explicite au bloc comme une procédure).
Si une méthode est appelée avec un bloc de la méthode peut - yield
de contrôle pour le bloc (appelez le bloc) avec des arguments, si nécessaire. Prenons cet exemple de méthode qui montre:
def foo(x)
puts "OK: called as foo(#{x.inspect})"
yield("A gift from foo!") if block_given?
end
>> foo(10)
=> OK: called as foo(10)
>> foo(123) {|y| puts "BLOCK: #{y} How nice =)"}
=> OK: called as foo(123)
BLOCK: A gift from foo! How nice =)
Il est tout à fait possible que quelqu'un va donner une véritable réponse détaillée ici, mais j'ai toujours trouvé ce post de Robert Sosinski être une bonne explication de l'subtilités entre les blocs, les procs et les lambdas.
Je dois ajouter que je crois que le post, je suis un lien vers est spécifique à ruby 1.8. Certaines choses ont changé dans ruby 1.9, tels que le bloc des variables locales au bloc. En 1.8, vous obtiendrez quelque chose comme ce qui suit:
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Goodbye"
Alors que de 1,9 vous donnerait:
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Hello"
Je n'ai pas de 1.9 sur cette machine, alors le ci-dessus pourrait avoir une erreur.
J'ai trouvé cet article très utile. En particulier, l'exemple suivant:
#!/usr/bin/ruby
def test
yield 5
puts "You are in the method test"
yield 100
end
test {|i| puts "You are in the block #{i}"}
test do |i|
puts "You are in the block #{i}"
end
ce qui devrait donner le résultat suivant:
You are in the block 5
You are in the method test
You are in the block 100
You are in the block 5
You are in the method test
You are in the block 100
Donc, essentiellement, à chaque fois qu'un appel est fait à yield
ruby va exécuter le code dans l' do
bloc ou à l'intérieur d' {}
. Si un paramètre est fourni à l' yield
, puis ce sera fournie en paramètre à l' do
bloc.
Pour moi, c'était la première fois que j'ai compris vraiment ce que l' do
des blocs ont été faites. C'est essentiellement un moyen pour la fonction de donner accès à des structures de données internes que pour l'itération ou pour la configuration de la fonction.
Ainsi, lorsque dans les rails de vous écrire ce qui suit:
respond_to do |format|
format.html { render template: "my/view", layout: 'my_layout' }
end
Cela va lancer l' respond_to
fonction qui renvoie l' do
bloc avec le (internes) format
paramètre. Vous appelez ensuite l' .html
fonction de cette variable interne qui à son tour génère le bloc de code à exécuter l' render
commande. Notez que .html
ne le rendement si c'est le format de fichier requis. (technicité: ces fonctions utilisent réellement block.call
pas yield
comme vous pouvez le voir à partir de la source , mais la fonctionnalité est essentiellement le même, voir à cette question pour une discussion.) Cela fournit un moyen pour la fonction à exercer certains d'initialisation puis de prendre une entrée dans le code appelant, puis poursuivre le traitement si nécessaire.
Ou mettre une autre façon, il est semblable à une fonction prenant une fonction anonyme comme un argument et en appelant ensuite en javascript.
Je voulais sorte d'ajouter pourquoi vous voulez faire les choses de cette façon, à la déjà de grandes réponses.
Aucune idée de ce que la langue que vous veniez, mais en supposant que c'est un langage statique, ce genre de chose doit vous paraître familier. C'est une façon de lire un fichier en java
public class FileInput {
public static void main(String[] args) {
File file = new File("C:\\MyFile.txt");
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;
try {
fis = new FileInputStream(file);
// Here BufferedInputStream is added for fast reading.
bis = new BufferedInputStream(fis);
dis = new DataInputStream(bis);
// dis.available() returns 0 if the file does not have more lines.
while (dis.available() != 0) {
// this statement reads the line from the file and print it to
// the console.
System.out.println(dis.readLine());
}
// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
En ignorant l'ensemble du flux de chaînage chose, l'idée est La suivante
- Initialiser ressource qui a besoin d'être nettoyé
- l'utilisation des ressources
- assurez-vous de nettoyer les
C'est la façon dont vous le faites en ruby
File.open("readfile.rb", "r") do |infile|
while (line = infile.gets)
puts "#{counter}: #{line}"
counter = counter + 1
end
end
Très différent. La rupture de ce une
- dites à la classe File comment initialiser les ressources
- dites-le fichier de la classe quoi faire avec elle
- rire à la java des mecs qui sont en train de taper ;-)
Ici, au lieu de la manipulation de l'étape un et deux, vous avez délégué que dans une autre classe. Comme vous pouvez le voir, qui a considérablement fait chuter la quantité de code à écrire, ce qui rend les choses plus faciles à lire, et réduit les chances des choses comme des fuites de mémoire, ou de verrous de fichier n'obtenant pas effacé.
Maintenant, ce n'est pas comme vous ne pouvez pas faire quelque chose de similaire à java, en fait, les gens ont fait pendant des décennies maintenant. Cela s'appelle la Stratégie de modèle. La différence est que, sans blocs, pour quelque chose de simple comme le fichier d'exemple, la stratégie devient exagéré en raison de la quantité de classes et de méthodes que vous devez écrire. Avec des blocs, c'est une façon simple et élégante de le faire, qu'il ne ferait aucun sens de ne PAS la structure de votre code de cette façon.
Ce n'est pas la seule façon, les blocs sont utilisés, mais d'autres (comme le Générateur de modèle, que vous pouvez voir dans le form_for de l'api dans les rails) sont suffisamment similaires pour que cela devrait être évident ce qui se passe une fois que vous envelopper votre tête autour de ce. Quand vous voyez des blocs, il est généralement plus sûr de supposer que l'appel de méthode est ce que vous voulez faire, et le bloc est de décrire la façon dont vous voulez le faire.