Go语言中的惯用切片splice?

3

我有以下代码来实现 splice(即,给定一个字节切片 full,另一个字节切片 part 和一个表示我想用 part 覆盖 full 中位置的 int pos):

package main

import (
    "fmt"
    "bytes"
)

func main() {
    full := []byte{0,0,0,0,0,0,0}
    part := []byte{1,1,1}

    newFull1 := splice(full, part, 2)
    fmt.Println(newFull1)
    // [0 0 1 1 1 0 0]

    newFull2 := splice(full, part, 3)
    fmt.Println(newFull2)
    // [0 0 0 1 1 1 0]
}

func splice(full []byte, part []byte, pos int) []byte {
    return bytes.Join([][]byte{full[:pos], part, full[len(full[:pos])+len(part):]}, []byte{})
}

基本上,我的方法是将三个字节片段连接起来:full中没有被part覆盖的第一部分、所有的part和full剩余的部分。是否有更好/更符合惯用法的方法来做这个呢?我没有找到标准库中实现这个的方法。


1
看起来不错。我认为你不会找到更简洁的方法来完成这个任务。不过你可能需要添加错误检查。pos 可能会超出边界。 - Daniel
4个回答

6
如果您知道的部分完全在整个范围内,您可以使用复制功能。
func main() {
    full := []byte{0, 0, 0, 0, 0, 0, 0}
    part := []byte{1, 1, 1}

    copy(full[2:], part)
    fmt.Println(full)
}

playground

然而,这样做会完全覆盖掉原来的内容。如果你想要保留原有的内容,可以首先用追加函数(append function)制作一份副本。

func main() {
    full := []byte{0, 0, 0, 0, 0, 0, 0}
    part := []byte{1, 1, 1}

    newFull := append([]byte{}, full...)
    copy(newFull[2:], part)
    fmt.Println("newFull:      ", newFull)
    fmt.Println("original full:", full)
}

playground

请注意,这仍然存在您原始代码的限制,即部分必须适合完整范围。


1
为什么不使用内置的append呢?
func splice(full, part []byte, pos int) (ret []byte) {
    ret = append(full[:pos], part...)
    return append(ret, full[pos:]...)
}

这可能不是非常快(有很多复制),但它相当易读。


1

这里是另一种拼接字节切片的变体。该算法类似于Anton Litvinov的stringSplice()。由于append函数与输入共享同一切片,因此必须使用与输入相同长度的副本。在之前的答案中,'beatgammit'没有这样做,导致无法正确工作。

package main

import "fmt"

func sliceSplice(input []byte, start, deleteCount int, item []byte) ([]byte) {
    fmt.Printf("input:      %v start = %d deleteCount = %d item: %v\n", input, start, deleteCount, item)
    cpy := make([]byte, len(input))
    copy(cpy, input)
    fmt.Println("cpy:       ", cpy)
    if start > len(cpy) {
        return append(cpy, item...)
    }
    ret := append(cpy[:start], item...)
    fmt.Println("ret:       ", ret)

    if start+deleteCount > len(cpy) {
        return ret
    }
    fmt.Println("cpy:       ", cpy, "       modified by shared slice 'ret'")
    fmt.Println("input[s+d] ", input[start+deleteCount:], "         not modified")
    return append(ret, input[start+deleteCount:]...)
}

func main() {
    oldFull := []byte{0, 0, 1, 1, 1, 0, 0}
    fmt.Println("oldFull:      ", oldFull, "\n")
    myFull := sliceSplice(oldFull, 3, 0, []byte{2, 2})
    fmt.Println("myFull:       ", myFull, "\n")
    myFull = sliceSplice(oldFull, 3, 1, []byte{2, 2})
    fmt.Println("myFull:       ", myFull, "\n")
    myFull = sliceSplice(oldFull, 3, 4, []byte{2, 2})
    fmt.Println("myFull:       ", myFull, "\n")
    myFull = sliceSplice(oldFull, 3, 6, []byte{2, 2})
    fmt.Println("myFull:       ", myFull, "\n")
    myFull = sliceSplice(oldFull, 7, 0, []byte{2, 2})
    fmt.Println("myFull:       ", myFull, "\n")
    myFull = sliceSplice(oldFull, 9, 6, []byte{2, 2})
    fmt.Println("myFull:       ", myFull, "\n")
}

我还包括了一组测试用例


0
一个关于字符串的变体(切割/拼接/连接)。
func stringSplice(full string, start, deleteCount int, item string) (ret string) {
    if start > len(full) {
        return full + item
    }
    ret = full[:start] + item

    if start+deleteCount > len(full) {
        return ret
    }
    return ret + full[start+deleteCount:]
}

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