Concatene duas fatias no Go


477

Estou tentando combinar a fatia [1, 2]e a fatia [3, 4]. Como posso fazer isso no Go?

Eu tentei:

append([]int{1,2}, []int{3,4})

mas conseguiu:

cannot use []int literal (type []int) as type int in append

No entanto, a documentação parece indicar que isso é possível, o que estou perdendo?

slice = append(slice, anotherSlice...)

Respostas:


878

Adicione pontos após a segunda fatia:

//---------------------------vvv
append([]int{1,2}, []int{3,4}...)

Isto é como qualquer outra função variável.

func foo(is ...int) {
    for i := 0; i < len(is); i++ {
        fmt.Println(is[i])
    }
}

func main() {
    foo([]int{9,8,7,6,5}...)
}

37
append()uma função variadica e ...permite passar vários argumentos para uma função variadic a partir de uma fatia.

11
Isso tem algum desempenho quando as fatias são bem grandes? Ou o compilador não passa realmente todos os elementos como parâmetros?
Toad

15
@ Sapo: Na verdade, não os espalha. No foo()exemplo acima, o isparâmetro contém uma cópia da fatia original, ou seja, possui uma cópia da referência leve para a mesma matriz subjacente, len e cap. Se a foofunção alterar um membro, a alteração será vista no original. Aqui está uma demonstração . Portanto, a única sobrecarga real será que ela cria uma nova fatia se você ainda não a possui, como: foo(1, 2, 3, 4, 5)que criará uma nova fatia que isserá mantida.

2
Ah Se bem entendi, a função variadic é realmente implementada como uma matriz de parâmetros (em vez de todos os parâmetros na pilha)? E desde que você passa a fatia, ela realmente mapeia uma a uma?
Toad

@Carga: Sim, quando você usa ...uma fatia existente, ela simplesmente passa essa fatia. Quando você passa argumentos individuais, ele os reúne em uma nova fatia e a passa. Não tenho conhecimento de primeira mão da mecânica exatas, mas eu acho que isso: foo(1, 2, 3, 4, 5)e isto: func foo(is ...int) {apenas de-açúcar para isso: foo([]int{1, 2, 3, 4, 5})e isto: func foo(is []int) {.

77

Anexando e copiando fatias

A função variável appendacrescenta zero ou mais valores xao s tipo S, que deve ser um tipo de fatia, e retorna a fatia resultante, também do tipo S. Os valores xsão passados ​​para um parâmetro do tipo ...Tonde Té o tipo de elemento Se as respectivas regras de passagem de parâmetro se aplicam. Como um caso especial, o append também aceita um primeiro argumento atribuível ao tipo []bytecom um segundo argumento do stringtipo seguido por .... Este formulário acrescenta os bytes da sequência.

append(s S, x ...T) S  // T is the element type of S

s0 := []int{0, 0}
s1 := append(s0, 2)        // append a single element     s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)  // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)    // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}

Passando argumentos para ... parâmetros

Se fé variável com o tipo de parâmetro final ...T, na função o argumento é equivalente a um parâmetro do tipo []T. Em cada chamada de f, o argumento passado para o parâmetro final é uma nova fatia do tipo []Tcujos elementos sucessivos são os argumentos reais, que devem ser todos atribuíveis ao tipo T. O comprimento da fatia é, portanto, o número de argumentos vinculados ao parâmetro final e pode diferir para cada site de chamada.

A resposta para sua pergunta é um exemplo s3 := append(s2, s0...)na Especificação da linguagem de programação Go . Por exemplo,

s := append([]int{1, 2}, []int{3, 4}...)

6
Nota: o uso geral de anexar (fatia1, fatia2 ...) me parece bastante perigoso. Se a fatia1 for uma fatia de uma matriz maior, os valores dessa matriz serão substituídos pela fatia2. (Faz-me encolho que esta não parece ser uma preocupação comum?)
Hugo

7
@ Hugo Se você "entregar" uma fatia da sua matriz, saiba que o "proprietário" da fatia poderá ver / substituir partes da matriz que estão além do comprimento atual da fatia. Se você não quiser isso, poderá usar uma expressão de fatia completa (na forma de a[low : high : max]) que também especifica a capacidade máxima . Por exemplo, a fatia a[0:2:4]terá capacidade 4e não poderá ser realçada para incluir elementos além disso, mesmo que a matriz de suporte possua mil elementos depois disso.
icza 17/02

30

Nada contra as outras respostas, mas achei a breve explicação nos documentos mais facilmente compreensível do que os exemplos:

func anexar

func append(slice []Type, elems ...Type) []TypeA função interna de acréscimo acrescenta elementos ao final de uma fatia. Se tiver capacidade suficiente, o destino será redimensionado para acomodar os novos elementos. Caso contrário, uma nova matriz subjacente será alocada. Anexar retorna a fatia atualizada. Portanto, é necessário armazenar o resultado do acréscimo, geralmente na variável que contém a própria fatia:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

Como um caso especial, é legal anexar uma string a uma fatia de bytes, assim:

slice = append([]byte("hello "), "world"...)

1
Obrigado! Valioso para mim!
Korjavin Ivan

23

Eu acho importante salientar e saber que, se a fatia de destino (a que você anexa) tiver capacidade suficiente, o anexo ocorrerá "no local", realocando novamente o destino (realocando novamente para aumentar seu comprimento para ser capaz de acomodar os elementos anexáveis).

Isso significa que, se o destino tiver sido criado dividindo uma matriz ou fatia maior que possua elementos adicionais além do comprimento da fatia resultante, eles poderão ser substituídos.

Para demonstrar, veja este exemplo:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)

x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))

x = append(x, y...)
fmt.Printf("x: %v\n", x)

fmt.Printf("a: %v\n", a)

Saída (experimente no Go Playground ):

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]

Criamos uma matriz "de apoio" acom comprimento 10. Em seguida, criamos a xfatia de destino cortando essa amatriz, a yfatia é criada usando o literal composto []int{3, 4}. Agora, quando anexar ya x, o resultado é o esperado [1 2 3 4], mas o que pode ser surpreendente é que a matriz de suporte atambém mudou, porque a capacidade de xseja 10o que é suficiente para anexar ya ele, assim xé resliced que também usará o mesmo aleque de apoio, e append()irá copiar elementos de ypara lá.

Se você quiser evitar isso, use uma expressão de fatia completa que tenha o formato

a[low : high : max]

que constrói uma fatia e também controla a capacidade da fatia resultante, configurando-a para max - low.

Veja o exemplo modificado (a única diferença é que criamos xassim x = a[:2:2]:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)

x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))

x = append(x, y...)
fmt.Printf("x: %v\n", x)

fmt.Printf("a: %v\n", a)

Saída (experimente no Go Playground )

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]

Como você pode ver, obtemos o mesmo xresultado, mas a matriz de backup anão mudou, porque a capacidade de xera "apenas" 2(graças à expressão de fatia completa a[:2:2]). Para fazer o acréscimo, é alocada uma nova matriz de apoio que pode armazenar os elementos de ambos xe y, o qual é distinto a.


2
É muito útil para o problema que estou enfrentando. Obrigado.
Aidy

9

Gostaria de enfatizar a resposta do @icza e simplificá-la um pouco, pois é um conceito crucial. Presumo que o leitor esteja familiarizado com as fatias .

c := append(a, b...)

Esta é uma resposta válida para a pergunta. MAS, se você precisar usar as fatias 'a' e 'c' posteriormente no código em um contexto diferente, essa não é a maneira segura de concatenar as fatias.

Para explicar, vamos ler a expressão não em termos de fatias, mas em termos de matrizes subjacentes:

"Pegue a matriz (subjacente) de 'a' e acrescente elementos da matriz 'b' a ela. Se a matriz 'a' tiver capacidade suficiente para incluir todos os elementos de 'b' - a matriz subjacente de 'c' não será uma nova matriz , na verdade será a matriz 'a'. Basicamente, a fatia 'a' mostrará len (a) elementos da matriz subjacente 'a' e a fatia 'c' mostrará len (c) a matriz 'a'. "

append () não cria necessariamente uma nova matriz! Isso pode levar a resultados inesperados. Veja o exemplo do Go Playground .

Sempre use a função make () se quiser garantir que uma nova matriz seja alocada para a fatia. Por exemplo, aqui estão algumas opções feias, mas eficientes o suficiente para a tarefa.

la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)

la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)

Obrigado por apontar para esses efeitos colaterais. Surpreendentemente, contrastando com este szenario modificado. play.golang.org/p/9FKo5idLBj4 Embora ao fornecer excesso de capacidade, deva-se pensar cuidadosamente sobre esses efeitos colaterais intrigantes contra intuição plausível.
olippuner

5

função append () e operador spread

Duas fatias podem ser concatenadas usando o appendmétodo na biblioteca golang padrão. O que é semelhante à variadicoperação da função. Então, precisamos usar...

package main

import (
    "fmt"
)

func main() {
    x := []int{1, 2, 3}
    y := []int{4, 5, 6}
    z := append([]int{}, append(x, y...)...)
    fmt.Println(z)
}

a saída do código acima é: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)vai funcionar. Passando argumentos para... parâmetros.

Se ffor variável com um parâmetro final pdo tipo ...T, dentro fdo tipo de pé equivalente ao tipo []T.

Se ffor invocado sem argumentos reais para p, o valor passado para pé nil.

Caso contrário, o valor passado é uma nova fatia do tipo []Tcom uma nova matriz subjacente cujos elementos sucessivos são os argumentos reais, aos quais todos devem ser atribuídos T. O comprimento e a capacidade da fatia são, portanto, o número de argumentos vinculados pe podem diferir para cada site de chamada.

Dada a função e chamadas

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
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.