Несколько точек возврата в закрытии / анонимной функции - PullRequest
11 голосов
/ 24 мая 2010

Насколько я понимаю, в 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.(И если бы это не было поднято, код не работал бы так, как я хотел бы, чтобы он работал.)

Один из обходных путей, о котором я мог бы подумать, был бы следующий

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

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

(Вариант использования будет состоять в том, чтобы эмулировать какую-то ценную continue конструкцию внутри цикла.)

Редактировать

Пожалуйста, поверьте мне, что необходимо избегать выражения 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.Извините, я думал, что это было ясно.

Ответы [ 4 ]

5 голосов
/ 24 мая 2010

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

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

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

3 голосов
/ 24 мая 2010

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

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

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

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

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

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

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())

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

3 голосов
/ 24 мая 2010

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

someList.map((i) => {
    if (i%2 == 0) i else
    doMoreStuffAndReturnSomething(i)
})
1 голос
/ 13 августа 2010

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

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

...