枚举数组的正确方法是什么?

问题描述 投票:-1回答:2

在阅读strconv package时,我注意到对测试用例的枚举实现为:link

    for i := range parseInt64Tests {
      test := &parseInt64Tests[i]
      if test.err != nil {
        test.err = &NumError{"ParseInt", test.in, test.err}
      }
    }

我对&parseInt64Tests[i]非常困惑。为什么不只是for i := range parseInt64Tests

我编写了自己的测试,以检查for循环是否返回原始数组项或新副本。

var calculationTests = []calculationCases {
    {0,0, nil},
    {8,0, nil},
    {9,1,nil},
    {11, 1, nil },
    {12, 2, nil },
    {14, 2, nil },
    {1969, 654, nil },
    {100756, 33583, nil },
 }

 func TestCalculate(t *testing.T) {
    for i, test := range calculationTests {
        if &calculationTests[i] != &test {
           t.Errorf("items not equal %v %v %p, %p", test, calculationTests[i], &test, &calculationTests[i])
        }

    } 
 }

输出如下:

--- FAIL: TestCalculate (0.00s)
fule_test.go:25: array item not same {0 0 <nil>} {0 0 <nil>} 0xc0000a4040, 0x12306a0
fule_test.go:25: array item not same {8 0 <nil>} {8 0 <nil>} 0xc0000a4040, 0x12306c0
fule_test.go:25: array item not same {9 1 <nil>} {9 1 <nil>} 0xc0000a4040, 0x12306e0
fule_test.go:25: array item not same {11 1 <nil>} {11 1 <nil>} 0xc0000a4040, 0x1230700
fule_test.go:25: array item not same {12 2 <nil>} {12 2 <nil>} 0xc0000a4040, 0x1230720
fule_test.go:25: array item not same {14 2 <nil>} {14 2 <nil>} 0xc0000a4040, 0x1230740
fule_test.go:25: array item not same {1969 654 <nil>} {1969 654 <nil>} 0xc0000a4040, 0x1230760
fule_test.go:25: array item not same {100756 33583 <nil>} {100756 33583 <nil>} 0xc0000a4040, 0x1230780
FAIL
exit status 1

证明for loop返回一个副本,而不是原始实例。

我的问题是:循环数组的最佳实践是什么,我应该始终使用索引器而不是枚举器吗?

go
2个回答
1
投票
文章https://github.com/golang/go/wiki/CommonMistakes提供了清晰的描述,即在循环中处理数组时发生了什么紧急情况。

您可以检查部分

使用对循环迭代器变量的引用

如上所述,&calculationTests[i]是切片元素的地址。因此,如果将slice的地址与局部变量的地址进行比较,每次都会给出错误。如果您想比较这些值,则可以像

if calculationTests[i] != test { t.Errorf("items not equal %v %v %p, %p", test, calculationTests[i], &test, &calculationTests[i]) }


0
投票
我的问题是:循环数组的最佳实践是什么,我应该始终使用索引器而不是枚举器吗?

我认为这取决于您的用例。

strconv包的情况下,作者想要

modify全局范围内的parseInt64Tests数组,因此他们选择使用索引号访问该数组的原始成员,然后对其进行修改。

如果作者使用枚举器,则他们不能修改原始的parseInt64Tests数组,因为枚举器返回该数组每个成员的副本;如果他们使用&从该副本获取地址,它将返回另一个地址,而不是原始地址。

通常,如果要修改或改变封闭变量(循环外的变量)的状态,请使用索引。如果没有,请使用枚举器。

这里是示例:https://play.golang.org/p/EItIKpWU_qK

package main import ( "fmt" ) type Data struct { Value int } var modifyMe = []Data{ {Value: 0}, {Value: 1}, {Value: 2}, {Value: 3}, {Value: 4}, {Value: 5}, {Value: 6}, {Value: 7}, {Value: 8}, } func main() { // modify() notModify() fmt.Println(modifyMe) } func modify() { for i, _ := range modifyMe { refToModifyMeData := &modifyMe[i] refToModifyMeData.Value = refToModifyMeData.Value + 100 } } func notModify() { for _, data := range modifyMe { refToModifyMeData := &data refToModifyMeData.Value = refToModifyMeData.Value + 100 } }

© www.soinside.com 2019 - 2024. All rights reserved.