2016-01-06 3 views
23

В Котлине я не могу сделать break или continue в рамках цикла функций и моей лямбда-подобной я могу от обычного цикла for. Например, это не работает:Как сделать «перерыв» или «продолжить», когда в функциональном цикле внутри Котлина?

(1..5).forEach { 
    [email protected] // not allowed, nor [email protected] 
} 

Есть old documentation, что говорит об этом быть доступным, но оказывается, это не было реализовано. Каков наилучший способ получить такое же поведение, когда я хочу continue или break изнутри лямбда?

Примечание:этот вопрос намеренно написан и ответил автором (Self-Answered Questions), так что идиоматические ответы на часто задаваемые вопросы Котлин присутствуют в SO. Также прояснить некоторые действительно старые ответы, написанные для альфов Котлина, которые не точны для сегодняшнего дня Котлин.

ответ

40

Вы можете использовать return from lambda expression, который имитирует continue или break в зависимости от вашего использования.

Вот пример имитируя continue:

(1..5).forEach { 
    if (it == 3) [email protected] // mimic [email protected] 
    // ... do something more 
} 

И вы можете пойти более сложным и использовать метки, когда вы имеющие гнездования или запутанные ситуации:

(1..3).forEach [email protected] { x -> 
    (1..3).forEach [email protected] { y -> 
     if (x == 2 && y == 2) [email protected] // mimic [email protected] 
     if (x == 1 && y == 1) [email protected] // mimic [email protected] 
     // ... do something more 
    } 
} 

Если вы хотите сделать a break вам нужно что-то вне цикла, из которого вы можете вернуться, здесь мы будем использовать функцию run(), чтобы помочь нам:

run [email protected] { 
    (1..20).forEach { x -> 
     if (x == 5) [email protected] // mimic [email protected] 
     // ... do something more 
    } 
} 

Вместо run() это может быть let() или apply() или что-нибудь, естественно, вы окружающие forEach, что это место, вы хотите отдохнуть от. Но вы также пропустите код в том же блоке после forEach, поэтому будьте осторожны.

Это встроенные функции, поэтому они действительно не добавляют накладных расходов.

Прочтите справочные документы Kotlin для Returns and Jumps для всех особых случаев, в том числе для анонимных функций.


Вот тестовый модуль доказывающие это все работает:

@Test fun testSo32540947() { 
    val results = arrayListOf<Pair<Int,Int>>() 
    (1..3).forEach [email protected] { x -> 
     (1..3).forEach [email protected] { y -> 
      if (x == 2 && y == 2) [email protected] // continue @outer 
      if (x == 1 && y == 1) [email protected] // continue @inner 
      results.add(Pair(x,y)) 
     } 
    } 

    assertEquals(listOf(Pair(1,2), Pair(1,3), Pair(2,1), Pair(3,1), Pair(3,2), Pair(3,3)), results) 

    val results2 = arrayListOf<Int>() 
    run [email protected] { 
     (1..20).forEach { x -> 
      if (x == 5) [email protected] 
      results2.add(x) 
     } 
    } 

    assertEquals(listOf(1,2,3,4), results2) 
} 
+0

не красиво, это не другое решение? – store88

1

forEach с разрывом может быть специфически замещенным anyfunction:

(1..20).any { x -> 
    (x == 5).apply { // break on true 
     if (!this) { 
      results2.add(x) 
     } 
    } 
} 

Или, возможно, еще короче:

(1..20).any { x -> 
    results2.add(x) 
    x == 4 // break on true 
} 
+1

Единственная проблема заключается в том, что она не задокументирована как остановка при первом вызове лямбда, которая возвращает true, и менее очевидна. Если вы не ввели комментарии в код, сколько людей пропустит логику? –

0

takeWhile Функция stdlib может использоваться вместо разрыва.

Например,

val array = arrayOf(2, 8, 4, 5, 13, 12, 16) 
array.takeWhile { it % 2 == 0 }.forEach { println(it) } // break on odd 
array.takeWhile { it % 3 != 0 }.forEach { println(it) } // break on 3 * n 
+0

Пример использования улучшит ваш ответ. –

+1

Остерегайтесь того, что это скопирует все удовлетворяющие элементы в недавно выделенный промежуточный сбор. – Vadzim

Смежные вопросы