mutable/dense チュートリアル
このチュートリアルでは、一変数多項式をその場で構築・更新する方法を示します。係数を 1 つずつ設定し、和と積を 1 つのコンテナに累積し、スナップショットを保持し、結果をイミュータブルなコードに引き渡します。
クイックスタート
moon add Luna-Flow/luna-poly@0.2.0
import {
"Luna-Flow/luna-poly/mutable",
}
test "mutable dense quick start" {
let p = @mutable.DensePolynomial::from_coefficients([1, 2, 3])
p.set_coefficient(1, 5)
inspect(p, content="1 + 5x^1 + 3x^2")
}
set_coefficient(1, 5) は p 自身の の係数を変更します。
日常的なタスク
係数を 1 つずつ埋める
ゼロから始めて、分かっている係数を設定します。配列は必要に応じて伸びます:
test "filling" {
let p : @mutable.DensePolynomial[Int] = @mutable.DensePolynomial::zero()
for k in 0..<5 {
p.set_coefficient(k, k * k)
}
inspect(p, content="1x^1 + 4x^2 + 9x^3 + 16x^4")
p.set_coefficient(4, 0)
debug_inspect(p.degree(), content="Some(3)")
}
多項式は正規形に保たれるため、先頭係数をゼロにすると次数が下がります。
1 つのコンテナに累積する
add_inplace と mul_inplace はレシーバを更新するので、ループで中間値が生じません:
test "accumulate" {
let x_plus_1 = @mutable.DensePolynomial::from_coefficients([1, 1])
let product : @mutable.DensePolynomial[Int] = @mutable.DensePolynomial::one()
let sum : @mutable.DensePolynomial[Int] = @mutable.DensePolynomial::zero()
for _ in 0..<3 {
product.mul_inplace(x_plus_1)
sum.add_inplace(product)
}
inspect(product, content="1 + 3x^1 + 3x^2 + 1x^3")
inspect(sum, content="3 + 6x^1 + 4x^2 + 1x^3")
}
sum は です。
スナップショットを保持する
copy と to_immut は、さらに変更を加える前に独立したスナップショットを取ります:
test "snapshots" {
let p = @mutable.DensePolynomial::from_coefficients([1, 1])
let saved = p.copy()
let frozen = p.to_immut()
p.scale_inplace(2, 10)
inspect(p, content="10x^2 + 10x^3")
inspect(saved, content="1 + 1x^1")
inspect(frozen, content="1 + 1x^1")
}
演算子は変更を行わない
新しい値が欲しい場合は演算子を使います。オペランドはそのまま残ります:
test "operators" {
let p = @mutable.DensePolynomial::from_coefficients([1, 2])
let q = p * p + p
inspect(q, content="2 + 6x^1 + 4x^2")
inspect(p, content="1 + 2x^1")
}
さらに進む
ミュータブルに構築し、イミュータブルに公開する
逐次的な処理はミュータブルなバッファで行い、イミュータブルな値を返すと、呼び出し側は値セマンティクスを得られます:
fn truncated_exp_numerators(n : Int) -> @immut.DensePolynomial[Int] {
// n! * (1 + x + x^2/2! + ... + x^n/n!)
let buffer : @mutable.DensePolynomial[Int] = @mutable.DensePolynomial::zero()
let mut falling = 1
for k = n; k >= 0; k = k - 1 {
buffer.set_coefficient(k, falling)
falling = falling * (if k == 0 { 1 } else { k })
}
buffer.to_immut()
}
test "publish" {
inspect(truncated_exp_numerators(3), content="6 + 6x^1 + 3x^2 + 1x^3")
}
両方の層にまたがるジェネリックなコード
演算レコードと能力トレイトは、ミュータブル型とイミュータブル型で同じ形をしています:
fn[P : @mutable.UnivariatePolynomial] is_linear(p : P) -> Bool {
@mutable.HasDegree::degree(p) == Some(1)
}
test "generic" {
inspect(is_linear(@mutable.DensePolynomial::from_coefficients([0, 3])), content="true")
inspect(is_linear(@immut.DensePolynomial::from_coefficients([1, 0, 2])), content="false")
}
リセットして再利用する
clear(@mutable.Clearable::clear でもあります)はバッファをゼロにリセットし、再利用できるようにします:
test "reuse" {
let buffer = @mutable.DensePolynomial::from_coefficients([4, 5])
@mutable.Clearable::clear(buffer)
assert_true(buffer.is_zero())
buffer.set_coefficient(2, 1)
inspect(buffer, content="1x^2")
}
よくある落とし穴
- 共有されるコンテナ.
let q = pはqを同じコンテナにします。qを変更するとpも変わります。p.copy()を使ってください。 - チェック付きのセッターはない.
set_coefficientは負の次数で中断 (abort) します。 - 導関数 には、イミュータブル型と同様に
Float、Double、BigIntのいずれかの係数が必要です。 - 委譲される演算は変換を伴う.
pow、substitute、karatsubaは係数をイミュータブル型にコピーし、また戻します。演算そのものに比べれば安価ですが、無料ではありません。
次のステップ
- mutable/dense API では、どのメソッドが変更を行うかを示しています。
- mutable/dense の設計 では不変条件とエイリアシングを説明しています。
- immut/dense チュートリアル では演算の数学を扱っています。