`break` và` continue` trong `forEach` trong Kotlin


120

Kotlin có các hàm lặp rất hay, như forEachhoặc repeat, nhưng tôi không thể làm cho các toán tử breakcontinuehoạt động với chúng (cả cục bộ và không cục bộ):

repeat(5) {
    break
}

(1..5).forEach {
    continue@forEach
}

Mục đích là để bắt chước các vòng lặp thông thường với cú pháp hàm càng giống càng tốt. Nó chắc chắn có thể xảy ra trong một số phiên bản cũ hơn của Kotlin, nhưng tôi phải vật lộn để tái tạo cú pháp.

Vấn đề có thể là một lỗi với nhãn (M12), nhưng tôi nghĩ rằng ví dụ đầu tiên vẫn hoạt động.

Đối với tôi, dường như tôi đã đọc ở đâu đó về một thủ thuật / chú thích đặc biệt, nhưng tôi không thể tìm thấy bất kỳ tài liệu tham khảo nào về chủ đề này. Có thể trông giống như sau:

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

1
Trong Kotlin hiện tại bạn có thể thực sự bắt chước này (trong khi chờ đợi continue@labelbreak@labeltính năng), xem câu hỏi liên quan: stackoverflow.com/questions/34642868/...
Jayson Minard

1
Câu hỏi này có thể sử dụng sự làm rõ về việc bạn chỉ hỏi về sự tồn tại của breakcontinueđối với các vòng lặp chức năng, hoặc nếu bạn đang tìm kiếm các câu trả lời thay thế làm chính xác điều tương tự. Cái trước dường như đúng như vậy, bởi vì bạn đã từ chối cái sau.
Jayson Minard

có vẻ như họ đang nói thêm rằng trong Kotlin 1.3
Tigran Babajanyan

@TigranBabajanyan wow! Bạn có một liên kết?
voddan

@voddan, không, tôi chỉ cố gắng nó hoạt động
Tigran Babajanyan

Câu trả lời:


68

Chỉnh sửa :
Theo tài liệu của Kotlin , có thể sử dụng chú thích.

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

Câu trả lời gốc :
Vì bạn cung cấp a (Int) -> Unit, bạn không thể ngắt khỏi nó, vì trình biên dịch không biết rằng nó được sử dụng trong một vòng lặp.

Bạn có một số lựa chọn:

Sử dụng vòng lặp for thông thường:

for (index in 0 until times) {
    // your code here
}

Nếu vòng lặp là mã cuối cùng trong phương thức
bạn có thể sử dụng returnđể thoát khỏi phương thức (hoặc return valuenếu nó không phải là unitphương thức).

Sử dụng một phương thức
Tạo một phương thức phương thức lặp lại tùy chỉnh trả về Booleanđể tiếp tục.

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

Trên thực tế, câu hỏi của tôi là về việc làm cho cú pháp cụ thể hoạt động, không phải về việc lặp lại. Bạn không nhớ rằng nó có thể xảy ra ở một số cột mốc Kotlin?
voddan

1
Tôi không nhớ. Nhưng có lẽ là do tôi không sử dụng break & continue nhiều. Xem vấn đề này , nó nói "Ước tính - Không ước tính".
Yoav Sternberg

1
breakcontinuechỉ hoạt động trong các vòng lặp. forEach, repeatVà tất cả các phương pháp khác chỉ rằng: phương pháp và không lặp. Yoav đã trình bày một số lựa chọn thay thế nhưng breakcontinuekhông được đề cập đến để làm việc cho các phương pháp.
Kirill Rakhman

@YoavSternberg Rực rỡ! Sự yên bình của các tài liệu cũ này là những gì tôi đang tìm kiếm! Vì vậy, tính năng vẫn chưa được triển khai, để lại cho các phiên bản sau. Nếu bạn quan tâm để tạo ra một câu trả lời riêng, tôi sẽ đánh dấu nó
voddan

Trong Kotlin hiện tại bạn có thể thực sự bắt chước này (trong khi chờ đợi continue@labelbreak@labeltính năng), xem câu hỏi liên quan: stackoverflow.com/questions/34642868/...
Jayson Minard

104

Điều này sẽ in từ 1 đến 5. Hoạt return@forEachđộng giống như từ khóa continuetrong Java, có nghĩa là trong trường hợp này, nó vẫn thực hiện mọi vòng lặp nhưng bỏ qua lần lặp tiếp theo nếu giá trị lớn hơn 5.

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

Điều này sẽ in từ 1 đến 10 nhưng bỏ qua 5.

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

Hãy thử chúng tại Kotlin Playground .


Tuyệt vời, nhưng điều này vẫn không giải quyết được vấn đề không thể kết thúc sớm forEach khi một số điều kiện được đáp ứng. Nó vẫn tiếp tục thực hiện vòng lặp.
The Fox

1
@TheFox có, nó thực thi mọi vòng lặp và mọi thứ sau khi trả về bị bỏ qua khi điều kiện được đáp ứng. Mỗi hoạt động trong forEach là một hàm lambda, hiện tại không có hoạt động ngắt chính xác cho hoạt động forEach. Giải lao có sẵn trong các vòng lặp, xem: kotlinlang.org/docs/reference/returns.html
s-hunter

Đây là đoạn mã Kotlin Playground có thể chạy được với cả a continuebreakexample: pl.kotl.in/_LAvET-wX
ashughes

34

Có thể đạt được thời gian nghỉ bằng cách sử dụng:

//Will produce"12 done with nested loop"
//Using "run" and a tag will prevent the loop from running again. Using return@forEach 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 loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // non-local return from the lambda passed to run
            print(it)
        }
    }
    print(" done with nested loop")
}

Và bạn có thể tiếp tục với:

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

Như bất kỳ ai ở đây đề xuất ... hãy đọc tài liệu: P https://kotlinlang.org/docs/reference/returns.html#return-at-labels


Giải pháp tốt. Hoạt động rất tốt. Mặc dù nó có vẻ như không sử dụng cũng @loopcho kết quả mong muốn tương tự.
Paras Sidhu

Trên thực tế, bạn có thể bỏ qua thẻ rõ ràng "@loop" và sử dụng thẻ ngầm "@run". Khía cạnh quan trọng ở đây là việc trả về cục bộ cho người gọi của lambda. Lưu ý rằng bạn cần phải quấn vòng lặp bên trong một số phạm vi để bạn có thể quay trở lại cục bộ trên nó sau này.
Raymond Arteaga


17

Như tài liệu Kotlin đã nói , sử dụng returnlà cách để đi. Điều tốt về kotlin là nếu bạn có các hàm lồng nhau, bạn có thể sử dụng nhãn để viết rõ ràng kết quả của bạn đến từ đâu:

Trả về phạm vi chức năng

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

Trở lại địa phương (nó không ngừng đi qua forEach = tiếp tục)

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

Kiểm tra tài liệu, nó thực sự tốt :)


3
Cảnh báo: trở lại @ lit không dừngforEach
Jemshit Iskenderov

Đúng rồi. Nó được dự định. Giải pháp đầu tiên nó làm được, nhưng nếu bạn có hướng dẫn bên trong một vòng lặp, bạn có thể chọn nơi bạn muốn quay lại / chuyển đến. Trong trường hợp thứ hai, nếu chúng ta chỉ sử dụng return, nó sẽ dừng lại ;-)
cesards 12/1218

Gọi trở lại @ lit lượt thích Tiếp tục
pqtuan86

10

continue nhập hành vi trong forEach

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

    // your code
}

đối với breakhành vi loại bạn phải sử dụng for in untilhoặc for intheo danh sách là NullablehoặcNon-Nullable

  1. Đối với danh sách Nullable :

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
    
        // your code
    }
  2. Đối với danh sách Non-Nullable :

    for (item in list) { // data item will available right away
        if () {
            // your code
            break
        }
    
        // your code
    }

2

Câu lệnh break cho các vòng lồng nhau forEach ():

listOf("a", "b", "c").forEach find@{ i ->
    listOf("b", "d").forEach { j ->
        if (i == j) return@find
        println("i = $i, j = $j")
    }
}

Kết quả:

i = a, j = b
i = a, j = d
i = c, j = b
i = c, j = d

Tiếp tục tuyên bố với chức năng ẩn danh:

listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
    if (value == 3) return
    print("$value ")
})

Kết quả:

1 2 4 5 

0

có thể thay đổi cho

for(it in myList){
   if(condition){
     doSomething()
   }else{
     break //or continue
    }
} 

nó hoạt động cho các bản đồ băm

 for(it in myMap){
     val k = it.key
     val v = it.value

       if(condition){
         doSomething()
       }else{
         break //or continue
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.