core 教程

本教程使用泛型 Complex[T] 类型进行复数运算。你将构造并打印复数,在 Double 和整数上用它们进行计算,编写通过 luna-generic trait 接受复数的泛型代码,并原地更新值。其背后的代数原理见核心设计。

快速开始

moon add Luna-Flow/luna-complex@0.2.0
import {
  "Luna-Flow/luna-complex" @complex,
}
fn main {
  let z = @complex.Complex::new(1.0, 2.0)
  let w = @complex.Complex::new(3.0, -1.0)
  println("z + w = \{z + w}")
  println("z * w = \{z * w}")
  println("z / w = \{z / w}")
}
z + w = 4 + 1i
z * w = 5 + 5i
z / w = 0.1 + 0.7000000000000001i

日常任务

共轭与模平方

zzˉ=∣z∣2z\bar z = |z|^2 是实数:

fn main {
  let z = @complex.Complex::new(3.0, 4.0)
  let n = z * z.conjugate()
  println("z * conj(z) = \{n}")
  println("1 / z = \{z.inv()}")
}
z * conj(z) = 25 + 0i
1 / z = 0.12 + -0.16i

高斯整数

在 Int 上,Complex[Int] 就是高斯整数环 Z[i]\mathbb Z[i]。除法以外的一切都能工作,且结果是精确的:

fn main {
  let a = @complex.Complex::new(2, 1)
  let b = @complex.Complex::new(2, -1)
  println("(2 + i)(2 - i) = \{a * b}")
  let mut p = @complex.Complex::one()
  for _ in 0..<4 {
    p = p * @complex.Complex::new(1, 1)
  }
  println("(1 + i)^4 = \{p}")
}
(2 + i)(2 - i) = 5 + 0i
(1 + i)^4 = -4 + 0i

5=(2+i)(2−i)5 = (2 + i)(2 - i) 表明 55 在 Z[i]\mathbb Z[i] 中不是素数。

环上的泛型代码

针对 luna-generic trait 编写的代码可以接受复数。下面用 Horner 方法求 p(x)=x2+1p(x) = x^2 + 1 的值:

fn[T : @lg.Ring] horner(coefficients : Array[T], x : T) -> T {
  let mut acc : T = @lg.Zero::zero()
  for i = coefficients.length() - 1; i >= 0; i = i - 1 {
    acc = acc * x + coefficients[i]
  }
  acc
}

fn main {
  let one : @complex.Complex[Double] = @complex.Complex::one()
  let zero : @complex.Complex[Double] = @complex.Complex::zero()
  let i = @complex.Complex::new(0.0, 1.0)
  println("p(i) = \{horner([one, zero, one], i)}")
  println("p(2) = \{horner([1.0, 0.0, 1.0], 2.0)}")
}
p(i) = 0 + 0i
p(2) = 5

原地更新

set、set_re 和 set_im 会覆盖一个值。所有绑定到它的变量都会看到这一变化:

fn main {
  let acc = @complex.Complex::new(0.0, 0.0)
  let shared = acc
  for k in 1..=3 {
    let kd = k.to_double()
    acc.set(acc.re + kd, acc.im - kd)
  }
  println("acc    = \{acc}")
  println("shared = \{shared}")
}
acc    = 6 + -6i
shared = 6 + -6i

进阶

嵌套复数

Complex[T] 适用于任何具备所需 trait 的 T,包括 Complex[Double] 本身:

fn main {
  let a = @complex.Complex::new(1.0, 2.0)
  let b = @complex.Complex::new(-0.5, 0.25)
  let z = @complex.Complex::new(a, b)
  let w = z * z.inv()
  println("z * z^-1 = (\{w.re}) + (\{w.im})j")
}
z * z^-1 = (1.0000000000000002 + 5.204170427930421e-17i) + (0 + 0i)j

乘积在舍入误差范围内等于 11,因为这个 zz 的模平方 a2+b2a^2 + b^2 可逆。一般而言,Complex[Complex[Double]] 不是域;参见下面的常见陷阱。

解析函数

核心中没有 sqrt、exp 或 sin。对于 Complex[Double],请导入 Luna-Flow/luna-complex/float_backend;float_backend 教程介绍了用法。

常见陷阱

  • 除以零会中止。 z / w 和 w.inv() 对 c2+d2c^2 + d^2 调用 Inverse::inv;对于 Double,当该值为零时会中止,当 ∣w∣≲10−162|w| \lesssim 10^{-162} 导致下溢时也是如此。稳健的除法请使用 float_backend 中的 @fb.div。
  • 大数值在除法中上溢。 当 ∣w∣≳10154|w| \gtrsim 10^{154} 时 c2+d2c^2 + d^2 上溢。
  • 共享的修改。 let shared = z 不会复制;setter 会改变每一个别名。
  • 文本形式。 负虚部打印为 + -2i;需要其他形式时,请使用 debug_inspect 或自行格式化。
  • 嵌套类型不是域。 在 Complex[Complex[Double]] 中,1+i j1 + i\,j 这样的值模平方为零,inv 会中止。

后续步骤