Groovy Multiple Assignment

Salve Devs!

Sei que não tenho conseguido manter uma frequência nas postagens do SantoGrails, ainda tenho tido um pouco de dificuldade no gerenciamento do meu tempo, mas eu não poderia deixar de escrever algo sobre o que eu acabei de descobrir que o Groovy pode fazer… Estou falando do Multiple Assignment.

droctocat

Definição:

Multiple Assignment é um recurso oferecido por algumas linguagens, que tem o interesse de facilitar a atribuição de vários valores ao mesmo tempo.

Problematizemos então!!

Caso de Uso:
Imagine que você possue um método que retorne uma linha de um arquivo delimitado por “#”, onde por definição o nome é o primeiro valor, o ano de nascimento é o segundo valor e sua cidade de nascimento é o terceiro valor e onde o objetivo é carregar cada um desses valores em suas respectivas variáveis.

def atributos = "Stanley Kubrick#1928#New York".split("#")
String nome = atributos[0]
String ano = atributos[1]
String cidade = atributos[2]
println "Nome: $nome, Nascimento: $ano, Cidade: $cidade"

Se analisarmos rapidamente o código perceberemos que o nosso gargalo produtivo (se é que eu posso chamar desta forma) está na fragmentação da lista em variáveis.

Para isso foi criado o Multiple Assignment. Vejamos como ele pode nos ajudar.

def (nome, ano, cidade) = "Stanley Kubrick#1928#New York".split("#")
println "Nome: $nome, Nascimento: $ano, Cidade: $cidade"

E pronto!

Para finalizar… O Multiple Assignment do Groovy é bem poderoso, mas deixarei esta parte para o próximo post.

 

 

Anúncios
Etiquetado ,

4 pensamentos sobre “Groovy Multiple Assignment

  1. Rodolfo disse:

    Testei algumas variações pra ver o que acontece quando o `split` produz mais ou menos items que o número de variáveis declaradas.

    Quando tem mais items, parece que os itens adicionais são ignorados.

    Quanto tem menos, exceção:

    java.lang.ArrayIndexOutOfBoundsException: 4
    at Script1.run(Script1.groovy:2)

    Também testei o caso do `println` fazendo referência a variáveis que não existem:

    groovy.lang.MissingPropertyException: No such property: vivo for class: Script1
    at Script1.run(Script1.groovy:3)

    Usei esse site aqui pra rodar o código sem ter que instalar nada:
    https://groovyconsole.appspot.com/

    Jonatas, como ficaria o código robusto para quando a string tem mais ou menos itens?

    • jonatasemidio disse:

      Fala Rodolfo,

      Como o seu comentário tem várias observações e perguntas, eu tentei fragmentar a resposta. Mesmo assim já adiantando… Eu já separei alguns exemplos para escrever mais para frente sobre outras maneiras de se trabalhar com esse recurso, mas não sei se dentro destes exemplos, possa ter algum que esclareça sua dúvida. [ Gist com Exemplos ]


      //Funcionamento padrão. 2 valores para 2 variáveis
      def (a, b, c) = [1,2]
      assert [a,b] == [1,2]


      //Com mais valores do que variáveis, os valores se perdem,pois ele distribui em ordem.
      (a, b) = [1,2,3]
      assert [a,b] == [1,2]


      //Mas em caso contrário, o erro relatado não foi identificado
      (a, b, c) = [1,2]
      assert [a,b,c] == [1,2,null] //Não apresentou o ArrayIndexOutOfBoundsException, por não ter valor, atribuiu o null


      1. No primeiro caso (o mais simples), o funcionamento está como o esperado.

      2. Já no segundo caso, me parece ser uma reação normal, já que não temos mais variáveis para atribuir valores, esse valores acabam sendo deixados de lado. Não sei se essa é a melhor estratégia, mas o que eu posso ver é se existe alguma opção para que os valores restantes sejam, passados em forma de lista, concatenados ou até calculados.

      3. Por ultimo e não menos importante. No caso de termos mais variáveis do que valores, as variáveis restantes ficaram nulas, já que estas foram declaradas sem a atribuição de algum valor. No entanto, o erro relatado: java.lang.ArrayIndexOutOfBoundsException: 4 at Script1.run(Script1.groovy:2) não ocorreu. Se possível mande o script testado para eu entender melhor.

      OBS: Com relação ao MissingPropertyException, pelo fato de o groovy ser uma linguagem que gera bytecode java… algumas características foram herdadas, e uma delas é o fato de não podermos utilizar variáveis que ainda não foram explicitamente declaradas. Para ser sincero, não sei informar se hoje já existe algo para lidar com isso sem gerar Exception, mas o que normalmente acontece é a declaração desta variável antes de seu uso – (Como no seu caso usando o println)

      ***

      Então Rodolfo, espero ter respondido suas questões, caso eu tenha entendido algo errado, por favor não deixe de perguntar, pois as suas dúvida podem me levar a aprender mais sobre novos recursos da linguagem.

      Se possível, eu gostaria que você me desse um exemplo referente a sua última pergunta, pois no caso da String não existem muitos tratamentos mesmo…

      1. Caso se informe def (a,b,c) = "ab": Ele vai retornar uma Exceção.

      Vou correr atrás dessas informações, mas me ajude a entender se estou no caminho que você precisa

      • Rodolfo disse:

        Jonatas, obrigado pela atenção 🙂

        Eu testei coisas baseadas no último exemplo do post, só pra sentir o gostinho mesmo. Nada sério.

        Usando o https://groovyconsole.appspot.com/, caso tenha alguma influência:

        // mais variáveis que valores
        def (nome, ano, cidade, pais) = “Stanley Kubrick#1928#New York”.split(“#”)
        println “Nome: $nome, Nascimento: $ano, Cidade: $cidade”
        ​———————-
        java.lang.ArrayIndexOutOfBoundsException: 3
        at Script1.run(Script1.groovy:1)

        // mais valores que variáveis
        def (nome, ano) = “Stanley Kubrick#1928#New York”.split(“#”)
        println “Nome: $nome, Nascimento: $ano”
        ———————-
        Nome: Stanley Kubrick, Nascimento: 1928

        A questão da variável não definida é OK, assim como “perder” um dos valores do split.

        O que me chamou atenção quando a variável não definida é que ela é usada como se fosse “Bash” no println… um erro de digitação e boom 🙂
        Achei meio esquisito isso. E parece que teria que escapar o $ com \ pra evitar a substituição, ex: “Cidade: \$cidade”

        Comparando com Python,

        >>> a, b, c = range(5)
        Traceback (most recent call last):
        File “”, line 1, in
        ValueError: too many values to unpack

        É um erro “ignorar” valores. Acho esse comportamento mais interessante. Imagina que você esteja processando um arquivo com várias linhas como aquela, mas talvez algumas linhas estejam faltando campos…

        Pra fechar, eu adicionaria 2 casos de uso pro multiple assignment.

        O primeiro,

        >>> a, b = 1, 2
        >>> a, b = b, a
        >>> a, b
        (2, 1)

        É um caso em que conhecemos bem o número de items na esquerda e direita, e de quebra pode usar multiple assignment para evitar a criação de uma variável temporária. Sem multiple assignment seria

        tmp = a
        a = b
        b = tmp

        O segundo, usar com funções que retornam vários valores. Isto tem em Python, mas vejo um uso interessante em Go, onde é comum funções retornarem um valor de erro explicitamente. Neste exemplo de Split não tem erro, mas podemos verificar o número de itens depois do split:

        https://play.golang.org/p/DHNXhWDaE4

        Parece que escreve-se algo similar em Groove assim:

        def v = “Stanley Kubrick#1928#New York”.split(“#”)
        if ( v.length != 3 ) {
        println “erro”
        return
        }
        (nome, ano, cidade) = v
        println “Nome: $nome, Nascimento: $ano, Cidade: $cidade”

        O caso do string split original é esquisito pois geralmente não vemos a string como um constante, mas sim algo que lemos como entrada.

        O que acha?

      • jonatasemidio disse:

        Opa Rodolfo,

        Percebi algo bem curioso…
        Quando utilizamos um split() que resulta em menos valores do que as variaveis:
        Ex
        // mais variáveis que valores
        def (nome, ano, cidade, pais) = "Stanley Kubrick#1928#New York".split("#")

        Ele gera a exceção: ArrayIndexOutOfBoundsException (Como você já observou anteriormente)

        No entanto, quando utilizamos a lista que seria gerada pelo split, o erro não ocorre
        def (nome, ano, cidade, pais) = [“Stanley Kubrick”, “1928”,”New York”]

        Depois de dar uma olhada, percebi que ao usarmos o split() o retorno da lista é do tipo String[] (array de String) que é diferente de usarmos uma lista direta onde o tipo é um ArrayList. E após testar o exemplo, percebi que quando o tipo é String[], este não consegue tratar a diferença entre variáveis e valores.

        Não sei informar em detalhes o porque desta diferença, mas neste caso, uma solução momentânea, seria gerar a lista para em seguida atribuir as variáveis.
        def (nome, ano, cidade, pais) = "Stanley Kubrick#1928#New York".split("#") as List

        Assim antes de fazer a atribuição o tipo String[] seria convertido para ArrayList, para assim poder efetuar a atribuição múltipla.

        —-

        Com relação a utilizar variáveis não declaradas, na minha opinião, tem que se retornar uma exceção.
        Para ser mais exato, no caso das IDEs, isso não aconteceria, pois a variável ia ficar marcada com a informação de que a mesma ainda não foi declarada. (Se tiver em um editor de texto simples, esse erro pode acabar ocorrendo por falta de atenção).

        —-

        Já quando o assunto é ignorar valores, realmente me parece mais interessante validar as quantidades antes de fazer as atribuições múltiplas. Não sei se o core team do groovy vai considerar isso um erro, mas acho valido criar uma Issue (pelo menos para desenvolver uma discussão sobre o assunto).

        Espero ter respondido.

        Abraços e obrigado por contribuir com o post.
        Perguntas, sugestões e reclamações são sempre bem vindas.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: