mutable/dense 教程
本教程展示如何原地构造和更新单变量多项式:逐个设置系数、把和与积累加到同一个容器中、保留快照,并把结果交还给不可变代码。
快速开始
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 本身中 的系数。
日常任务
逐个填入系数
从零开始,设置你已知的系数;数组会按需增长:
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)")
}
把首系数设为零会降低次数,因为多项式始终保持规范形式。
累加到同一个容器中
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")
}
跨两层的泛型代码
操作记录和能力 trait 对可变类型和不可变类型具有相同的形态:
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()。 - 没有带检查的 setter。
set_coefficient遇到负的幂次时中止(abort)。 - 求导需要
Float、Double或BigInt系数,与不可变类型相同。 - 委托的操作会进行转换。
pow、substitute和karatsuba会把系数复制到不可变类型再复制回来;与操作本身相比这很廉价,但并非没有代价。
后续步骤
- mutable/dense API 标明了哪些方法会修改。
- mutable/dense 设计 解释了不变量与别名问题。
- immut/dense 教程 讲解了各操作的数学原理。