Go:在循环中追加big.Int来切换意外结果

时间:2016-02-23 03:34:58

标签: go

以下Go程序产生1,2,3,4 in,然后产生5,5,5,5。在这两种情况下我都期待1,2,3,4。我做错了什么?

package main

import (
    "fmt"
    "math/big"
)

func primesLessThan(n *big.Int) (primes []big.Int) {
    var one big.Int
    one.SetInt64(1)
    var i big.Int
    i.SetInt64(1)
    for i.Cmp(n) < 0 {
        fmt.Println(i.String())
        primes = append(primes, i)
        i.Add(&i, &one)
    }
    return
}

func main() {
    primes := primesLessThan(big.NewInt(5))
    for _, p := range primes {
        fmt.Println(p.String())
    }
}

更新:以下代码段说明了响应中描述的浅拷贝的意外副作用。以下代码段的输出为3

one := big.NewInt(1)
two := big.NewInt(2)

one = two // Shallow copy. Question: how do I do a deep copy?

one.SetInt64(3) // Side-effect: also changes two

fmt.Println(one.String())
fmt.Println(two.String())

3 个答案:

答案 0 :(得分:3)

这是因为对象存储内部数据的方式。

举个例子:

package main

import "fmt"

type foo struct {
    value int
}

func bar() (r []foo) {
    var f foo
    for i := 0; i < 5; i++ {
        f.value = i
        r = append(r, f)
    }
    return
}

func main() {
    for _, v := range bar() {
        fmt.Println(v)
    }
}

输出将是预期的

{0}
{1}
{2}
{3}
{4}

你的例子中的问题是big.Int将其值存储在切片中,切片是指针。因此,当创建big.Int的副本时,新副本包含指向内存中相同切片的新指针。创建浅拷贝而不是深拷贝。

有关bit.Int的声明方式,请参阅https://golang.org/src/math/big/int.go?s=388:468#L8,然后参阅https://golang.org/src/math/big/nat.go#L25了解nat的声明方式。

这是一个使用big.Int的解决方案

package main

import (
    "fmt"
    "math/big"
)

func primesLessThan(n *big.Int) (primes []big.Int) {
    var one big.Int
    one.SetInt64(1)
    var i big.Int
    i.SetInt64(1)
    for i.Cmp(n) < 0 {
        var result big.Int
        result.Set(&i)
        fmt.Println(result.String())
        primes = append(primes, result)
        i.Add(&i, &one)
    }
    return
}

func main() {
    primes := primesLessThan(big.NewInt(5))
    for _, p := range primes {
        fmt.Println(p.String())
    }
}

答案 1 :(得分:0)

@GarMan解决方案很好,但您可以更简单地在append() primes []string级别进行字符串转换:

primes = append(primes, i.String())

答案 2 :(得分:-2)

我们看到的...... 你的素数[] big.Int就像一个指针数组。通过在每个素数槽中存储i,您实际上是在每个点中存储完全相同的指针。这就是它为什么要四次打印的原因。

为什么我们看到这个... 由于big.Int的内部,big.Int的每个严格对象都有一个内部切片。切片是作为指针实现的,因为它们实际上只是花哨的数组。因此,虽然您可能有一个big.Int对象,但该对象有一个指向切片的指针。

复制big.Int对象时,您正在复制此内部切片指针。

@GarMan是对的,我也同意它与浅版和深版相关。