【问题标题】:golang slice [:] matches with the last elementgolang slice [:] 匹配最后一个元素
【发布时间】:2018-06-06 12:24:54
【问题描述】:

我正在使用 golang 编写一个示例程序,如下所示

package main

import (
    "fmt"
)

type thing [2]byte

func walk(things []thing, idx int) []byte {
    var match []byte
    for i, thing := range things {
        if i == idx {
            match = thing[:]
        }
    }
    return match
}


func main() {
    ta := []thing{ thing{'W','A'}, thing{'O','R'} }
    m := walk(ta, 0)

    tb := []thing{ thing{'C','A'}, thing{'W','Y'}, thing{'N','V'} }
    n := walk(tb, 1)

    fmt.Printf("m = %s\n", m)
    fmt.Printf("n = %s\n", n)
}

输出是:

m = OR
n = NV

我不知道为什么当 type thing [2]byte 是一个大小为 2 的数组而 ta 是 type []thing 时会出现这种情况。

现在,当您使用 [][]byte

编写与此相同的代码时
package main

import (
    "fmt"
)

func walk(things [][]byte, idx int) []byte {
    var match []byte
    for i, thing := range things {
        if i == idx {
            match = thing[:]
        }
    }
    return match
}

func main() {
    ta := [][]byte{[]byte{'W', 'A'}, []byte{'O', 'R'}}
    m := walk(ta, 0)

    tb := [][]byte{[]byte{'C', 'A'}, []byte{'W', 'Y'}, []byte{'N', 'V'}}
    n := walk(tb, 1)

    fmt.Printf("m = %s\n", m)
    fmt.Printf("n = %s\n", n)
}

输出是

m = WA
n = WY

我对切片的这些不同行为感到困惑? 当ta is type thing []byteta[:] when ta is [][]byte 相同时打印出ta[:]

【问题讨论】:

  • 不一样——thing[2]byte 类型,而不是[]byte 类型。也就是说,它是一个array,而不是一个slice。语义不一样。
  • @manugup1 请允许我指出一些可能不正确的代码设计:如果你知道你有 [2]byte 作为切片的元素,为什么在你的第一个例子中,正在返回 @ 987654334@?如果您返回 [2]byte(一个数组),则该算法将按预期工作。你自己看看我想说什么:play.golang.org/p/wOGaJ9Pwtck。尽管如此,这是理解切片内部结构的一个很好的例子......
  • 顺便说一句:这个例子直截了当:play.golang.org/p/GelDPKlNFO-
  • @manugulp1 昨天我在这里看到一条评论,感谢您提供有关实际问题的更多见解。你不应该像那样抹去 cmets,实际上它们通过给我积极的反馈并鼓励我继续贡献来帮助。别忘了这个地方毕竟是一个 CoP(实践社区)。
  • @Victor 我没有删除任何评论(至少不是故意的)!事实上,那条评论帮助我很好地理解了这个概念。我很惊讶它后来消失了。

标签: go


【解决方案1】:

两种情况相同。

在第一种情况,您使用的是[][2]byte(数组切片)而不是[][]byte(切片切片)。

var match []byte
for i, thing := range things { // (2) change array on each iteration
    fmt.Printf("Thing %v", thing)
    if i == idx {
        match = thing[:] // (1) slice refers to array   
    }
}
return match // (3) here match slice refers to the last item of things

这里的解决方案之一是在match = thing[:] 之后添加break 语句。它结束循环,match 将引用预期的数组。

只是为了澄清这里的实际问题是什么,问题是您正在创建一个切片,该切片引用一个数组,该数组在每个 n-iteration 中用对应的 值覆盖>2字节数组切片的第n个元素因此,如果您不停止迭代,切片将获得最后一个元素的值

使用for .. := range“隐藏”一点这个事实。我想如果你用for i;i<n;i++ 编写相同的代码,你可以更好地理解实际发生的事情:https://play.golang.org/p/z3hCskZ9ezV

【讨论】:

  • 我想这将是一个可接受的答案,但我可以询问“持续迭代如何影响返回结果?”步骤(2)中发生了什么(我没有看到)?这里有一种clousure吗?猜猜这里没有 clousures...肯定是与 sliceHeader 结构有关的东西...真的不知道,让我想知道...
  • 这是一个棘手的行为。 match = thing[:] - 这里切片 match 收集引用数组。在每次迭代中thing 变量具有相同的地址 - 尝试fmt.Printf("Pointer %p \n", &thing)。在循环的末尾thing 是最后一个元素,因此match 指的是它的值。
  • 好吧,谢谢你的洞察力......我真的不明白它会出现......它具有相同的地址,但不同的价值......这是什么意思? .
  • 对我来说,在每次迭代中,thing(我的意思是,每次迭代中使用的 [2]byte 数组)都会被覆盖(被第 n 个元素的对应值)覆盖修改切片所引用的底层数组(正如match = thing[:]statement 所反映的那样)。所以问题是我们正在切片 sole 数组(即,名为“thing”的短声明变量)用于在每次迭代中存储值
【解决方案2】:

在一种情况下,单个 thing 是一个数组 ([2]byte),而在另一种情况下,它是一个切片 ([]byte)。在第一种情况下,您将一个数组切片为match,这会为您提供一个指向循环迭代变量的新切片。在第二种情况下,您正在对现有切片进行重新切片,因此您的新切片指向该切片的底层数组,即使在循环更改了局部切片变量之后也是如此。

因为您的循环在找到匹配项后继续运行,所以您找到匹配项,对其进行切片,然后继续迭代,更改本地循环迭代器的值。如果你这样做了:

    if i == idx {
        return thing[:]
    }

相反,问题消失了:https://play.golang.org/p/Uq4DbEGlGX8

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-17
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    相关资源
    最近更新 更多