Você pode romper com um encerramento "cada" do Groovy?


143

É possível a breakpartir de um Groovy .each{Closure}, ou devo usar um loop clássico?

Respostas:


194

Não, você não pode abortar um "cada" sem lançar uma exceção. Você provavelmente deseja um loop clássico se quiser que a interrupção seja interrompida sob uma condição específica.

Como alternativa, você poderia usar um fechamento "find" em vez de um each e retornar true quando você tivesse feito uma pausa.

Este exemplo será interrompido antes de processar a lista inteira:

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
}

Impressões

1
2
3
4
5

mas não imprime 6 ou 7.

Também é muito fácil escrever seus próprios métodos de iterador com comportamento de interrupção personalizado que aceita fechamentos:

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
}

Também imprime:

1
2
3
4
5    

3
Também enviei um patch para o groovy que adiciona um método findResult que faz uma descoberta em curto-circuito que retorna o primeiro resultado não nulo do fechamento. Esse método pode ser usado para cobrir quase todas as situações em que alguém pode querer sair mais cedo. Verifique o patch para ver se ele é aceito e adicionado ao groovy (espero 1.8): jira.codehaus.org/browse/GROOVY-4253
Ted Naleid

2
Eu tentei este caso: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Ele retornou 1 2 3 4 5 -1 -2. Portanto, "breaK" não funciona.
Phat H. VU

Achado correta é a opção correta, cada nunca quebrará pelo retorno
Pushkar

é findmelhor do que any- veja a outra resposta abaixo de @Michal que um funciona para mim
ruibarbo

O patch de Ted Naleid para groovy é muito útil. Use findResult, se você realmente precisa do resultado da operação find e não do próprio elemento. Ex: ​def test = [2] test.findResult{ it * 2 }​retornará 4 em vez de 2
Doug

57

Substitua cada loop por qualquer fechamento.

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

    println element

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

Resultado

1
3

Apenas um truque. O que acontece se houver uma declaração após o "intervalo". Esta declaração ainda será executada após o encontro "pausa".
Phat H. VU

2
@Phat H. VU Adicionei retorno. A declaração depois de "break" não será executada
Michal Z muda

1
Obrigado pela sua resposta. Você está certo. Também voto sua resposta. Mais: o método any é definido em DefaultGroovyMethods e é uma função de predicado que retorna true se um elemento em uma coleção satisfizer o fechamento de predicado fornecido.
Phat H. VU

Usar any()dessa maneira é um pouco enganador, mas certamente funciona e permite que você quebre ou continue .
precisa

1
como @ vegemite4me, acho que é um "truque", mas você não entende bem o significado de nenhum. Para manutenção, você não deve usar esta solução.
MatRt

11

Não, você não pode interromper um fechamento no Groovy sem lançar uma exceção. Além disso, você não deve usar exceções para o fluxo de controle.

Se você deseja interromper um fechamento, provavelmente deve primeiro pensar por que deseja fazer isso e não como fazê-lo. A primeira coisa a considerar poderia ser a substituição do fechamento em questão por uma das funções de ordem superior (conceitual) de Groovy. O exemplo a seguir:

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

torna-se

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

torna-se

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

o que também ajuda na clareza. Ele afirma a intenção do seu código muito melhor.

A desvantagem potencial nos exemplos mostrados é que a iteração para apenas no início do primeiro exemplo. Se você tiver considerações de desempenho, poderá interrompê-lo imediatamente.

No entanto, para a maioria dos casos de uso que envolvem iterações, você geralmente pode recorrer a um dos métodos de descoberta, grep, coleta, injeção, etc. do Groovy. Eles geralmente usam alguma "configuração" e, em seguida, "sabem" como fazer a iteração para você, para que você possa evitar o loop imperativo sempre que possível.


1
"Não, você não pode interromper um encerramento no Groovy sem lançar uma exceção.". É isso que Scala faz, consulte dev.bizo.com/2010/01/scala-supports-non-local-returns.html
OlliP

2

Apenas usando Fechamento especial

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

3
e se você tiver 1 bilhão de linhas e o fechamento interno retornar verdadeiro na primeira chamada, iterará mais de 1 bilhão menos um valor. :(
sbglasius

-4

(1..10) .each {

se (<5)

imprima

outro

retorna falso


2
Isso não ocorre each, simplesmente não imprime valores maiores que 4. Isso elseé supérfluo, seu código faria o mesmo sem ele. Além disso, você pode provar eachque não rompe return falsese você colocar println "not breaking"logo depois elsee pouco antes return false.
Stefan van den Akker

Faça pelo menos um esforço mínimo para formatar seu código para facilitar a leitura. Há uma visualização visual quando você responde e muitas instruções de como fazê-lo.
Mateusz foi

-11

Você poderia passar por aqui RETURN. Por exemplo

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

Funciona para mim!


6
Foi exatamente o que o OP pediu, embora obviamente não seja o que ele quis dizer.
Szocske

Posso ter uma descrição do motivo pelo qual isso tem votos negativos. Parece-me o mesmo conceito que a resposta principal diz (com menos explicações) com +133 votos.
Skepi

1
@Skepi Você não pode "quebrar" acima do fechamento. Você pode interromper o anymétodo da matriz retornando false. Você não pode quebrar o eachmétodo da mesma maneira.
Nux
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.