Transformasikan while loop menjadi closure di Groovy

Oct 20 2020

Cara mengubah kode while berikut di groovy menjadi penutupan yang menarik. Apa itu mungkin ?

 List<String> list = ["s1", "s2", "s3:", "s4", "s5"]
 List<String> res = []
    int index = 0
    while (index < res.size()) {
      String cur = list[index]
      if (cur.endsWith(":")) {
        String next = list[index + 1] /*out of bounds won't happen*/
        res.add(cur + "\n" + next)
        index = index + 2
      } else {
        res.add(cur)
        index++
      }
    }
assert res == ["s1", "s2", "s3:s4", "s5"] 

Saya memikirkan solusi berikut, tetapi sepertinya itu tidak berfungsi:

 List<String> list = ["s1", "s2", "s3:", "s4", "s5"]
 List<String> res = list.eachWithIndex { int index, String cur ->
   if (cur.endsWith(":")) {
     String next = list[index + 1] /*Out of bounds won't happen*/
     index = index + 2
     return cur + "\n" + next
   }
   return cur
 } as List<String>
assert res == ["s1", "s2", "s3:s4", "s5"] 

Saya mendapatkan komentar ini dalam kode saya di atas yang " index = index + 2tidak pernah digunakan"

Jawaban

3 MichaelEaster Oct 20 2020 at 18:52

Pertimbangkan injectmetodenya (meskipun ini mengubah elemen daftar, yang tampaknya tidak pasti):

def list = ["s1", "s2", "s3:", "s4", "s5"]

def res = list.inject([], { acc, item ->
    def lastItem = acc.isEmpty() ? null : acc.last()

    if (lastItem?.endsWith(":")) {
        acc[-1] += item
    } else {
        acc << item
    }

    acc
}) 

assert res == ["s1", "s2", "s3:s4", "s5"]

Di sini, accadalah "daftar hasil yang terakumulasi" dan itemmerupakan item saat ini dalam daftar asli. Dari sini : injectmetode ini juga dikenal sebagai reduceatau folddalam bahasa lain.

2 MatiasBjarland Oct 20 2020 at 22:54

Jawaban lain yang mirip dengan Michael Easter dengan suntikan:

def list = ["s1", "s2", "s3:", "s4", "s5"]

def res = list.inject([]) { acc, item ->
  if (acc && acc[-1].endsWith(':')) 
    item = acc.removeLast() + item

  acc + item
}

assert res == ["s1", "s2", "s3:s4", "s5"]

dengan mungkin beberapa karakter lebih sedikit.

1 MichaelEaster Oct 20 2020 at 19:36

Ini solusi kedua saya. Itu tidak menggunakan closure tapi bisa dibilang "fungsional". Perhatikan bahwa ini bukan ide bagus untuk daftar besar:

def list = ["s1", "s2", "s3:", "s4", "s5"]

def str = list.join(",")
// e.g. s1,s2,s3:,s4,s5

str = str.replaceAll(/\:,/, ":")
// e.g. s1,s2,s3:s4,s5

def res = str.split(",")
assert res == ["s1", "s2", "s3:s4", "s5"]
1 MarmiteBomber Oct 20 2020 at 15:04

Tidak yakin apakah ini yang paling grooviest dari semua solusi yang ada, tetapi ini berfungsi untuk contoh Anda:

list = ["s1", "s2", "s3:", "s4", "s5:"]

res = []

list.eachWithIndex 
    {e, i -> if (i > 0 && list[i-1].endsWith(":")) {return} 
             if (e.endsWith(":") && i <  list.size-1) {res.add("${e}${list[i+1]}") } else {res.add(e)}}

yang kembali [s1, s2, s3:s4, s5:]seperti yang diharapkan

Anda memproses setiap elemen dalam daftar dan

jika elemen sebelumnya ada dan berakhiran: tidak melakukan apa - apa

jika elemen saat ini dicadangkan dan bukan yang terakhir: gabungkan elemen saat ini dengan elemen berikutnya.