web-dev-qa-db-fra.com

Comment sortir d'un bloc Ruby?

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 une relance, mais j'essaie de le rendre générique, donc je ne veux rien mettre de particulier dans Foo.

405
user169930

Utilisez le mot-clé next. Si vous ne voulez pas continuer avec l'élément suivant, utilisez break.

Lorsque next est utilisé dans un bloc, le bloc se ferme immédiatement, rendant ainsi le contrôle à la méthode itérateur, qui peut alors commencer une nouvelle itération en appelant à 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 du bloc, de l'itérateur qui a appelé le bloc et à la première expression qui suit 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

Et enfin, l'utilisation de return dans un bloc:

return provoque toujours le renvoi de la méthode englobante, quelle que soit sa profondeur d'imbrication dans les blocs (sauf dans le cas de 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
719
JRL

Je voulais juste être capable de sortir d'un bloc - un peu comme un passage en avant, pas vraiment lié à une boucle. En fait, je veux rompre un bloc qui est dans une boucle sans terminer la boucle. Pour ce faire, j'ai fait du bloc une boucle d'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 atterrit ici en fonction de la ligne d'objet.

55
Don Law

Si vous souhaitez que votre bloc renvoie une valeur utile (par exemple, lorsque vous utilisez #map, #inject, etc.), next et break acceptent également un argument.

Considérer 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 de 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
38
Tyler Holien

utilisez le mot-clé break au lieu de return

19
AShelly

Vous pouvez peut-être utiliser les méthodes intégrées pour rechercher des éléments particuliers dans un tableau, au lieu de eaching 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
8
August Lilleaas

next et break semblent bien agir dans cet exemple simplifié!

class Bar
  def self.do_things
      Foo.some_method(1..10) do |x|
            next if x == 2
            break if x == 9
            print "#{x} "
      end
  end
end

class Foo
    def self.some_method(targets, &block)
      targets.each do |target|
        begin
          r = yield(target)
        rescue  => x
          puts "rescue #{x}"
        end
     end
   end
end

Bar.do_things

sortie: 1 3 4 5 6 7 8

2