436 votes

Comment sortir d'un bloc de rubis ?

Voici Bar#do_things :

class Bar   
  def do_things
    Foo.some_method(x) do |x|
      y = x.do_something
      return y_is_bad if y.bad? # how do i tell it to stop and return do_things? 
      y.do_something_else
    end
    keep_doing_more_things
  end
end

Et voici Foo#some_method :

class Foo
  def self.some_method(targets, &block)
    targets.each do |target|
      begin
        r = yield(target)
      rescue 
        failed << target
      end
    end
  end
end

J'ai pensé à utiliser la relance, mais j'essaie de la rendre générique, donc je ne veux pas mettre quelque chose de spécifique dedans. Foo .

766voto

JRL Points 36674

Utiliser le mot-clé next . Si vous ne souhaitez pas passer à l'élément suivant, utilisez la touche break .

Quand next est utilisé à l'intérieur d'un bloc, il entraîne la sortie immédiate du bloc, renvoyant le contrôle à la méthode de l'itérateur, qui peut alors commencer une nouvelle itération en invoquant à nouveau le bloc :

f.each do |line|              # Iterate over the lines in file f
  next if line[0,1] == "#"    # If this line is a comment, go to the next
  puts eval(line)
end

Lorsqu'il est utilisé dans un bloc, break transfère le contrôle hors du bloc, hors de l'itérateur qui a invoqué le bloc, et vers la première expression suivant l'invocation de l'itérateur :

f.each do |line|             # Iterate over the lines in file f
  break if line == "quit\n"  # If this break statement is executed...
  puts eval(line)
end
puts "Good bye"              # ...then control is transferred here

Enfin, l'utilisation de return dans un bloc :

return provoque toujours le retour de la méthode englobante, quelle que soit sa profondeur d'imbrication dans les blocs (sauf dans le cas des lambdas) :

def find(array, target)
  array.each_with_index do |element,index|
    return index if (element == target)  # return from find
  end
  nil  # If we didn't find the element, return nil
end

2 votes

Merci, mais next ne passe qu'à l'élément suivant du tableau. est-il possible de sortir ?

0 votes

Vous devez appeler next avec la valeur de retour. "def f ; x = yield ; puts x ; end" "f do next 3 ; puts "o" ; end" Ceci imprime 3 (mais pas de "o") sur la console.

5 votes

next , break , return , il n'y a pas de comparaison possible

62voto

Don Law Points 479

Je voulais simplement pouvoir sortir d'un bloc - un peu comme un goto en avant, pas vraiment lié à une boucle. En fait, je veux sortir d'un bloc qui est dans une boucle sans terminer la boucle. Pour ce faire, j'ai fait du bloc une boucle à une itération :

for b in 1..2 do
    puts b
    begin
        puts 'want this to run'
        break
        puts 'but not this'
    end while false
    puts 'also want this to run'
end

J'espère que cela aidera le prochain googleur qui atterrira ici en se basant sur la ligne de l'objet.

4 votes

C'est la seule réponse qui répondait à la question telle qu'elle était posée. Elle mérite plus de points. Merci.

0 votes

Cela fonctionne de la même manière pour la pause et la suite. Si la valeur false est remplacée par true, next restera dans le look et break en sortira.

40voto

Tyler Holien Points 1645

Si vous souhaitez que votre bloc renvoie une valeur utile (par exemple lors de l'utilisation de #map , #inject etc,) next y break accepter également un argument.

Considérez ce qui suit :

def contrived_example(numbers)
  numbers.inject(0) do |count, x|
    if x % 3 == 0
      count + 2
    elsif x.odd?
      count + 1
    else 
      count
    end
  end
end

L'équivalent en utilisant next :

def contrived_example(numbers)
  numbers.inject(0) do |count, x|
    next count if x.even?
    next (count + 2) if x % 3 == 0
    count + 1
  end
end

Bien sûr, vous pouvez toujours extraire la logique nécessaire dans une méthode et l'appeler à l'intérieur de votre bloc :

def contrived_example(numbers)
  numbers.inject(0) { |count, x| count + extracted_logic(x) }
end

def extracted_logic(x)
  return 0 if x.even?
  return 2 if x % 3 == 0
  1
end

1 votes

Merci pour l'indication de l'argument en faveur de break !

2 votes

Pourriez-vous mettre à jour avec un exemple spécifique en utilisant break (il suffit probablement de remplacer l'un de vos next con break ..

0 votes

Une chose très intéressante. break something œuvre, break(something) fonctionne mais true && break(somehting) entraîne une erreur de syntaxe. Pour information. Si une condition est nécessaire, alors if o unless doit être utilisé.

23voto

AShelly Points 17389

Utiliser le mot clé break au lieu de return

8voto

August Lilleaas Points 25812

Vous pouvez peut-être utiliser les méthodes intégrées pour trouver des éléments particuliers dans un tableau, au lieu de each -ing targets et tout faire à la main. Quelques exemples :

class Array
  def first_frog
    detect {|i| i =~ /frog/ }
  end

  def last_frog
    select {|i| i =~ /frog/ }.last
  end
end

p ["dog", "cat", "godzilla", "dogfrog", "woot", "catfrog"].first_frog
# => "dogfrog"
p ["hats", "coats"].first_frog
# => nil
p ["houses", "frogcars", "bottles", "superfrogs"].last_frog
# => "superfrogs"

Un exemple serait de faire quelque chose comme ceci :

class Bar
  def do_things
    Foo.some_method(x) do |i|
      # only valid `targets` here, yay.
    end
  end
end

class Foo
  def self.failed
    @failed ||= []
  end

  def self.some_method(targets, &block)
    targets.reject {|t| t.do_something.bad? }.each(&block)
  end
end

2 votes

N'ajoutez pas de méthodes arbitraires comme celle-là à la classe Array ! C'est une très mauvaise pratique.

3 votes

Rails le fait, alors pourquoi pas lui ?

2 votes

@wberry Cela ne veut pas dire qu'il est nécessairement devrait . ;) En général, il est préférable d'éviter de singer les classes de base Parcheando à moins d'avoir une très bonne raison (par exemple, ajouter quelques généralisable que beaucoup d'autres codes trouveront utiles). Même dans ce cas, il faut faire preuve de prudence, car une fois qu'une classe a été lourdement modifiée, il est facile pour les bibliothèques de commencer à se marcher dessus et de provoquer des comportements extrêmement bizarres.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X