Как мне сделать ранний возврат в рубине, чтобы уменьшить вложенные if?

Я всегда использую ранний возврат, чтобы уменьшить вложенные if (программирование на других языках), например:

//instead of nested if like this 
if (condition1) {
   if (condition2) {
     if (condition3) {
       //... finally meet all conditions 
       //do the job 
     }
   }
}
//return early 
if (!condition1) {
   //clean up resource in step #1
   return
}
if (!condition2) {
   //clean up resource in step #2
   return
}
if (!condition3) {
   //clean up resource in step #2
   return
}
...
//finally meet all conditions 

Но как мне досрочно вернуть рубин? Я не могу вернуться в блок if в Ruby. Я получил ошибку

«Неперехваченное исключение: неожиданный возврат ...` блок (2 уровня) в ': неожиданный возврат (LocalJumpError) »

---- Обновить -----

Извините, я забыл упомянуть, что в такой простой ситуации, как эта, это работает

def early(input)
  if (input <0)
    puts 'should >0'
    return
  end
  puts 'good'
end

Я изучаю Fiber и использую образец из https://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/

def http_get(url)
  f = Fiber.current
  http = EventMachine::HttpRequest.new(url).get

  # resume fiber once http call is done
  http.callback { f.resume(1,http) }
  http.errback  { f.resume(2,http) }

  return Fiber.yield
end

EventMachine.run do
  Fiber.new{
    result = http_get('https://www.google.com/')
    if (result[0] ==2) 
      puts "error"
      return # using break has the error 'break from proc-closure (LocalJumpError)' too 
    end
    result = http_get('https://www.google.com/search?q=eventmachine')
    page = result[1]
    puts "Fetched page 2: #{page.response}"
  }.resume
end

Я получил ошибку.

---- обновление 2 ----

Получив ответ и комментарии, я нашел этот Как я могу вернуться что-то рано из блока?

И этот Почему оператор break в ruby ​​ведет себя по-другому при использовании Proc.new v. знак амперсанда? объясняет, почему break также не работает. Процитируем: «break должен вернуть вызывающего блока, но Proc.new уже вернулся».

return vs break vs next определенно является препятствием для рубинового новичка


person Qiulang    schedule 21.12.2018    source источник
comment
«Я не могу вернуться внутрь if блока в Ruby» - конечно, можете.   -  person Aleksei Matiushkin    schedule 21.12.2018
comment
Я получил исключение Uncaught: неожиданное возвращение сейчас   -  person Qiulang    schedule 21.12.2018
comment
Не могли бы вы показать метод, с которым у вас возникли проблемы? Досрочный возврат обычно работает, пока вы находитесь в методе.   -  person user000001    schedule 21.12.2018
comment
Кроме того, я бы сказал, что ifs уже являются запахом кода, но столько ifs - явный признак того, что код необходимо реорганизовать как можно скорее.   -  person Aleksei Matiushkin    schedule 21.12.2018
comment
@ JörgWMittag См. Мой вопрос об обновлении.   -  person Qiulang    schedule 21.12.2018
comment
@ user000001, пожалуйста, посмотрите мой вопрос об обновлении.   -  person Qiulang    schedule 21.12.2018


Ответы (1)


Используйте next вместо return, чтобы выйти из внутреннего блока раньше:

class A 
  def run 
    yield
  end
end

a = A.new
a.run do 
  puts "ola"
  next
  puts "hello"
end

Чтобы использовать return, его следует разместить непосредственно внутри тела метода.

Но когда вы передаете блок в функцию с EventMachine.run do и с Fiber.new{, код внутри блока не находится непосредственно внутри функции, а передается как параметр другой функции. Внутри блока вы не можете вызвать return, но вы можете выйти раньше с помощью next.

Я предполагаю, что причина, по которой дизайнеры решили это, заключается в том, что return внутри блока вызовет путаницу, вернется ли блок или весь метод.

person user000001    schedule 21.12.2018
comment
Эх. Чем он отличается от моего ответа? - person Aleksei Matiushkin; 21.12.2018
comment
@AlekseiMatiushkin: Если я не неправильно понял, в вашем ответе предлагается использовать tap, в моем - нет - person user000001; 21.12.2018
comment
Неважно, tap, run или foo; что важно, блоки в рубине можно прервать с помощью break. Так или иначе. - person Aleksei Matiushkin; 21.12.2018
comment
Привет, вы можете объяснить, почему break работает, а return нет, и когда я должен использовать один над другим? Сейчас я изучаю рубин, и некоторые грамматики всегда кажутся мне странными (исходящие от C). Спасибо! - person Qiulang; 21.12.2018
comment
Кстати, в моем примере перерыв тоже не сработал, я получил исключение Uncaught: перерыв от proc-closure /Users/qiulang/Dev/ruby-HW/fiber.rb:65:in block (2 levels) in <top (required)>' /Users/qiulang/Dev/ruby-HW/fiber.rb:65:in block (2 уровня) в ‹вверху (обязательно) ›': Перерыв в закрытии процедуры (LocalJumpError) - person Qiulang; 21.12.2018
comment
Перерыв @AlekseiMatiushkin тоже не сработал для моего примера. - person Qiulang; 21.12.2018
comment
@Qiulang: return возвращается из ближайшего лексически включающего тела метода. Нет тела метода, включающего ваш return. next возвращается из ближайшего лексически включающего блока. break возвращается из ближайшего лексически включающего блока и сигнализирует методу yielding блоку о прекращении итерации. Итак, если вы используете такой метод, как Enumerable#each, например, next перейдет к следующей итерации, а break полностью прервет итерацию. - person Jörg W Mittag; 21.12.2018
comment
Итак, причина, по которой я не могу использовать здесь break, заключается в том, что нет метода обертывания моего блока Fiber.new? - person Qiulang; 21.12.2018