`break` и` continue` в `forEach` в Котлине

У Kotlin очень хорошие функции итерации, такие как forEach или repeat, но я не могу заставить операторы break и continue работать с ними (как локальные, так и нелокальные):

repeat(5) {
    break
}

(1..5).forEach {
    [email protected]
}

Цель состоит в том, чтобы имитировать обычные циклы с функциональным синтаксисом как можно ближе. Это было определенно возможно в некоторых старых версиях Kotlin, но я изо всех сил пытаюсь воспроизвести синтаксис.

Проблема может быть ошибкой с метками (M12), но я думаю, что первый пример должен работать в любом случае.

Мне кажется, что я где-то читал о специальном трюке/аннотации, но я не мог найти ссылку на эту тему. Может выглядеть так:

public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
    for (index in 0..times - 1) {
        body(index)
    }
}

Ответы

Ответ 1

Редактирование:
Согласно старым документам Kotlin - ссылка не работает, это должно быть возможно с использованием аннотаций. Однако он еще не реализован.

Разрыв и продолжение для пользовательских структур управления не реализованы еще

проблема для этой функции уже 3 года, поэтому я думаю, что она не будет исправлена.


Оригинальный ответ:
Поскольку вы предоставляете (Int) -> Unit, вы не можете от него отказаться, так как компилятор не знает, что он используется в цикле.

У вас есть несколько вариантов:

Используйте обычный цикл for:

for (index in 0..times - 1) {
    // your code here
}

Если цикл является последним кодом в методе
вы можете использовать return, чтобы выйти из метода (или return value, если это не метод unit).

Используйте метод
Создайте собственный метод метода повторения, который возвращает Boolean для продолжения.

public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
    for (index in 0..times - 1) {
        if (!body(index)) break
    }
}

Ответ 2

Это будет печатать от 1 до 5. [email protected] действует как ключевое слово continue в Java, что означает, что в этом случае он все равно выполняет каждый цикл, но пропускает следующую итерацию, если значение больше 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    nums.forEach {
       if (it > 5) [email protected]
       println(it)
    }
}

Это будет печатать от 1 до 10, но пропускает 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    nums.forEach {
       if (it == 5) [email protected]
       println(it)
    }
}

Попробуйте их на игровой площадке Котлин.

Ответ 4

Перерыв может быть достигнут с помощью:

//Will produce"12 done with nested loop"
//Using "run" and a tag will prevent the loop from running again. Using [email protected] if I>=3 may look simpler, but it will keep running the loop and checking if i>=3 for values >=3 which is a waste of time.
fun foo() {
    run [email protected]{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) [email protected] // non-local return from the lambda passed to run
            print(it)
        }
    }
    print(" done with nested loop")
}

И продолжить можно с помощью:

//Will produce: "1245 done with implicit label"
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) [email protected] // local return to the caller of the lambda, i.e. the forEach loop
        print(it)
    }
    print(" done with implicit label")
}

Как кто-нибудь здесь рекомендует... читать документы: Phttps://kotlinlang.org/docs/reference/returns.html#return-at-labels

Ответ 5

Как сказано в документации Kotlin, использование return - путь к успеху. Хорошая вещь о kotlin состоит в том, что если у вас есть вложенные функции, вы можете использовать метки для подробного написания, откуда ваш возврат:

Возврат области действия функции

fun foo() {
  listOf(1, 2, 3, 4, 5).forEach {
    if (it == 3) return // non-local return directly to the caller of foo()
    print(it)
  }
  println("this point is unreachable")
}

и Локальный возврат (он не прекращает проход через forEach = продолжение)

fun foo() {
  listOf(1, 2, 3, 4, 5).forEach [email protected]{
    if (it == 3) [email protected] // local return to the caller of the lambda, i.e. the forEach loop
    print(it)
  }
  print(" done with explicit label")
}

Оформление документации, это действительно хорошо :)

Ответ 6

Поведение типа continue в forEach

list.forEach { item -> // here forEach give you data item and you can use it 
    if () {
        // your code
        [email protected] // Same as continue
    }

    // your code
}

для поведения типа break вы должны использовать for until

for (index in 0 until list.size) {
    val item = listOfItems[index] // you can use data item now
    if () {
        // your code
        break
    }

    // your code
}