Yakut bloktan nasıl çıkılır?


420

İşte 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

Ve işte 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

Yükseltmeyi kullanmayı düşündüm, ama genel yapmaya çalışıyorum, bu yüzden belirli bir şey koymak istemiyorum Foo.

Yanıtlar:


747

Anahtar kelimeyi kullanın next. Bir sonraki öğeye devam etmek istemiyorsanız tuşunu kullanın break.

Bir nextblok içinde kullanıldığında, bloğun hemen çıkmasına neden olur ve kontrolü yineleyici yöntemine döndürür, bu da bloğu tekrar çağırarak yeni bir yineleme başlatabilir:

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

Bir blokta kullanıldığında, breakkontrolü bloktan, bloğu başlatan yineleyiciden ve yineleyicinin çağrılmasını takiben ilk ifadeye aktarır:

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

Ve son olarak, returnbir blokta kullanımı :

return bloklar içinde ne kadar derinden yuvalandığına bakılmaksızın (lambdalar hariç) her zaman çevreleme yönteminin geri dönmesine neden olur:

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
teşekkürler, ancak sonraki yalnızca dizideki bir sonraki öğeye gider. çıkmak mümkün mü?
user169930

Sonraki değeri dönüş değeri ile çağırmanız gerekir. "def f; x = verim; x koyar; son" "f sonraki 3 yapar;" o "koyar; son" Bu konsolda 3 yazdırır (ancak "o" yazmaz).
Marcel Jackwerth

5
next, break, return, Karşılaştırmak olamaz
Finiteloop

@MarcelJackwerth kullanımı hakkında nextveya breakbir argümanla eklenen yorumu genişleten bir yanıt ekledim .
Tyler Holien

8
Ayrıca redo, temel olarak yürütmeyi geçerli yineleme içinde bloğun üstüne geri taşıyan bir anahtar kelime de vardır .
Ajedi32

59

Sadece bir bloktan kurtulmak istedim - bir döngü gibi değil, ileri bir git gibi. Aslında, döngü sona ermeden bir döngü içinde bir blok kırmak istiyorum. Bunu yapmak için, bloğu bir yineleme döngüsü yaptım:

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

Umarım bu konu satırına göre buraya bir sonraki googler yardımcı olur.


4
Soruyu ortaya konduğu anda cevaplayan tek yanıt buydu. Daha fazla puan hak ediyor. Teşekkürler.
Alex Nye

Bu hem mola hem de sonraki için aynı şekilde çalışır. Yanlış doğru olarak değiştirilirse, bir sonraki görünümde kalacaktır ve kopma kopacaktır.
G. Allen Morris III

39

Eğer blok yararlı bir değer (örneğin kullanırken dönmek istiyorsanız #map, #injectvs.), nextve breakaynı zamanda bir argüman kabul edin.

Aşağıdakileri göz önünde bulundur:

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

Eşdeğer kullanımı 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

Elbette, gereken mantığı her zaman bir yönteme çıkarabilir ve bloğunuzun içinden çağırabilirsiniz:

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
Argümanı ile ipucu için teşekkürler break!
rkallensee

2
Belirli bir örneği kullanarak w / güncellemeyi memnun olabilir breakmuhtemelen sadece birini yerine ( nextile break..
Mike Graf

Çok ilginç bir şey. break somethingçalışır, break(something)çalışır ancak true && break(somehting)sözdizimi hatası verir. Sadece FYI. Koşul gerekliyse ifveya unlesskullanılması gerekir.
akostadinov


8

Belki de her şeyi elle yapmak ve yapmak yerine bir eachArray'da belirli öğeleri bulmak için yerleşik yöntemleri kullanabilirsiniz targets. Birkaç örnek:

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"

Örnek olarak şöyle bir şey yapılabilir:

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
Array sınıfına böyle rastgele yöntemler eklemeyin! Bu gerçekten kötü bir uygulama.
mrbrdo

3
Raylar yapıyor, neden olmasın?
wberry

2
@wberry Mutlaka olması gerektiği anlamına gelmez . ;) Yine de, genel olarak, oldukça iyi bir nedeniniz yoksa maymun yama çekirdek sınıflarından kaçınmak en iyisidir (yani , diğer kodların çoğunun faydalı bulacağı bazı kullanışlı genelleştirilebilir işlevler eklemek ). O zaman bile, hafifçe adım atın çünkü bir sınıf yoğun bir şekilde maymun yamalı olduğunda, kütüphanelerin birbirleri üzerinde yürümeye ve son derece garip davranışlara neden olması kolaydır.
blm768

2

nextve breakbu basitleştirilmiş örnekte doğru şeyi yapıyor gibi görünüyor!

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

çıktı: 1 3 4 5 6 7 8


2
break hemen biter - sonraki sonraki yinelemeye devam eder.
Ben Aubin

-3

Bir yakut bloktan çıkmak için sadece returnanahtar kelime kullanınreturn if value.nil?


2
İşlevden çıkmıyor mu return?
ragerdl
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.