2010-05-24 5 views
11

Насколько я понимаю, нет никакого способа, в 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 конструкцию внутри цикла.)

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

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

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

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

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

+0

В чем проблема с использованием инструкции else? – Patrick

+1

В примере, который вы приводите, нет причин вообще избегать ключевого слова 'else'; нет никакого дополнительного выражения, если вы будете использовать 'else', поэтому вы ничего не получите, используя раннее возвращение здесь. – Jesper

+0

Извините, я пересмотрел его. Подумал, что было ясно, что часть 'doMoreStuff' на самом деле * делает * немного больше. – Debilski

ответ

1

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

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

5

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

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

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

+0

Это прекрасное решение. Я scala newb, поэтому я не понимаю, почему это требуется, но он решил проблему. – ripper234

3

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

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

См. Мое редактирование. – Debilski

+0

На самом деле, я бы сказал, что он сохраняет более двух символов: явный 'return' заставляет вас объявлять тип возврата в любом случае ... Однако мой фактический прецедент был бы немного сложнее, чем пример, который я изначально дал, так что это не просто о сохранении символов. – Debilski

3

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

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

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

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

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

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

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

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

+0

Да, это как свернутый (или чуть более ровный), как исходный пример без возврата. Но, конечно, это правильный подход в других ситуациях. – Debilski