Minimal Maxima
本文作者:Robert Dodier
翻译:dbzhang800
适用版本:
文章状态:整理中...
Maxima 是什么?
Maxima 是一个处理数学表达式的系统,例如x + y, sin(a + bπ)以及u · v − v · u
Maxima 并不太关心表达式的含义,一个表达式是否有意义由使用者来判断。
有时你想给未知数赋值并且要计算表达式的值。Maxima 很善长做这件事。但是Maxima 也很 善长推迟赋值;你可以先对表达式做一番处理,然后才给(也许永远不给)未知数赋值。
我们先看几个例子:
1.我想计算球的体积。
(%i1) V: 4/3 * %pi * r^3; 3 4 %pi r (%o1) -------- 3
2.半径是10。
(%i2) r: 10; (%o2) 10
3.V的值和前面一样;在我们下令以前,Maxima不会改变V的值。
(%i3) V; 3 4 %pi r (%o3) -------- 3
4.Maxima,重新计算一下V。
(%i4) ’’V; 4000 %pi (%o4) -------- 3
5.我想看到一个具体的数值而不是一个表达式。
(%i5) ’’V, numer; (%o5) 4188.79020478639
表达式
Maxima 中所有的东西都是表达式,包括数学表达式、对象、程序结构等。表达式要么是原子,要么是操作符与它的自变量。
原子是一个符号(名字),一个用引号括起来的字符串,或者是一个数(整数或浮点数)。
所有非原子表达式都表示为op(a1 , . . . , an ),其中op是操作符的名字,a1 , . . . , an 是自变。(表达式的显示形式可能不太一致,但内部表示都是一样的。)表达式中的自变量可以是原子或非原子表达式。
数学表达式中含有一个操作符,比如+ − ∗/ < = > 或者一个函数,比如sin(x),bessel_j(n, x)。在这种情况下操作符就是函数。
对象在Maxima 中是表达式。列表[a1 , . . . , an ]就是表达式list(a1 , . . . , an )。矩阵就是表达式
matrix(list(a1,1 , . . . , a1,n ), . . . ,list(am,1 , . . . , am,n ))
程 序 构 造 是 表 达 式 。 一 个 代 码 块block(a1 , . . . , an )是 操 作 符 为block自 变 量 为a1 , . . . , an 的表 达 式 。 条 件 语 句if a then b elseif c then d是 表 达 式if(a, b, c, d)。 循 环for a in L do S是 类 似于do(a, L, S)的表达式。
Maxima 函数op返回一个非原子表达式的操作符。函数args返回非原子表达式的自变量。函数atom判断一个表达式是不是原子。
让我们看几个例子。
1. 符号、字符串和数字都是原子。为了一次能看到它们,我把几个例子凑到时了一个列表里面。
(%i2) [a, foo, foo_bar, "Hello, world!", 42, 17.29]; (%o2) [a, foo, foo_bar, Hello, world!, 42, 17.29]
2. 数学表达式。
(%i1) [a + b + c, a * b * c, foo = bar, a*b < c*d]; (%o1) [c + b + a, a b c, foo = bar, a b < c d]
3. 列表和矩阵。列表和矩阵的元可以是任何表达式,甚至是其它列表或矩阵。
(%i1) L: [a, b, c, %pi, %e, 1729, 1/(a*d - b*c)]; 1 (%o1) [a, b, c, %pi, %e, 1729, ---------] a d - b c (%i2) L2: [a, b, [c, %pi, [%e, 1729], 1/(a*d - b*c)]]; 1 (%o2) [a, b, [c, %pi, [%e, 1729], ---------]] a d - b c (%i3) L [7]; 1 (%o3) --------- a d - b c (%i4) L2 [3]; 1 (%o4) [c, %pi, [%e, 1729], ---------] a d - b c (%i5) M: matrix ([%pi, 17], [29, %e]); [ %pi 17 ] (%o5) [ ] [ 29 %e ] (%i6) M2: matrix([[%pi,17],a*d - b*c],[matrix([1,a],[b,7]),%e]); [ [%pi, 17] a d - b c ] [ ] (%o6) [ [ 1 a ] ] [ [ ] %e ] [ [ b 7 ] ] (%i7) M [2][1]; (%o7) 29 (%i8) M2 [2][1]; [ 1 a ] (%o8) [ ] [ b 7 ]
4. 程序构造是表达式。x : y表示把y赋给x;这个赋值表达式的值是y。block把几个表达式组合为一个块,然后依次求各个表达式的值;最后一个表达式的值即是块的值。
(%o1) 25 (%i2) [a, b]; (%o2) [42, 17] (%i3) block ([a], a: 42, a^2 - 1600) + block ([b], b: 5, %pi^b); 5 (%o3) %pi + 164 (%i4) (if a > 1 then %pi else %e) + (if b < 0 then 1/2 else 1/7); 1 (%o4) %pi + - 7
5. op返回操作符,args返回自变量,atom判断一个表达式是不是原子。
(%i1) op (p + q); (%o1) + (%i2) op (p + q > p*q); (%o2) > (%i3) op (sin (p + q)); (%o3) sin (%i4) op (foo (p, q)); (%o4) foo (%i5) op (foo (p, q) := p - q); (%o5) := (%i6) args (p + q); (%o6) [q, p] (%i7) args (p + q > p*q); (%o7) [q + p, p q] (%i8) args (sin (p + q)); (%o8) [q + p] (%i9) args (foo (p, q)); (%o9) [p, - q] (%i10) args (foo (p, q) := p - q); (%o10) [foo(p, q), p - q] (%i11) atom (p); (%o11) true (%i12) atom (p + q); (%o12) false (%i13) atom (sin (p + q)); (%o13) false
6. 程序构造的操作符和自变量。单引号告诉Maxima构造表达式,但不求值。这点我们后面会涉及。
(%o0) done (%i1) op ('(block ([a], a: 42, a^2 - 1600))); (%o1) block (%i2) op (' (if p > q then p else q)); (%o2) if (%i3) op ('(for x in L do print (x))); (%o3) mdoin (%i4) args (' (block ([a], a: 42, a^2 -1600))); 2 (%o4) [[a], a : 42, a - 1600] (%i5) args (' (if p > q then p else q)); (%o5) [p > q, p, true, q] (%i6) args (' (for x in L do print (x))); (%o6) [x, L, false, false, false, false, print(x)]
求值
一个符号的值是跟这个符号相关联的一个表达式。每个符号都有一个值如果没有被赋值,一个符号的值就是它自己。(例如,如果没有给x赋过值,那么它的值就是x。)数字和字符串的值就是它们本身。
一个非原子表达式基本上按下面方式求值。
- 对表达式中操作符的每一个自变量求值。
- 如果一个操作符是一个可以调用的函数,那么就调用这个函数,而表达式的值就是该函数 的返回值。
在几种情况下,上述求值过程会发生变化。其中一些变化将导致较少的求值运算:
- 有一些函数并不对它们一部分或者全部自变量进行求值,或者相反修改了它们自变量的值。
- 一个单引号 会阻止求值。
- 'a的求值结果为a。a的任何其它值都被忽略掉。
- 对 f (a1, ..., an )的求值得到时f (ev(a1), ..., ev(an))。也就是说,自变量都被求值了,而f 却没有被调用。
- '(...)将阻止(...)中任何表达式的求值。
另外一些变化会导致更多的求值运算。
- 两个单引号 a使得在对表达式a作语法分析的时候就额外求值一次。
- ev(a)使得每次对ev(a)求值的时候都额外对a求值。
- apply(f, [a1, ..., an ])将会对自变量(a1, ..., an )求值,即使f 只是一般地引用它们。
- define像:= 一样构造一个函数定义,但是define会对函数体求值而:=则仅仅引用函数体。
下面看一些表达式是怎样被求值的。
1. 如果没有被赋值,那么符号的求值结果就是它们自身。
(%i1) block (a: 1, b: 2, e: 5); (%o1) 5 (%i2) [a, b, c, d, e]; (%o2) [1, 2, c, d, 5]
2. 操作符的自变量一般求值(除非以某种方法阻止求值)。
(%i1) block (x: %pi, y: %e); (%o1) %e (%i2) sin (x + y); (%o2) - sin(%e) (%i3) x > y; (%o3) %pi > %e (%i4) x!; (%o4) %pi!
3. 如果操作符对应于一个可调用函数,那么调用这个函数(除非被阻止)。否则,求值会得到具有同一个操作符的另一个表达式。
(%i1) foo (p, q) := p - q; (%o1) foo(p, q) := p - q (%i2) p: %phi; (%o2) %phi (%i3) foo (p, q); (%o3) %phi - q (%i4) bar (p, q); (%o4) bar(%phi, q)
4. 一些函数引用它们的自变量。例如:save , :=, kill
5. 一个单引号会阻止求值。
6. 两个单引号使得在对表达式作语法分析的时候就额外求值一次。
7. ev使得每当它求值的时候才有一次额外的求值。请对比它与两个单引号的不同效果。
8. apply导致对自变量求值,即便通常情况下仅仅对它们引用。
9. define对函数定义求值。
化简
apply,map和lambda
内置对象类型
如何. . .
Maxima 编程
Maxima有一个包含了全部Maxima符号的命名空间。没有办法建立其它命名空间。
所有变量都是全局的,除非它们出现在局部变量的声明中。函数、lambda表达式、以及块中可以有局部变量。
一个变量的值总是最近一次赋值给它的那个,要么通过显式赋值,要么是通过对块、函数、或者lambda表达式中的局部变量赋值。这个策略称为动态域。
如果一个变量是函数、lambda表达式、或者块中的局部变量,它的值是局部的,但它的其它属性(象由declare建立的)是全局的。函数local把一个变量的全部属性变为局部的。
一个函数定义默认是全局的,即便它出现在函数、lambda表达式、或者块中。local(f),f(x):=...产生一个局部函数定义。
trace(foo)会让Maxima在进入和退出函数foo时打印一条消息。
下面看一些Maxima编程的例子。
1. 所有变量都是全局的,除非它们出现在局部变量的声明中。函数、lambda表达式、以及块中可以有局部变量。
(%i1) (x: 42, y:1729, z:foo*bar); (%o1) bar foo (%i2) f(x, y) := x * y * z; (%o2) f(x, y) := x y z (%i3) f(aa, bb); (%o3) aa bar bb foo (%i4) lambda([x, z], (x - z)/y); x - z (%o4) lambda([x, z], -----) y (%i5) apply(%, [uu, vv]); uu - vv (%o5) ------- 1729 (%i6) block([y, z], y: 65536, [x, y, z]); (%o6) [42, 65536, z] (%i7)
2. 一个变量的值总是最近一次赋给它的那个,要么通过显式赋值,要么是通过对块、函数、或者lambda表达式中的局部变量赋值。
(%i1) foo(y) := x - y; (%o1) foo(y) := x - y (%i2) x: 1729; (%o2) 1729 (%i3) foo(%pi); (%o3) 1729 - %pi (%i4) bar(x) := foo(%e); (%o4) bar(x) := foo(%e) (%i5) bar(42); (%o5) 42 - %e (%i6)
Lisp 和Maxima
构造 :lisp expr告诉Lisp解释器去计算expr。这个构造可以在命令提示符中被识别,也可以 在batch而不是load处理的文件中被识别。
Maxima的符号foo对应于Lisp中的符号$foo,Lisp中的符号foo对应于Maxima中的符号?foo 。
:lisp (defun $foo(a)(. . .)定义了一个计算它的参数的Lisp函数。在Maxima中,这个函数通过foo(a)被调用。
:lisp (defmspec $foo(e)(. . .)定义了一个引用它的参数的Lisp函数。在Maxima中,这个函数通过foo(a)被调用。$foo的参数是(cdr e),而且(caar e)总是$foo自身。
在Lisp中,构造(mfuncall ’$foo a1 . . . an ) 调用Maxima中定义的foo函数。
下面让我们从Maxima进入Lisp,或者反过来说。
1. 构造:lisp expr告诉Lisp解释器去计算expr。
(%i1) (aa + bb)^2; 2 (%o1) (bb + aa) (%i2) :lisp $% ((MEXPT SIMP) ((MPLUS SIMP) $AA $BB) 2)
2. :lisp (defun $foo(a)(. . .)定义了一个计算它的参数的Lisp函数foo。
(%i1) :lisp (defun $foo (a b) ‘((mplus) ((mtimes) ,a ,b) $%pi)) $FOO (%i1) (p: x + y, q: x - y); (%o1) x - y (%i2) foo (p, q); (%o2) (x - y) (y + x) + %pi
3. :lisp (defmspec $foo(e)(. . .)定义了一个引用它的参数的Lisp函数foo。
(%i1) :lisp(defmspec $bar(e)(let((a(cdr e)))‘((mplus)((mtimes),@a) #<CLOSURE LAMBDA(E)(LET((A(CDR E)))‘((MPLUS)((MTIMES),@A)$%PI))> (%i1) bar (p, q); (%o1) p q + %pi (%i2) bar (’’p, ’’q); (%o2) p q + %pi
4. 在Lisp中,构造(mfuncall ’$foo a1 . . . an ) 调用Maxima中定义的foo函数。
(%i1) blurf (x) := x^2; 2 (%o1) blurf(x) := x (%i2) :lisp (displa (mfuncall '$blurf' ((mplus) $grotz $mumble))) 2 (mumble + grotz) NIL