One place for hosting & domains

      Variadic

      Como usar funções variadic em Go


      Introdução

      Uma função variadic é uma função que aceita zero, um, ou mais valores como um único argumento. Embora as funções variadic não sejam o caso comum, elas podem ser usadas para tornar seu código mais limpo e mais legível.

      As funções variadic são mais comuns do que parecem. A função mais comum é a Println do pacote fmt.

      func Println(a ...interface{}) (n int, err error)
      

      Uma função com um parâmetro antecedido por um sinal de reticências (...) é considerado como uma função variadic. As reticências significam que o parâmetro fornecido pode ser zero, um, ou mais valores. Para o pacote fmt.Println, elas declaram que o parâmetro a é uma função variadic.

      Vamos criar um programa que usa a função fmt.Println e envia valores zero, um ou mais:

      print.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println()
          fmt.Println("one")
          fmt.Println("one", "two")
          fmt.Println("one", "two", "three")
      }
      

      A primeira vez que chamamos fmt.Println, não enviamos nenhum argumento. A segunda vez que chamamos fmt.Println, enviamos um único argumento apenas, com o valor de one. Então, enviamos one e two e, finalmente, one, two e three.

      Vamos executar o programa com o seguinte comando:

      Veremos o seguinte resultado:

      Output

      one one two one two three

      A primeira linha do resultado está em branco. Isso se deve ao fato de que não enviamos nenhum argumento na primeira vez que o fmt.Println foi chamado. Na segunda vez que chamamos a função, o valor one foi impresso. Em seguida, one e two e, finalmente, one, two e three.

      Agora que vimos como chamar uma função variadic, vamos examinar como podemos definir nossa própria função variadic.

      Definindo uma função variadic

      Podemos definir uma função variadic usando reticências (...) na frente do argumento. Vamos criar um programa que cumprimenta as pessoas quando seus nomes são enviados para a função:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      Criamos uma função sayHello que usa um único parâmetro chamado names. O parâmetro é variadic, uma vez que colocamos reticências (...) antes do tipo de dados: ...string. Isso diz ao Go que a função pode aceitar zero, um, ou muitos argumentos.

      A função sayHello recebe o parâmetro names como uma slice. Como o tipo de dados é uma string, o parâmetro names pode ser tratado como uma fatia de strings ([]string) dentro do corpo da função. Podemos criar um loop com o operador range e iterar ao longo da fatia de strings.

      Se executarmos o programa, vamos receber o seguinte resultado:

      Output

      Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      Note que nada foi impresso na primeira vez que chamamos o sayHello. Isso acontece porque o parâmetro variadic era uma slice vazia da string. Como estamos formando loops pela fatia, não há nada para iterar até o fim e, dessa forma a função fmt.Printf nunca é chamada.

      Vamos modificar o programa para detectar se nenhum valor foi enviado:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          if len(names) == 0 {
              fmt.Println("nobody to greet")
              return
          }
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      Agora, usando uma instrução if, se nenhum valor for enviado, o tamanho de names será 0 e vamos imprimir nobody to greet:

      Output

      nobody to greet Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      O uso de um parâmetro variadic pode tornar seu código mais legível. Vamos criar uma função que junta as palavras com um delimitador específico. Criaremos esse programa sem uma função variadic primeiro para mostrar como ele seria lido:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy", "Jessica"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy"})
          fmt.Println(line)
      }
      
      func join(del string, values []string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Neste programa, estamos enviando uma vírgula (,) como o delimitador para a função join. Depois, enviamos uma fatia de valores para unir. Aqui está o resultado:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Como a função aceita uma fatia de string como o parâmetro values, tivemos que empacotar todas nossas palavras em uma fatia quando chamamos a função join. Isso pode tornar o código difícil de ler.

      Agora, vamos escrever a mesma função, mas usaremos uma função variadic:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Se executarmos o programa, podemos verificar que obtemos o mesmo resultado do programa anterior:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Embora ambas as versões da função join façam a mesma coisa em termos de programação, a versão variadic da função é muito mais fácil de ler quando ela está sendo chamada.

      Ordem do argumento variadic

      Você só pode ter um parâmetro variadic em uma função e ele deverá ser o último parâmetro definido na função. Definir parâmetros em uma função variadic em qualquer ordem que não seja o último parâmetro resultará em um erro de compilação:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(values ...string, del string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Desta vez, colocamos primeiro o parâmetro values na função join. Isso causará o seguinte erro de compilação:

      Output

      ./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

      Ao definir qualquer função variadic, apenas o último parâmetro pode ser variadic.

      Destacando argumentos

      Até agora, vimos que podemos enviar zero, um, ou mais valores para uma função variadic. No entanto, haverá ocasiões em que teremos uma fatia de valores, os quais vamos querer enviar para uma função variadic.

      Vamos examinar nossa função join da última seção para ver o que acontece:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Se executarmos este programa, vamos receber um erro de compilação:

      Output

      ./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

      Embora a função variadic converta o parâmetro de values ...string em uma fatia de strings []string, não podemos enviar uma fatia de strings como argumento. Isso acontece porque o compilador espera argumentos discretos de strings.

      Para contornar esse problema, podemos destacar uma fatia, adicionando a ela um sufixo na forma de reticências (...) e transformando-a em argumentos discretos que serão enviados para uma função variadic:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names...)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Desta vez, quando chamamos a função join, destacamos a fatia names anexando reticências (...).

      Isso permite que o programa agora seja executado conforme esperado:

      Output

      Sammy,Jessica,Drew,Jamie

      É importante notar que ainda poderemos enviar zero, um, ou mais argumentos, assim como uma fatia que destacarmos. Aqui está o código que envia todas as variações que vimos até agora:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Agora, sabemos como enviar zero, um, ou muitos argumentos, além de uma fatia que destacarmos para uma função variadic.

      Conclusão

      Neste tutorial, vimos como as funções variadic podem tornar seu código mais limpo. Embora nem sempre será necessário usá-las, você pode achá-las uteis:

      • Se achar que vai criar uma fatia temporária apenas para enviar para uma função.
      • Quando o número de parâmetros de entrada são desconhecidos ou irão variar quando chamados.
      • Para tornar seu código mais legível.

      Para aprender mais sobre como criar e chamar funções, você pode ler o artigo sobre Como definir e chamar funções em Go.



      Source link

      How To Use Variadic Functions in Go


      Introduction

      A variadic function is a function that accepts zero, one, or more values as a single argument. While variadic functions are not the common case, they can be used to make your code cleaner and more readable.

      Variadic functions are more common than they seem. The most common one is the Println function from the fmt package.

      func Println(a ...interface{}) (n int, err error)
      

      A function with a parameter that is preceded with a set of ellipses (...) is considered a variadic function. The ellipsis means that the parameter provided can be zero, one, or more values. For the fmt.Println package, it is stating that the parameter a is variadic.

      Let’s create a program that uses the fmt.Println function and pass in zero, one, or more values:

      print.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println()
          fmt.Println("one")
          fmt.Println("one", "two")
          fmt.Println("one", "two", "three")
      }
      

      The first time we call fmt.Println, we don’t pass any arguments. The second time we call fmt.Println we pass in only a single argument, with the value of one. Then we pass one and two, and finally one, two, and three.

      Let’s run the program with the following command:

      We’ll see the following output:

      Output

      one one two one two three

      The first line of the output is blank. This is because we didn’t pass any arguments the first time fmt.Println was called. The second time the value of one was printed. Then one and two, and finally one, two, and three.

      Now that we have seen how to call a variadic function, let’s look at how we can define our own variadic function.

      Defining a Variadic Function

      We can define a variadic function by using an ellipsis (...) in front of the argument. Let’s create a program that greets people when their names are sent to the function:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      We created a sayHello function that takes only a single parameter called names. The parameter is variadic, as we put an ellipsis (...) before the data type: ...string. This tells Go that the function can accept zero, one, or many arguments.

      The sayHello function receives the names parameter as a slice. Since the data type is a string, the names parameter can be treated just like a slice of strings ([]string) inside the function body. We can create a loop with the range operator and iterate through the slice of strings.

      If we run the program, we will get the following output:

      Output

      Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      Notice that nothing printed for the first time we called sayHello. This is because the variadic parameter was an empty slice of string. Since we are looping through the slice, there is nothing to iterate through, and fmt.Printf is never called.

      Let’s modify the program to detect that no values were sent in:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          if len(names) == 0 {
              fmt.Println("nobody to greet")
              return
          }
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      Now, by using an if statement, if no values are passed, the length of names will be 0, and we will print out nobody to greet:

      Output

      nobody to greet Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      Using a variadic parameter can make your code more readable. Let’s create a function that joins words together with a specified delimiter. We’ll create this program without a variadic function first to show how it would read:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy", "Jessica"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy"})
          fmt.Println(line)
      }
      
      func join(del string, values []string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      In this program, we are passing a comma (,) as the delimiter to the join function. Then we are passing a slice of values to join. Here is the output:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Because the function takes a slice of string as the values parameter, we had to wrap all of our words in a slice when we called the join function. This can make the code difficult to read.

      Now, let’s write the same function, but we’ll use a variadic function:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      If we run the program, we can see that we get the same output as the previous program:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      While both versions of the join function do the exact same thing programmatically, the variadic version of the function is much easier to read when it is being called.

      Variadic Argument Order

      You can only have one variadic parameter in a function, and it must be the last parameter defined in the function. Defining parameters in a variadic function in any order other than the last parameter will result in a compilation error:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(values ...string, del string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      This time we put the values parameter first in the join function. This will cause the following compilation error:

      Output

      ./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

      When defining any variadic function, only the last parameter can be variadic.

      Exploding Arguments

      So far, we have seen that we can pass zero, one, or more values to a variadic function. However, there will be occasions when we have a slice of values and we want to send them to a variadic function.

      Let’s look at our join function from the last section to see what happens:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      If we run this program, we will receive a compilation error:

      Output

      ./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

      Even though the variadic function will convert the parameter of values ...string to a slice of strings []string, we can’t pass a slice of strings as the argument. This is because the compiler expects discrete arguments of strings.

      To work around this, we can explode a slice by suffixing it with a set of ellipses (...) and turning it into discrete arguments that will be passed to a variadic function:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names...)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      This time, when we called the join function, we exploded the names slice by appending ellipses (...).

      This allows the program to now run as expected:

      Output

      Sammy,Jessica,Drew,Jamie

      It’s important to note that we can still pass a zero, one, or more arguments, as well as a slice that we explode. Here is the code passing all the variations that we have seen so far:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      We now know how to pass zero, one, or many arguments, as well as a slice that we explode, to a variadic function.

      Conclusion

      In this tutorial, we have seen how variadic functions can make your code cleaner. While you won’t always need to use them, you may find them useful:

      • If you find that you’re creating a temporary slice just to pass to a function.
      • When the number of input params are unknown or will vary when called.
      • To make your code more readable.

      To learn more about creating and calling functions, you can read How to Define and Call Functions in Go.



      Source link