Несколько точек возврата в scala закрытие/анонимная функция

Насколько я понимаю, в Scala нет способа иметь несколько точек возврата в анонимной функции, т.е.

someList.map((i) => {
    if (i%2 == 0) return i // the early return allows me to avoid the else clause
    doMoreStuffAndReturnSomething(i) // thing of this being a few more ifs and returns
})

вызывает error: return outside method definition. (И если он не смог бы повысить это, код не работал бы, так как Id мог бы работать.)

Одним из способов решения проблемы я мог бы быть следующий

someList.map({
    def f(i: Int):Int = {
        if (i%2 == 0) return i
        doMoreStuffAndReturnSomething(i)
    }
    f
})

однако, Id нравится знать, есть ли другой "принятый способ сделать это". Может быть, возможность пойти без имени для внутренней функции?

(Использовать случай должен был бы эмулировать некоторую оцененную конструкцию continue внутри цикла.)

Edit

Пожалуйста, поверьте мне, что необходимо избегать оператора else, потому что часть doMoreStuff может выглядеть так:

val j = someCalculation(i)
if (j == 0) return 8
val k = needForRecalculation(i)
if (k == j) return 9
finalRecalc(i)
...

который, когда у вас есть только структура if - else, становится легко перепутанной.

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

Ответ 1

Я думаю, что основная проблема с точками возврата в анонимных функциях заключается в том, что анонимная функция может spring в том месте, где обычно не ожидалось. Таким образом, было бы непонятно, к какому закрытию должен относиться оператор return. Эта проблема решена путем явного требования соответствия def - return*.

В качестве альтернативы, нужно будет охранять вокруг утверждения, из которого следует вернуться. breakable - break, но, к сожалению, can not возвращает значение с этим. Некоторые решения, основанные на продолжении, могли бы достичь этой цели, хотя Ид хотел бы ждать некоторых общих приемов и библиотек.

Ответ 2

Если ваша анонимная функция такая сложная, я бы сделал ее более явной. Анонимные функции не подходят ни к чему более сложному, чем несколько строк. Вы можете сделать его методом private, объявив его в методе использования

def myF(i:Int):Int = {
    if (i%2 == 0) return i
    doMoreStuffAndReturnSomething(i)
}
someList.map(myF(_))

Это вариант вашего обходного пути, но он чище. Они оба сохраняют это в области локального метода.

Ответ 3

В своем комментарии к коду вы написали, что хотите избежать ключевого слова else, но IMHO делает именно то, что вы хотите, и его даже два символа короче; -)

someList.map((i) => {
    if (i%2 == 0) i else
    doMoreStuffAndReturnSomething(i)
})

Ответ 4

Приведенный вами пример легко решается с помощью оператора if. Для этого не требуется никаких действий или других наказаний.

Но у вас может быть и другая ситуация, которая выглядит примерно как

if (test) {
  if (anotherTest) {
    val a = someComputation()
    if (testOf(a)) return otherComputation()
  }
  else if (yetAnotherTest) return whatever()
}
bigComputation()

Есть несколько способов справиться с такой ситуацией, если вы хотите избежать путаницы if-statement и/или дублирования кода, необходимых для преобразования этого в форму без возврата.

С Option или Either можно использовать различные подлые вещи, чтобы сохранить состояние, текущее (с orElse и fold), чтобы вы выполняли только вычисления, которые вам нужны.

Вам действительно лучше создать def, как вы предлагаете. Но просто для сравнения рассмотрим стиль обертки Option:

i => {
  ( if ((i%2)==0) Some(i) 
    else None
  ).getOrElse(doStuffAndReturn(i))
}

В приведенном выше примере этот стиль даст

( if (test) {
    if (anotherTest) {
      val a = someComputation()
      if (testOf(a)) Some(otherComputation()) else None
    }
    else if (yetAnotherTest) Some(whatever())
    else None
}).getOrElse(bigComputation())

Лично я не думаю, что это яснее (и это конечно не быстрее), но это возможно.