Bisakah Anda keluar dari penutupan “setiap” Groovy?

143

Apakah mungkin breakdari Groovy .each{Closure}, atau haruskah saya menggunakan loop klasik?

kaya
sumber

Jawaban:

194

Tidak, Anda tidak dapat membatalkan "masing-masing" tanpa melemparkan pengecualian. Anda mungkin menginginkan loop klasik jika Anda ingin jeda untuk dibatalkan dalam kondisi tertentu.

Atau, Anda bisa menggunakan penutupan "find" alih-alih setiap dan mengembalikan true ketika Anda akan melakukan break.

Contoh ini akan dibatalkan sebelum memproses seluruh daftar:

def a = [1, 2, 3, 4, 5, 6, 7]

a.find { 
    if (it > 5) return true // break
    println it  // do the stuff that you wanted to before break
    return false // keep looping
}

Cetakan

1
2
3
4
5

tetapi tidak mencetak 6 atau 7.

Juga sangat mudah untuk menulis metode iterator Anda sendiri dengan perilaku istirahat kustom yang menerima penutupan:

List.metaClass.eachUntilGreaterThanFive = { closure ->
    for ( value in delegate ) {
        if ( value  > 5 ) break
        closure(value)
    }
}

def a = [1, 2, 3, 4, 5, 6, 7]

a.eachUntilGreaterThanFive {
    println it
}

Juga mencetak:

1
2
3
4
5    
Ted Naleid
sumber
3
Saya juga mengirimkan tambalan ke groovy yang menambahkan metode findResult yang melakukan hubung singkat yang mengembalikan hasil non-null pertama dari penutupan. Metode ini dapat digunakan untuk mencakup hampir semua situasi yang mungkin ingin dilepaskan seseorang dari awal. Periksa tambalan untuk melihat apakah itu diterima dan dimasukkan ke groovy (saya berharap dengan 1,8): jira.codehaus.org/browse/GROOVY-4253
Ted Naleid
2
Saya sudah mencoba kasus ini: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Ia mengembalikan 1 2 3 4 5 -1 -2. Jadi, "breaK" tidak berfungsi.
Phat H. VU
Temuan yang benar adalah pilihan yang benar, masing-masing tidak akan pernah kembali
Pushkar
adalah findlebih baik daripada any- lihat jawaban lain di bawah ini dari @Michal yang satu bekerja untuk saya
Rhubarb
Tambalan Ted Naleid ke groovy sangat berguna. Gunakan findResult sebagai gantinya jika Anda benar-benar membutuhkan hasil dari operasi find dan bukan elemen itu sendiri. Mis: ​def test = [2] test.findResult{ it * 2 }​akan mengembalikan 4 bukannya 2
Doug
57

Ganti setiap loop dengan penutupan apa pun .

def list = [1, 2, 3, 4, 5]
list.any { element ->
    if (element == 2)
        return // continue

    println element

    if (element == 3)
        return true // break
}

Keluaran

1
3
Michal Z muda
sumber
Hanya tipuan. Apa yang terjadi jika ada pernyataan setelah "break". Pernyataan ini akan tetap dilakukan setelah memenuhi "break".
Phat H. VU
2
@ Phat H. VU Saya telah menambahkan kembali. Pernyataan setelah "istirahat" tidak akan dieksekusi
Michal Z muda
1
Terima kasih untuk balasan Anda. Kamu benar. Saya juga memilih jawaban Anda. Lebih lanjut: metode apa pun didefinisikan dalam DefaultGroovyMethods dan ini adalah fungsi predikat yang mengembalikan true jika elemen dalam koleksi memenuhi penutupan predikat yang disediakan.
Phat H. VU
Menggunakan any()cara ini agak menyesatkan, tetapi tentu saja itu berhasil dan memberi Anda kemampuan untuk istirahat atau melanjutkan .
vegemite4me
1
seperti @ vegemite4me, saya pikir ini adalah "trik" tetapi Anda tidak mengerti dengan baik arti dari apa pun. Untuk pemeliharaan, Anda sebaiknya tidak menggunakan solusi ini.
MatRt
11

Tidak, Anda tidak dapat melepaskan diri dari penutupan di Groovy tanpa melempar pengecualian. Selain itu, Anda tidak boleh menggunakan pengecualian untuk aliran kontrol.

Jika Anda mendapati diri Anda ingin keluar dari penutupan, Anda mungkin harus terlebih dahulu memikirkan mengapa Anda ingin melakukan ini dan bukan bagaimana melakukannya. Hal pertama yang harus dipertimbangkan adalah penggantian penutupan yang dipertanyakan dengan salah satu fungsi tingkat tinggi Groovy (konseptual). Contoh berikut:

for ( i in 1..10) { if (i < 5) println i; else return}

menjadi

(1..10).each{if (it < 5) println it}

menjadi

(1..10).findAll{it < 5}.each{println it} 

yang juga membantu kejelasan. Ini menyatakan maksud dari kode Anda jauh lebih baik.

Kelemahan potensial dalam contoh yang ditunjukkan adalah bahwa iterasi hanya berhenti lebih awal pada contoh pertama. Jika Anda memiliki pertimbangan kinerja, Anda mungkin ingin menghentikannya saat itu juga.

Namun, untuk sebagian besar kasus penggunaan yang melibatkan iterasi, Anda biasanya dapat menggunakan salah satu dari metode penemuan, penjelajahan, pengumpulan, injeksi, dll. Mereka biasanya mengambil beberapa "konfigurasi" dan kemudian "tahu" bagaimana melakukan iterasi untuk Anda, sehingga Anda benar-benar dapat menghindari perulangan imperatif sedapat mungkin.

Kai Sternad
sumber
1
"Tidak, Anda tidak dapat melepaskan diri dari penutupan di Groovy tanpa melemparkan pengecualian.". Inilah yang dilakukan Scala, lihat dev.bizo.com/2010/01/scala-supports-non-local-returns.html
OlliP
2

Hanya menggunakan Penutupan khusus

// declare and implement:
def eachWithBreak = { list, Closure c ->
  boolean bBreak = false
  list.each() { it ->
     if (bBreak) return
     bBreak = c(it)
  }
}

def list = [1,2,3,4,5,6]
eachWithBreak list, { it ->
  if (it > 3) return true // break 'eachWithBreak'
  println it
  return false // next it
}
laut-kg
sumber
3
dan jika Anda memiliki 1 miliar baris dan penutupan bagian dalam benar pada panggilan pertama, maka Anda akan mengulangi lebih dari 1 miliar dikurangi satu nilai. :(
sbglasius
-4

(1..10). Masing-masing {

jika (<5)

cetak itu

lain

kembali salah

Sagar Mal Shankhala
sumber
2
Ini tidak keluar dari each, itu hanya tidak mencetak nilai lebih besar dari 4. Hal elseini berlebihan, kode Anda akan melakukan hal yang sama tanpanya. Juga, Anda dapat membuktikan eachtidak putus dengan return falsejika Anda memasukkan println "not breaking"setelah elsedan sebelum return false.
Stefan van den Akker
Harap lakukan setidaknya upaya minimum untuk memformat kode Anda agar dapat dibaca. Ada pratinjau visual ketika Anda membalas dan banyak instruksi bagaimana melakukannya
Mateusz Was
-11

Anda bisa istirahat RETURN. Sebagai contoh

  def a = [1, 2, 3, 4, 5, 6, 7]
  def ret = 0
  a.each {def n ->
    if (n > 5) {
      ret = n
      return ret
    }
  }

Ini bekerja untuk saya!

Tseveen D
sumber
6
Persis seperti yang diminta OP, meskipun jelas bukan itu yang ia maksudkan.
Szocske
Dapatkah saya memiliki penjelasan mengapa ini memiliki suara negatif? Bagi saya ini konsep yang sama dengan jawaban teratas (dengan sedikit penjelasan) dengan +133 suara.
Skepi
1
@Skepi Anda tidak dapat "memecahkan" penutupan di atas. Anda dapat merusak anymetode array dengan kembali false. Anda tidak dapat merusak eachmetode dengan cara yang sama.
Nux