Minimal Maxima:修订间差异

来自Ubuntu中文
跳到导航跳到搜索
Dbzhang800留言 | 贡献
无编辑摘要
 
(未显示1个用户的18个中间版本)
第1行: 第1行:
本文作者:Robert Dodier
本文作者:Robert Dodier


翻译:dbzhang800
翻译:dbzhang800, gzmfig


适用版本:
Maxima: http://wiki.ubuntu.org.cn/Maxima
 
文章状态:整理中...  


==  Maxima 是什么?==
==  Maxima 是什么?==
第167行: 第165行:
==求值==
==求值==


== ==
一个符号的值是跟这个符号相关联的一个表达式。每个符号都有一个值如果没有被赋值,一个符号的值就是它自己。(例如,如果没有给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'''
 
(%i1) block (a: 1, b: %pi, c: x + y);
(%o1)                        y+x
(%i2) [a, b, c];
(%o2)                    [1, %pi, y + x]
(%i3) save ("tmp.save", a, b, c);
(%o3)                      tmp.save
(%i4) f (a) := a^b;
(%o4)                          b
(%o4)                f(a) := a
(%i5) f (7);
                            %pi
(%o5)                    7
(%i6) kill (a, b, c);
(%o6)                    done
(%i7) [a, b, c];
(%o7)                  [a, b, c]
 
'''5. 一个单引号会阻止求值。'''
 
(%i1) foo (x, y) := y - x;
(%o1)                  foo(x, y) := y - x
(%i2) block (a: %e, b: 17);
(%o2)                          17
(%i3) foo (a, b);
(%o3)                        17 - %e
(%i4) foo (’a, ’b);
(%o4)                        b-a
(%i5) ’foo (a, b);
(%o5)                      foo(%e, 17)
(%i6) ’(foo (a, b));
(%o6)                      foo(a, b)
 
'''6. 两个单引号使得在对表达式作语法分析的时候就额外求值一次。'''
 
(%i1) diff (sin (x), x);
(%o1)                        cos(x)
(%i2) foo (x) := diff (sin (x), x);
(%o2)              foo(x) := diff(sin(x), x)
(%i3) foo (x) := ’’(diff (sin (x), x));
(%o3)                  foo(x) := cos(x)
 
'''7. ev使得每当它求值的时候才有一次额外的求值。请对比它与两个单引号的不同效果。'''
 
(%i1) block (xx: yy, yy: zz);
(%o1)                          zz
(%i2) [xx, yy];
(%o2)                      [yy, zz]
(%i3) foo (x) := ’’x;
(%o3)                      foo(x) := x
(%i4) foo (xx);
(%o4)                          yy
(%i5) bar (x) := ev (x);
(%o5)                    bar(x) := ev(x)
(%i6) bar (xx);
(%o6)                          zz
 
'''8. apply导致对自变量求值,即便通常情况下仅仅对它们引用。'''
 
(%i1) block (a: aa, b: bb, c: cc);
(%o1)                          cc
(%i2) block (aa: 11, bb: 22, cc: 33);
(%o2)                          33
(%i3) [a, b, c, aa, bb, cc];
(%o3)              [aa, bb, cc, 11, 22, 33]
(%i4) apply (kill, [a, b, c]);
(%o4)                        done
(%i5) [a, b, c, aa, bb, cc];
(%o5)              [aa, bb, cc, aa, bb, cc]
(%i6) kill (a, b, c);
(%o6)                        done
(%i7) [a, b, c, aa, bb, cc];
(%o7)                [a, b, c, aa, bb, cc]
 
'''9. define对函数定义求值。'''
 
(%i1) integrate (sin (a*x), x, 0, %pi);
                          1  cos(%pi a)
(%o1)                    - - ----------
                          a      a
(%i2) foo (x) := integrate (sin (a*x), x, 0, %pi);
(%o2)      foo(x) := integrate(sin(a x), x, 0, %pi)
(%i3) define (foo (x), integrate (sin (a*x), x, 0, %pi));
                              1    cos(%pi a)
(%o3)              foo(x) := - - ----------
                              a        a
 
==化简==
 
对一个表达式求值之后,Maxima试图寻找一个“更简单”的等价的表达式。Maxima会应用几个规则,包括人们对简单的约定。例如,1 + 1化简为2,x + x化简为2x,以及'''sin'''(%'''pi''')化简为0。
 
然而,许多大家熟知的恒等式并不会被自动应用。例如,三角函数中的倍角公式,或者对分式的整理如a/b + c/b → (a + c)/b。有几个函数能够应用这些恒等式。
 
除非明确禁止,化简总是被执行。即使不对表达式求值也会执行化简。
 
'''tellsimpafter'''用来建立用户定义的化简规则。
 
我们来看几个化简的例子。
 
'''1. 单引号阻止求值,但不阻止化简。当全局标志simp是false的时候,阻止化简但不阻止求值。'''
 
(%i1) ’[1 + 1, x + x, x * x, sin (%pi)];
                                    2
(%o1)                    [2, 2 x, x , 0]
(%i2) simp: false$
(%i3) block ([x: 1], x + x);
(%o3)                        1+1
 
'''2. 一些恒等式不能自动应用。expand, ratsimp, trigexpand, edmoivre是能够应用恒等式的一些函数。'''
 
(%i1) (a + b)^2;
                              2
(%o1)                (b + a)
(%i2) expand (%);
                    2          2
(%o2)            b  +2 a b+a
(%i3) a/b + c/b;
                        c  a
(%o3)                  - + -
                        b  b
(%i4) ratsimp (%);
                              c + a
(%o4)                        -----
                                  b
(%i5) sin (2*x);
(%o5)                      sin(2 x)
(%i6) trigexpand (%);
(%o6)                    2 cos(x) sin(x)
(%i7) a * exp (b * %i);
                                  %i b
(%o7)                      a %e
(%i8) demoivre (%);
(%o8)                a (%i sin(b) + cos(b))
 
==apply,map和lambda==
 
'''1. apply构造一个表达式并求值。表达式中的自变量总是被求值(即便在其它情况下不会)。'''
 
(%i1) apply (sin, [x * %pi]);
(%o1)                      sin(%pi x)
(%i2) L: [a, b, c, x, y, z];
(%o2)                  [a, b, c, x, y, z]
(%i3) apply ("+", L);
(%o3)                z+y+x+c+b+a
 
'''2. map对自变量列表中的每一项构造一个表达式并求值。表达式中的自变量总是被求值(即便在其它情况下不会)。'''
 
(%i1) map (foo, [x, y, z]);
(%o1)              [foo(x), foo(y), foo(z)]
(%i2) map ("+", [1, 2, 3], [a, b, c]);
(%o2)                [a + 1, b + 2, c + 3]
(%i3) map (atom, [a, b, c, a + b, a + b + c]);
(%o3)          [true, true, true, false, false]
 
'''3. lambda构造一个lambda表达式(也就是说,一个无名函数)。lambda可以像一个命名函数一样用于一些环境中。lambda不对函数体求值。'''
 
(%i1) f: lambda ([x, y], (x + y)*(x - y));
(%o1)            lambda([x, y], (x + y) (x - y))
(%i2) f (a, b);
(%o2)                    (a - b) (b + a)
(%i3) apply (f, [p, q]);
(%o3)                    (p - q) (q + p)
(%i4) map (f, [1, 2, 3], [a, b, c]);
(%o4)  [(1 - a) (a + 1), (2 - b) (b + 2), (3 - c) (c + 3)]
 
==内置对象类型==
对象表示为一个表达式。像其它表达式一样,对象也由操作符和自变量组成。
 
最重要的内建类型列表、矩阵、集合。
 
===列表===
# 象这样来表示一个列表:[a, b, c]。
# 如果L是一个列表,则L[i]是它的第i个元素。L[1]是第一个元素。
# '''map'''(f, L)应用f 到L的每一个元素上。
# '''apply'''(“ + “, L)对L的所有元素求和。
# '''for''' x '''in''' L '''do''' expr对L中的每一个元素,求值expr。
# '''length'''(L)是L中元素的个数。
 
===矩阵===
# 矩阵象这样定义:'''matrix'''(L1 , . . . , Ln ),其中(L1 , . . . , Ln )是矩阵中每行矩阵元的列表。
# 如果M 是一个矩阵,M [i, j]或者M [i][j]就是它的第(i, j)个矩阵元。M [1, 1]是左上角矩阵元。
# 运算符 . 代表不可交换的乘法。M.L, L.M 和M.N 是不对易乘法,其中L是一个列表,M 和N 是矩阵。
# '''transpose'''(M )是M 的转置。
# '''eigenvalues'''(M )返回M 的本征值。
# '''eigenvectors'''(M )返回M 的本征向量。
# '''length'''(M )返回M 行数。
# '''length'''('''transpose'''(M ))返回M 的列数。
 
===集合===
# Maxima 理解显式定义的有限集合。集合跟列表不同如果要把一种变为另一种,需要显式的转换。
# 集合中元素是a, b, c, . . .时,我们这样指定集合:'''set'''(a, b, c, . . .)
# '''union'''(A, B)是A和B的并集。
# '''intersection'''(A, B)是A和B的交集。
# '''cardinality'''(A)是集合A中元素的个数。
 
==如何. . .==
 
===定义函数===
'''1. 运算符:=定义一个函数,引用函数体。'''
 
在下面的例子中, 每当调用函数时 ,'''diff'''都要重新求值。 自变量被代入到x, 然后求值最后的表达式。 当变量不  是一个符号时, 将发生错误 : 对于'''foo'''(1) Maxima试图对'''diff'''('''sin'''(1)2 , 1)求值。
 
(%i1) foo (x) := diff (sin(x)^2, x);
                                      2
(%o1)              foo(x) := diff(sin (x), x)
(%i2) foo (u);
(%o2)                    2 cos(u) sin(u)
(%i3) foo (1);
Non-variable 2nd argument to diff:
1
#0: foo(x=1)
  -- an error.
 
'''2. define定义一个函数并对函数体求值。'''
 
在这个例子中,只对'''diff'''求值一次(当函数被定义的时候)。'''foo'''(1)现在是有效的。
 
(%i1) define (foo (x), diff (sin(x)^2, x));
(%o1)              foo(x) := 2 cos(x) sin(x)
(%i2) foo (u);
(%o2)                    2 cos(u) sin(u)
(%i3) foo (1);
(%o3)                    2 cos(1) sin(1)
 
===解方程===
 
(%i1) eq_1: a * x + b * y + z = %pi;
(%o1)                  z + b y + a x = %pi
(%i2) eq_2: z - 5*y + x = 0;
(%o2)                    z-5y+x=0
(%i3) s: solve ([eq_1, eq_2], [x, z]);
                (b + 5) y - %pi      (b + 5 a) y - %pi
(%o3)  [[x = - ---------------, z = -----------------]]
                      a - 1                  a - 1
(%i4) length (s);
(%o4)                          1
(%i5) [subst (s[1], eq_1), subst (s[1], eq_2)];
        (b + 5 a) y - %pi  a ((b + 5) y - %pi)
(%o5) [----------------- - ------------------- + b y = %pi,
              a - 1                  a - 1
                    (b + 5 a) y - %pi  (b + 5) y - %pi
                    ----------------- - --------------- - 5 y = 0]
                          a - 1                a - 1
(%i6) ratsimp (%);
(%o6)                  [%pi = %pi, 0 = 0]
 
===积分和微分===
'''integrate'''计算积分和不定积分。
 
(%i1) integrate (1/(1 + x), x, 0, 1);
(%o1)                        log(2)
(%i2) integrate (exp(-u) * sin(u), u, 0, inf);
                                    1
(%o2)                              -
                                    2
(%i3) assume (a > 0);
(%o3)                            [a > 0]
(%i4) integrate (1/(1 + x), x, 0, a);
(%o4)                        log(a + 1)
(%i5) integrate (exp(-a*u) * sin(a*u), [u, 0, inf]);
                                    1
(%o5)                              ---
                                    2 a
(%i6) integrate (exp (sin (t)), [t, 0, %pi]);
                            %pi
                          /
                          [        sin(t)
(%o6)                    I      %e        dt
                          ]
                          /
                            0
(%i7) ’integrate (exp(-u) * sin(u), [u, 0, inf]);
                        inf
                      /
                      [          -u
(%o7)                I      %e    sin(u) du
                      ]
                      /
                        0
 
'''diff'''计算导数。
 
(%i1) diff (sin (y*x));
(%o1)                x cos(x y) del(y) + y cos(x y) del(x)
(%i2) diff (sin (y*x), x);
(%o2)                            y cos(x y)
(%i3) diff (sin (y*x), y);
(%o3)                            x cos(x y)
(%i4) diff (sin (y*x), x, 2);
                                    2
(%o4)                            - y  sin(x y)
(%i5) ’diff (sin (y*x), x, 2);
                                  2
                                d
(%o5)                          --- (sin(x y))
                                  2
                                dx
 
===作图===
'''plot2d'''画2维图。
 
(%i1) plot2d (exp(-u) * sin(u), [u, 0, 2*%pi]);
(%o1)
(%i2) plot2d ([exp(-u), exp(-u) * sin(u)], [u, 0, 2*%pi]);
(%o2)
(%i3) xx: makelist (i/2.5, i, 1, 10);
(%o3) [0.4, 0.8, 1.2, 1.6, 2.0, 2.4, 2.8, 3.2, 3.6, 4.0]
(%i4) yy: map (lambda ([x], exp(-x) * sin(x)), xx);
(%o4) [0.261034921143457, 0.322328869227062, .2807247779692679,
.2018104299334517, .1230600248057767, .0612766372619573,
.0203706503896865, - .0023794587414574, - .0120913057698414,
- 0.013861321214153]
(%i5) plot2d ([discrete, xx, yy]);
(%o5)
(%i6) plot2d([discrete,xx,yy], [gnuplot_curve_styles,["with points"]]);
(%o6)
 
参考 '''plot3d。'''
 
===保存和载入文件===
'''save'''把表达式写入一个文件。
 
(%i1) a: foo - bar;
(%o1)                      foo - bar
(%i2) b: foo^2 * bar;
                                    2
(%o2)                      bar foo
(%i3) save ("my.session", a, b);
(%o3)                      my.session
(%i4) save ("my.session", all);
(%o4)                      my.session
 
'''load'''从文件中读入表达式。
 
(%i1) load ("my.session");
(%o4)                      my.session
(%i5) a;
(%o5)                      foo - bar
(%i6) b;
                                    2
(%o6)                      bar foo
 
参考'''stringout'''的'''batch'''。
 
==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

2009年12月4日 (五) 15:26的最新版本

本文作者:Robert Dodier

翻译:dbzhang800, gzmfig

Maxima: http://wiki.ubuntu.org.cn/Maxima

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。)数字和字符串的值就是它们本身。

一个非原子表达式基本上按下面方式求值。

  1. 对表达式中操作符的每一个自变量求值。
  2. 如果一个操作符是一个可以调用的函数,那么就调用这个函数,而表达式的值就是该函数 的返回值。

在几种情况下,上述求值过程会发生变化。其中一些变化将导致较少的求值运算:

  1. 有一些函数并不对它们一部分或者全部自变量进行求值,或者相反修改了它们自变量的值。
  2. 一个单引号 会阻止求值。
    1. 'a的求值结果为a。a的任何其它值都被忽略掉。
    2. 对 f (a1, ..., an )的求值得到时f (ev(a1), ..., ev(an))。也就是说,自变量都被求值了,而f 却没有被调用。
    3. '(...)将阻止(...)中任何表达式的求值。

另外一些变化会导致更多的求值运算。

  1. 两个单引号 a使得在对表达式a作语法分析的时候就额外求值一次。
  2. ev(a)使得每次对ev(a)求值的时候都额外对a求值。
  3. apply(f, [a1, ..., an ])将会对自变量(a1, ..., an )求值,即使f 只是一般地引用它们。
  4. 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

(%i1) block (a: 1, b: %pi, c: x + y);
(%o1)                         y+x
(%i2) [a, b, c];
(%o2)                    [1, %pi, y + x]
(%i3) save ("tmp.save", a, b, c);
(%o3)                       tmp.save
(%i4) f (a) := a^b;
(%o4)                          b
(%o4)                 f(a) := a
(%i5) f (7);
                           %pi
(%o5)                    7
(%i6) kill (a, b, c);
(%o6)                    done
(%i7) [a, b, c];
(%o7)                  [a, b, c]

5. 一个单引号会阻止求值。

(%i1) foo (x, y) := y - x;
(%o1)                  foo(x, y) := y - x
(%i2) block (a: %e, b: 17);
(%o2)                          17
(%i3) foo (a, b);
(%o3)                        17 - %e
(%i4) foo (’a, ’b);
(%o4)                         b-a
(%i5) ’foo (a, b);
(%o5)                      foo(%e, 17)
(%i6) ’(foo (a, b));
(%o6)                       foo(a, b)

6. 两个单引号使得在对表达式作语法分析的时候就额外求值一次。

(%i1) diff (sin (x), x);
(%o1)                        cos(x)
(%i2) foo (x) := diff (sin (x), x);
(%o2)               foo(x) := diff(sin(x), x)
(%i3) foo (x) := ’’(diff (sin (x), x));
(%o3)                   foo(x) := cos(x)

7. ev使得每当它求值的时候才有一次额外的求值。请对比它与两个单引号的不同效果。

(%i1) block (xx: yy, yy: zz);
(%o1)                          zz
(%i2) [xx, yy];
(%o2)                       [yy, zz]
(%i3) foo (x) := ’’x;
(%o3)                      foo(x) := x
(%i4) foo (xx);
(%o4)                          yy
(%i5) bar (x) := ev (x);
(%o5)                    bar(x) := ev(x)
(%i6) bar (xx);
(%o6)                          zz

8. apply导致对自变量求值,即便通常情况下仅仅对它们引用。

(%i1) block (a: aa, b: bb, c: cc);
(%o1)                          cc
(%i2) block (aa: 11, bb: 22, cc: 33);
(%o2)                          33
(%i3) [a, b, c, aa, bb, cc];
(%o3)               [aa, bb, cc, 11, 22, 33]
(%i4) apply (kill, [a, b, c]);
(%o4)                         done
(%i5) [a, b, c, aa, bb, cc];
(%o5)               [aa, bb, cc, aa, bb, cc]
(%i6) kill (a, b, c);
(%o6)                         done
(%i7) [a, b, c, aa, bb, cc];
(%o7)                 [a, b, c, aa, bb, cc]

9. define对函数定义求值。

(%i1) integrate (sin (a*x), x, 0, %pi);
                         1   cos(%pi a)
(%o1)                    - - ----------
                         a       a
(%i2) foo (x) := integrate (sin (a*x), x, 0, %pi);
(%o2)       foo(x) := integrate(sin(a x), x, 0, %pi)
(%i3) define (foo (x), integrate (sin (a*x), x, 0, %pi));
                              1    cos(%pi a)
(%o3)               foo(x) := - - ----------
                              a        a

化简

对一个表达式求值之后,Maxima试图寻找一个“更简单”的等价的表达式。Maxima会应用几个规则,包括人们对简单的约定。例如,1 + 1化简为2,x + x化简为2x,以及sin(%pi)化简为0。

然而,许多大家熟知的恒等式并不会被自动应用。例如,三角函数中的倍角公式,或者对分式的整理如a/b + c/b → (a + c)/b。有几个函数能够应用这些恒等式。

除非明确禁止,化简总是被执行。即使不对表达式求值也会执行化简。

tellsimpafter用来建立用户定义的化简规则。

我们来看几个化简的例子。

1. 单引号阻止求值,但不阻止化简。当全局标志simp是false的时候,阻止化简但不阻止求值。

(%i1) ’[1 + 1, x + x, x * x, sin (%pi)];
                                   2
(%o1)                    [2, 2 x, x , 0]
(%i2) simp: false$
(%i3) block ([x: 1], x + x);
(%o3)                         1+1

2. 一些恒等式不能自动应用。expand, ratsimp, trigexpand, edmoivre是能够应用恒等式的一些函数。

(%i1) (a + b)^2;
                              2
(%o1)                 (b + a)
(%i2) expand (%);
                    2           2
(%o2)             b   +2 a b+a
(%i3) a/b + c/b;
                        c   a
(%o3)                   - + -
                        b   b
(%i4) ratsimp (%);
                              c + a
(%o4)                         -----
                                 b
(%i5) sin (2*x);
(%o5)                       sin(2 x)
(%i6) trigexpand (%);
(%o6)                    2 cos(x) sin(x)
(%i7) a * exp (b * %i);
                                 %i b
(%o7)                       a %e
(%i8) demoivre (%);
(%o8)                a (%i sin(b) + cos(b))

apply,map和lambda

1. apply构造一个表达式并求值。表达式中的自变量总是被求值(即便在其它情况下不会)。

(%i1) apply (sin, [x * %pi]);
(%o1)                      sin(%pi x)
(%i2) L: [a, b, c, x, y, z];
(%o2)                  [a, b, c, x, y, z]
(%i3) apply ("+", L);
(%o3)                 z+y+x+c+b+a

2. map对自变量列表中的每一项构造一个表达式并求值。表达式中的自变量总是被求值(即便在其它情况下不会)。

(%i1) map (foo, [x, y, z]);
(%o1)               [foo(x), foo(y), foo(z)]
(%i2) map ("+", [1, 2, 3], [a, b, c]);
(%o2)                 [a + 1, b + 2, c + 3]
(%i3) map (atom, [a, b, c, a + b, a + b + c]);
(%o3)           [true, true, true, false, false]

3. lambda构造一个lambda表达式(也就是说,一个无名函数)。lambda可以像一个命名函数一样用于一些环境中。lambda不对函数体求值。

(%i1) f: lambda ([x, y], (x + y)*(x - y));
(%o1)            lambda([x, y], (x + y) (x - y))
(%i2) f (a, b);
(%o2)                    (a - b) (b + a)
(%i3) apply (f, [p, q]);
(%o3)                    (p - q) (q + p)
(%i4) map (f, [1, 2, 3], [a, b, c]);
(%o4)  [(1 - a) (a + 1), (2 - b) (b + 2), (3 - c) (c + 3)]

内置对象类型

对象表示为一个表达式。像其它表达式一样,对象也由操作符和自变量组成。

最重要的内建类型列表、矩阵、集合。

列表

  1. 象这样来表示一个列表:[a, b, c]。
  2. 如果L是一个列表,则L[i]是它的第i个元素。L[1]是第一个元素。
  3. map(f, L)应用f 到L的每一个元素上。
  4. apply(“ + “, L)对L的所有元素求和。
  5. for x in L do expr对L中的每一个元素,求值expr。
  6. length(L)是L中元素的个数。

矩阵

  1. 矩阵象这样定义:matrix(L1 , . . . , Ln ),其中(L1 , . . . , Ln )是矩阵中每行矩阵元的列表。
  2. 如果M 是一个矩阵,M [i, j]或者M [i][j]就是它的第(i, j)个矩阵元。M [1, 1]是左上角矩阵元。
  3. 运算符 . 代表不可交换的乘法。M.L, L.M 和M.N 是不对易乘法,其中L是一个列表,M 和N 是矩阵。
  4. transpose(M )是M 的转置。
  5. eigenvalues(M )返回M 的本征值。
  6. eigenvectors(M )返回M 的本征向量。
  7. length(M )返回M 行数。
  8. length(transpose(M ))返回M 的列数。

集合

  1. Maxima 理解显式定义的有限集合。集合跟列表不同如果要把一种变为另一种,需要显式的转换。
  2. 集合中元素是a, b, c, . . .时,我们这样指定集合:set(a, b, c, . . .)
  3. union(A, B)是A和B的并集。
  4. intersection(A, B)是A和B的交集。
  5. cardinality(A)是集合A中元素的个数。

如何. . .

定义函数

1. 运算符:=定义一个函数,引用函数体。

在下面的例子中, 每当调用函数时 ,diff都要重新求值。 自变量被代入到x, 然后求值最后的表达式。 当变量不 是一个符号时, 将发生错误 : 对于foo(1) Maxima试图对diff(sin(1)2 , 1)求值。

(%i1) foo (x) := diff (sin(x)^2, x);
                                     2
(%o1)              foo(x) := diff(sin (x), x)
(%i2) foo (u);
(%o2)                    2 cos(u) sin(u)
(%i3) foo (1);
Non-variable 2nd argument to diff:
1
#0: foo(x=1)
  -- an error.

2. define定义一个函数并对函数体求值。

在这个例子中,只对diff求值一次(当函数被定义的时候)。foo(1)现在是有效的。

(%i1) define (foo (x), diff (sin(x)^2, x));
(%o1)               foo(x) := 2 cos(x) sin(x)
(%i2) foo (u);
(%o2)                    2 cos(u) sin(u)
(%i3) foo (1);
(%o3)                    2 cos(1) sin(1)

解方程

(%i1) eq_1: a * x + b * y + z = %pi;
(%o1)                  z + b y + a x = %pi
(%i2) eq_2: z - 5*y + x = 0;
(%o2)                    z-5y+x=0
(%i3) s: solve ([eq_1, eq_2], [x, z]);
                (b + 5) y - %pi      (b + 5 a) y - %pi
(%o3)   x = - ---------------, z = -----------------
                     a - 1                   a - 1
(%i4) length (s);
(%o4)                           1
(%i5) [subst (s[1], eq_1), subst (s[1], eq_2)];
       (b + 5 a) y - %pi   a ((b + 5) y - %pi)
(%o5) [----------------- - ------------------- + b y = %pi,
             a - 1                  a - 1
                   (b + 5 a) y - %pi   (b + 5) y - %pi
                   ----------------- - --------------- - 5 y = 0]
                         a - 1                a - 1
(%i6) ratsimp (%);
(%o6)                  [%pi = %pi, 0 = 0]

积分和微分

integrate计算积分和不定积分。

(%i1) integrate (1/(1 + x), x, 0, 1);
(%o1)                        log(2)
(%i2) integrate (exp(-u) * sin(u), u, 0, inf);
                                    1
(%o2)                               -
                                    2
(%i3) assume (a > 0);
(%o3)                            [a > 0]
(%i4) integrate (1/(1 + x), x, 0, a);
(%o4)                         log(a + 1)
(%i5) integrate (exp(-a*u) * sin(a*u), [u, 0, inf]);
                                    1
(%o5)                              ---
                                   2 a
(%i6) integrate (exp (sin (t)), [t, 0, %pi]);
                            %pi
                          /
                          [        sin(t)
(%o6)                     I      %e        dt
                          ]
                          /
                            0
(%i7) ’integrate (exp(-u) * sin(u), [u, 0, inf]);
                        inf
                      /
                      [          -u
(%o7)                 I       %e     sin(u) du
                      ]
                      /
                        0

diff计算导数。

(%i1) diff (sin (y*x));
(%o1)                x cos(x y) del(y) + y cos(x y) del(x)
(%i2) diff (sin (y*x), x);
(%o2)                             y cos(x y)
(%i3) diff (sin (y*x), y);
(%o3)                             x cos(x y)
(%i4) diff (sin (y*x), x, 2);
                                    2
(%o4)                            - y  sin(x y)
(%i5) ’diff (sin (y*x), x, 2);
                                 2
                                d
(%o5)                           --- (sin(x y))
                                  2
                                dx

作图

plot2d画2维图。

(%i1) plot2d (exp(-u) * sin(u), [u, 0, 2*%pi]);
(%o1)
(%i2) plot2d ([exp(-u), exp(-u) * sin(u)], [u, 0, 2*%pi]);
(%o2)
(%i3) xx: makelist (i/2.5, i, 1, 10);
(%o3) [0.4, 0.8, 1.2, 1.6, 2.0, 2.4, 2.8, 3.2, 3.6, 4.0]
(%i4) yy: map (lambda ([x], exp(-x) * sin(x)), xx);
(%o4) [0.261034921143457, 0.322328869227062, .2807247779692679,
.2018104299334517, .1230600248057767, .0612766372619573,
.0203706503896865, - .0023794587414574, - .0120913057698414,
- 0.013861321214153]
(%i5) plot2d ([discrete, xx, yy]);
(%o5)
(%i6) plot2d([discrete,xx,yy], [gnuplot_curve_styles,["with points"]]);
(%o6)

参考 plot3d。

保存和载入文件

save把表达式写入一个文件。

(%i1) a: foo - bar;
(%o1)                       foo - bar
(%i2) b: foo^2 * bar;
                                    2
(%o2)                       bar foo
(%i3) save ("my.session", a, b);
(%o3)                      my.session
(%i4) save ("my.session", all);
(%o4)                      my.session

load从文件中读入表达式。

(%i1) load ("my.session");
(%o4)                      my.session
(%i5) a;
(%o5)                       foo - bar
(%i6) b;
                                    2
(%o6)                       bar foo

参考stringoutbatch

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