我正在尝试理解big int api背后的设计决策。
例如,要添加两个大的整数:
a := big.NewInt(10)
b := big.NewInt(20)
c := big.NewInt(0)
d := c.Add(a,b)
其中d与末尾的c相同。初始零点无关紧要。
为什么不呢:
a := big.NewInt(10)
b := big.NewInt(20)
c := big.Add(a,b)
或者更好:
a := big.NewInt(10)
b := big.NewInt(20)
c := a.Add(b)
他们选择这样做有什么理由吗?我发现它有点混乱,每当我使用它时都要查找它。
答案 0 :(得分:11)
Add
是一种改变接收器的方法。
所以就这样做
c := big.NewInt(0).Add(a,b)
或
var c big.Int
c.Add(a,b)
Add返回接收器这一事实对于函数链很有用,但你不需要 来使用返回的值。
现在假设我们不会将bigInt作为接收器(c := big.Add(a,b)
)或接收器不会被修改(c := a.Add(b)
)。在这两种情况下,都必须为操作分配一个大的Int并返回(作为指针)。如果您已经分配了大量的Int并准备就绪,这将是浪费。计算的整数不仅仅是一个简单的一个或两个单词struct,它可能很大。所以最好允许使用预定义的var,特别是因为你经常在计算循环中使用你的大整数。
c := big.Add(a,b) // wasteful because doesn't allow the use of a preexisting big int
c := a.Add(b) // either modifies a (which would force you to copy it each time if you want to keep it) or is wasteful like the last one
答案 1 :(得分:2)
我想补充一下丹尼斯的答案,如果你考虑替代api,它可以支持链接如下:
x.Add(y).Add(z).Mul(v)
很快问题是 - 这是否遵循正常的操作员排序?
x+y+z*v = x+y+(z*v)
但是第一个链会导致(x + y + z)* v(在go中,但可能不在另一种语言中) - 因此需要小心。
此:
r = r.AddP(x, y.Add(y, z.Mul(z, v)))
有些丑陋,我同意,但它确实强制显式顺序,它也让我们有机会保持操作数不变,没有额外的分配(如Denys所述)。例如(注意r每次都是接收者):
r = r.Add(x, r.Add(y, r.MulInt(z, v)))
此处仅更改结果值(r),x,y,z未更改 - 要在第一个样式API中执行此操作,每次都需要进行分配。因此,在这种情况下,你要么改变操作数,要么在big.Int api中分配,你可以选择。
顺便说一句,以下内容相当于第一个链接...
r = r.Mul(r.AddP(x, r.Add(y, z)), v)
实际上看起来更像是(x + y + z)* v