以下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())
答案 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是对的,我也同意它与浅版和深版相关。