Почему большой 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