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.