如何在Go中连接两个数组

9

我有一个基础问题,我很难找到答案,因为有很多关于如何使用append函数和spread运算符连接两个“slices”的答案,这些答案错误地使用了“数组”一词。

我是Go语言的新手,并且假设在已知大小的情况下使用定长数组是一个好习惯。然而,我在处理数组时遇到了困难,因为我无法弄清楚如何进行简单的操作,比如拼接。以下是一些代码。

var seven [7]int

five := [5]int{1,2,3,4,5}
two := [2]int{6,7}

//this doesn't work as both the inputs and assignment are the wrong type
seven = append(five,two)

//this doesn't work as the assignment is still the wrong type
seven = append(five[:],two[:])

//this works but I'm not using arrays anymore so may as well use slices everywhere and forget sizing
seven2 := append(five[:],two[:])

据我所见,我可以放弃使用数组,仅使用切片,或者编写循环来显式构建新数组。还有第三种选择吗?
6个回答

16

append() 只能用于将元素附加到切片中。如果您有一个数组,您不能直接将其传递给 append()

您可以做的是 切片 数组,这样您就会得到一个切片(它将使用数组作为其后备存储器),并且您可以将该切片用作元素的目标和源。

例如:

s := seven[:0]
s = append(s, five[:]...)
s = append(s, two[:]...)
fmt.Println(seven)

这将会打印出以下内容(可以在Go Playground上试一下):
[1 2 3 4 5 6 7]

此外还要注意,由于 append() 返回结果切片,因此可以将所有内容都写在一行中:
_ = append(append(seven[:0], five[:]...), two[:]...)

这里不需要存储结果,因为我们只有并且想要使用支持数组,但通常情况下并非如此。

这将输出相同的结果,在Go Playground上尝试一下。虽然这不是很易读,所以不值得将其压缩成一行。

虽然当你有目标数组时,“追加”数组只是将它们复制到目标位置。为此,您也可以使用内置的copy()函数。请注意,copy()函数也仅接受切片,因此您也必须在此处对数组进行切片。

copy(seven[:], five[:])
copy(seven[len(five):], two[:])
fmt.Println(seven)

这将输出相同的结果。在Go Playground上尝试一下吧。


谢谢,我之前没有完全理解支持数组如何在原地修改,即使返回值是一个切片。 - Simon Notley
3
如果append()可以进行“原地”执行,则不会分配新数组,而是使用现有的后备数组。 - icza
@SimonN,请阅读这篇文章。同时,这篇文章也是必读的。 - kostix

1
你可以使用 copy
copy(seven[:], five[:])
copy(seven[5:], two[:])
fmt.Printf("%v\n", seven)
> [1 2 3 4 5 6 7]

1
你可以使用copy函数在go中连接两个数组。
package main
import "fmt"

func main() {
 five := [5]int{1, 2, 3, 4, 5}
 two := [2]int{6, 7}

 var n [len(five) + len(two)]int
 copy(n[:], five[:])
 copy(n[len(five):], two[:])
 fmt.Println(n)
}

1

https://blog.golang.org/go-slices-usage-and-internals

Golang运行时会检查当前索引是否超过最大可能值。在数组的一侧,它查找其类型(其中包含其长度和对元素类型的引用),因为这种类型只能在编译时注册。
// each array mention with unique size creates new type
array := [5]byte{1,2,3,4,5} 

在切片的一侧,它查找它们的标题,看起来像这样:
type slice {
      data *byte
      len int
      cap int // capacity, the maximum possible index
}

正如您所看到的,任何切片都是具有数据、长度和容量字段的单一结构,而数组只是指向数据的单一指针(*byte)。
当您尝试将数组转换为切片时,它只会创建切片头并填充字段:
slice := array[:]

==

slice := Slice{}
slice.data = array
slice.len = type_of(array).len
slice.cap = type_of(array).len

0

有一种更通用的方法可以附加任何类型的数组(一旦Golang具有泛型,但现在此解决方案仅适用于字符串。只需根据需要更改类型即可)。Fold的概念来自函数式编程。请注意,我还包括了一个使用Fold的过滤器函数。该解决方案不是堆栈安全的,但在许多情况下并不重要。可以通过跳板实现堆栈安全。最后是其用法示例。

func FoldRightStrings(as, z []string, f func(string, []string) []string) []string {
    if len(as) > 1 { //Slice has a head and a tail.
        h, t := as[0], as[1:len(as)]
        return f(h, FoldRightStrings(t, z, f))
     } else if len(as) == 1 { //Slice has a head and an empty tail.
        h := as[0]
        return f(h, FoldRightStrings([]string{}, z, f))
    }
    return z
}

func FilterStrings(as []string, p func(string) bool) []string {
    var g = func(h string, accum []string) []string {
        if p(h) {
            return append(accum, h)
        } else {
            return accum
        }
    }
    return FoldRightStrings(as, []string{}, g)
}

func AppendStrings(as1, as2 []string) []string {
    var g = func(h string, accum []string) []string {
        return append(accum, h)
    }
    return FoldRightStrings(as1, as2, g)
}

func TestAppendStringArrays(t *testing.T) {
    strings := []string{"a","b","c"}
    bigarray := AppendStrings(AppendStrings(strings, strings),AppendStrings(strings, strings))

    if diff := deep.Equal(bigarray, []string{"a","b","c","c","b","a","a","b","c","c","b","a"}); diff != nil {
        t.Error(diff)
    }
}

0

你可以通过将数组转换为切片来轻松实现:

arr1 := [...]int {1,2,3,}
arr2 := [...]int {4,5,6, }

//arr3 = arr1 + arr2                // not allowed

// converting arrays into slice 
slc_arr1, slc_arr2 := arr1[:], arr2[:]
slc_arr3 := make([]int, 0)

slc_arr3 = append(slc_arr1, slc_arr2...)
fmt.Println(slc_arr3)               // [1 2 3 4 5 6]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接