Подтвердить что ты не робот

Как мне выполнить "перерыв" или "продолжить", когда в функциональном цикле внутри Котлина?

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

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

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

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

4b9b3361

Ответ 1

Вы можете использовать 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
    }
}

Если вы хотите сделать 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 для Возвраты и переходы для всех особых случаев, в том числе для анонимных функций.


Вот unit test, подтверждающий, что все это работает:

@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)
}

Ответ 2

forEach с разрывом может быть определенно заменен на any функция:

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

Ответ 3

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