Почему большой int api в Go так странно?

Я пытаюсь понять конструктивные решения большого 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)

Есть ли причина, по которой они решили это сделать? Я нахожу это немного запутанным и должен искать его, когда я его использую.

Ответ 1

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

Ответ 2

Я бы добавил к ответу Denys, что если вы рассмотрите альтернативный 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 первого стиля вам нужно распределение каждый раз. Таким образом, в этом случае вы либо мутируете операнды, либо выделяете в большой. В api у вас есть возможность либо.

Btw, следующее было бы эквивалентно первой цепочке...

r = r.Mul(r.AddP(x, r.Add(y, z)), v) 

Что на самом деле выглядит более явно (x + y + z) * v