| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
7.1 関数定義について 7.2 関数 7.3 マクロ 7.4 最適化 7.5 関数定義に関する諸定義
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
例えば、
F(X):=SIN(X) |
例えば、 lambda([i,j], ... ) |
F(I,J):=BLOCK([], ... ); |
例えば、 MAP(LAMBDA([I],I+1),L) |
(*訳者注: 上記の例の実行結果を以下に示す。 (C1) map(lambda([i],i+1),[1,2,3]); (D1) [2, 3, 4] この様にlambda([i],i+1)で引数iに1を加える無名関数を構築し、それをmap関数で リスト[1,2,3]に作用させている。lambda関数に関してはLISPの本を参照されたい。 *) |
引数の個数が可変な関数では、最後の引数を特別な引数リストとして割当てて定義して も良い。
(C8) f([u]):=u; (C9) f(1,2,3,4); (D9) [1, 2, 3, 4] (C11) f(a,b,[u]):=[a,b,u]; (C12) f(1,2,3,4,5,6); (D12) [1, 2, [3, 4, 5, 6]] |
関数の右手側は式である。それ故、式の列が欲しければ次の様にする。
f(x):=(expr1,expr2,....,exprn); |
(* 訳者注:
(C1) f(x):=(2*x,3*x,10,x^3);
(D1) f(x) := (2 x, 3 x, 10, x )
(C2) f(1);
(D2) 1
(C3) f(10);
(D3) 1000
(C4) f(x);
3
(D4) x
(C5) f(2*x);
3
(D5) 8 x
(C6)
(C6) f(0);
(D6) 0
(C7) f(x):=(1,2,3,4,5,6,7,8,9,10);
(D7) f(x) := (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
(C8) f(a);
(D8) 10
(C9) f([1,2,3,4]);
(D9) 10
(C10) map(f,[1,2,3,4]);
(D10) [10, 10, 10, 10]
*)
|
返却値が欲しければ、blockとreturn
を用いなければならない。
block([],expr1,...,if(a>10) then return(a),...exprn) |
returnによる値の返却は最後の式よりも先に処理されても良い。
BLOCKの中の最初にある[]は、変数リストや[a:3,b,c:[]と云った変数
の指定を含んでいて良い。ここで、[a:3,b,c:[]の3個の変数a、
bとcはBLOCKの内部か、BLOCKの内部から呼び出された
内部関数でプログラムが実行される限り、大域変数の値を参照せずにそれらの特定の
値を持つ。
これは動的束縛と呼ばれる。何故なら、BLOCKの開始から終了まで変数が存在し 続ける為である。
一旦、BLOCKから値を戻したり、外に放り出しても、(任意の)変数の古い値は
保たれている。この方法で値を守る事は確実に良い考えである。そのBLOCK変数の
割当ては並行して行われる事に注意する。これは、上のc:aでcの値は、
そのBLOCKに入った時点で値aとなっている。しかし、以前はaが束縛
されていた。そこで、以下の様に実行する。
block([a:a],expr1,... a:a+3,...exprn) |
aの外部の値が変更される事を妨げるが、その値が何であるかを
参照する事が可能である。そして、代入の右辺は任意の束縛が生じる前に属する文脈
(context)内で評価される。
BLOCK([x],..を使うと、Xをそれ自身の値とみなし、あたかも新規に立ち上げ
たMAXIMAセッションに入った状態になる。
関数の実際の引数はBLOCKの中の変数と丁度同じ方法で扱われる。
つまり、 f(x):=(expr1,...exprn); と f(1); |
block([x:1],expr1,...exprn) |
関数内部では、定義の右辺が実行時に計算される時、defineと可能であれば
buildqを用いれば便利である。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
EXPRESSIONは任意の単一のMAXIMAの式で、VARLISTは<原子>か
<原子>:<値>の書式の要素のリスト。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
<varlist>の中の<値>は左から右に評価される(構文<原子>は<原子>:<原子>と同値 である)。そして、これらの変数は並行して<expression>に代入される。任意の<原子> が単一の引数として<expression>の内側で特別な形式SPLICE(つまり、 SPLICE(<原子>)に対して現われるのであれば、<原子>と関連する値はMACSYMAリスト でなければならず、代入の代りに<expression>に結合(SPLICE)されねばならない。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
BUILDQの引数は代入が実行される迄、簡易化を防ぐ必要がある。これは'
を利用する事で行える。
buildqはさっさと関数を構成するのに便利である。MAXIMAが強力な事の
一つに、問題を解く手助けとなる他の関数を定義する関数が作られる事である。以下、
級数の解法に関して、再帰的な関数の構成について議論する。この関数内部での関数
定義は通常defineを用いる。このdefineはその引数を評価する。
幾つかの例が以下のspliceに含まれている。
例えば、 MPRINT([X]) ::= BUILDQ([U : x], if (debuglevel > 3) then print(splice(u))); とし、 |
呼出しが以下であれば、
MPRINT("matrix is ",MAT,"with length",LENGTH(MAT))
|
これは次の行と同値である。
IF DEBUGLEVEL > 3
THEN PRINT("matrix is ",MAT,"with length",
LENGTH(MAT))
|
次の非自明な例は変数の値とそれらの名前を表示するものである。
MSHOW(A,B,C) |
PRINT('A,"=",A,",",'B,"=",B,", and",'C,"=",C)
|
(C101) foo(x,y,z):=mshow(x,y,z); (C102) foo(1,2,3); X = 1 , Y = 2 , and Z = 3 |
実際のmshowの定義は以下の通りである。BUILDQがどうやって'uがその変数名
を与える様に'引用された'構造を組み立てるかに注意せよ。マクロに含まれるRESULT
は、そのマクロに対して代入と評価されるコードの一片である事に注意せよ。
MSHOW([lis])::=BLOCK([ans:[],N:LENGTH(lis)], FOR i THRU N DO (ans:APPEND(ans, BUILDQ([u:lis[i]], ['u,"=",u])), IF i < N THEN ans :APPEND(ans, IF i < N-1 THEN [","] ELSE [", and"])), BUILDQ([U:ans],PRINT(SPLICE(u)))) |
Spliceは代数的操作に引数を与えたりする。
(C108) BUILDQ([A:'[B,C,D]],+SPLICE(A)); (D108) D+C+B |
代入後にのみ簡易化がどのよう行なわれるかを注意せよ。最初の場合では、
spliceに対して適応されるのは+で、第二の場合は*である。
そこで、論理的にsplice(a)+sploce(A)は2*spliceで置換えられると
思うかもしれないが、如何なる簡易化もbuildqでは実行されない。SPLICEが代数で
何を実行するかを理解する為に、A+B+Cの様な公式操作がMAXIMA内部で
+(A,B,C)と非常に類似しており、積についても類似している事を理解して
いなければならない。それ故、*(2,B,C,Dは2*B*C*Dとなる。
(C114) BUILDQ([A:'[B,C,D]],+SPLICE(A)); (D114) D+C+B (C111) BUILDQ([A:'[B,C,D]],SPLICE(A)+SPLICE(A)); (D111) 2*D+2*C+2*B しかし、 (C112) BUILDQ([A:'[B,C,D]],2*SPLICE(A)); (D112) 2*B*C*D |
最後に、BUILDQは再帰的な関数の構築で計り知れない価値がある。ここで、ある微分 方程式を級数を用いた方法で解くプログラムがあり、その方程式で次の再帰的な関係 を構築する必要があったとする。
F[N]:=(-((N^2-2*N+1)*F[N-1]+F[N-2]+F[N-3])/(N^2-N)) |
expandを実際に付け足したければ、
F[N]:=EXPAND((-((N^2-2*N+1)*F[N-1]+F[N-2]+F[N-3]) /(N^2-N))); |
expandが
動作するのが、その関数が動作する各時点であって、その以前でない事を望めば、
kill(f), val:(-((n^2-2*n+1)*f[n-1]+f[n-2]+f[n-3])/(n^2-n)), define(f[n],buildq([u:val],expand(u))), (*訳者注: この例ではMaximaのプログラムに入れる事を考えているので、末尾が";"では無い。 *) |
(C28) f[6]; (D28) -AA1/8-13*AA0/180 |
(C25) f[6];
(D25) (5*(-4*(-3*(-2*(AA1+AA0)+AA1+AA0)/2
-(AA1+AA0)/2+AA1)
/3
-(-2*(AA1+AA0)+AA1+AA0)/6+(-AA1-AA0)/2)
/4
+(-3*(-2*(AA1+AA0)+AA1+AA0)/2
-(AA1+AA0)/2+AA1)
/12-(2*(AA1+AA0)-AA1-AA0)/6)
/30
|
BUILDQはその形式を構築する
のに便利である。
(*訳者注:
上記のfを決める為には勿論f[0],f[1],f[2]が必要。ここでは上記の例に沿って
macsymaに式を入れて行く。尚、f[0]:0,f[1]:aa0,f[2]:aa1とする。
(C1) val:(-((n^2-2*n+1)*f[n-1]+f[n-2]+f[n-3])/(n^2-n));
2
- (n - 2 n + 1) f - f - f
n - 1 n - 2 n - 3
(D1) -----------------------------------------
2
n - n
(C2) define(f[n],buildq([u:val],expand(u)));
2
- (n - 2 n + 1) f - f - f
n - 1 n - 2 n - 3
(D2) f := EXPAND(-----------------------------------------)
n 2
n - n
(C3) f[0]:0;f[1]:aa0;f[2]:aa1;
(D3) 0
(C4)
(D4) aa0
(C5)
(D5) aa1
(C6) f[6];
3 aa1 aa0
(D6) ----- + ---
10 40
この例では簡易化された答が返却される。それに対し、define内部のbuildqでexpandを
抜いた場合を以下に示す。
(C1) val:(-((n^2-2*n+1)*f[n-1]+f[n-2]+f[n-3])/(n^2-n));
2
- (n - 2 n + 1) f - f - f
n - 1 n - 2 n - 3
(D1) -----------------------------------------
2
n - n
(C2) define(f[n],buildq([u:val],u));
2
- (n - 2 n + 1) f - f - f
n - 1 n - 2 n - 3
(D2) f := -----------------------------------------
n 2
n - n
(C3) f[0]:0;f[1]:aa0;f[2]:aa1;
(D3) 0
(C4)
(D4) aa0
(C5)
(D5) aa1
(C6) f[6];
3 (- 4 aa1 - aa0)
4 (- aa1 - ----------------- - aa0)
2 - 4 aa1 - aa0
5 (- aa1 - ----------------------------------- - -------------)
3 6
(D6) (- ---------------------------------------------------------------
4
3 (- 4 aa1 - aa0)
- aa1 - ----------------- - aa0
2 - 4 aa1 - aa0
- ------------------------------- - -------------)/30
12 6
展開されない為に複雑なままである。
*)
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
TRANSLATEを用いてMACSYMAでコードの生成を行う時、時間の節約が可能で、便利な 幾つかの技法がある。デモはDEMO("optimu.dem")を実行せよ。 特に、TRANSL;OPTIMU FASLからの関数FLOATDEFUNKは関数定義を数学風の式から生成 するが、それを(OPTIMIZEで)最適化し、正確にCOMPILEを行うのに必要なMODE_DECLARE によるモードの宣言を行う(勿論、これは手動で実行しなければならない)。デモは MACSYMAを新規に起動した状態のみで動作する。
(* 訳者注: optimu.demはmaxima-5.6に含まれていない様である。 *) |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
(* 訳者注:
先頭に'を付けると云う意味は、名詞型を評価するのが本来と云う事。
(C17) apply('expand,[(x+1)^2]);
2
(D17) x + 2 x + 1
(C18) apply('apply,['expand,[(x+1)^2]]);
2
(D18) x + 2 x + 1
(C19)
この様にする理由は、以下の馬鹿馬鹿しい例から明確になるだろう。
(C30) neko(x):=2*x;
(D30) neko(x) := 2 x
(C31) neko:-128;
(D31) - 128
(C32) neko(10);
(D32) 20
(C33) apply('neko,[20]);
(D33) 40
(C34) appply(neko,[20]);
(D34) appply(- 128, [20])
この例では、nekoを関数として定義したが、同時に、変数として-128を束縛している。
その為、applyでは'が無ければ変数として評価されてしまっている。
*)
|
例えば、ARRAY(完全な配列を除く),FUNCTION,DEPENDENCIES,ATVALUE,MATCHDECLARE, ATOMGRAD, CONSTANTとNONSCALARと云った、VALUE(値)を除く、局所的な属性の保存と 復旧が望ましければ、関数LOCALはBLOCK内部で変数の名前となる引数を用いなければ ならない。
BLOCKの値は、最後の文の値やBLOCKから明示的に抜ける為に用いられる関数RETURNに 渡された引数の値である。関数GOは制御をGOの引数でラベル付けされたBLOCK内の文に 移す事に用いる。文のラベル付けは、BLOCKで原子引数を別の文として、その文の 先頭に置く事で行う。例えば、BLOCK([X],X:1,LOOP,X:X+1,...,GO(LOOP),...)の様に する。GOの引数はBLOCK内部に現われるラベル名でなければならない。ここで、GOを 含まないBLOCK中のラベルにGOで移動する事は出来ない。BLOCKは通常、関数定義の 右側に現われるが、他の場所に置く事も同様に可能である。
この"非局所的回帰"(non-local return)は、任意の(構文の)入れ子の深さを通過して 最も近いTHROWに対応するCATCHに行く。THROWに対応するCATCHがなければならず、 そうでなければエラーとなる。もし、expiの評価が何らのTHROWの評価に至らなかった 場合、CATCHの値はexpnの値となる。
(C1) G(L):=CATCH(MAP(LAMBDA([X],
IF X<0 THEN THROW(X) ELSE F(X)),L));
(C2) G([1,2,3,7]);
(D2) [F(1), F(2), F(3), F(7)]
(C3) G([1,2,-3,7]);
(D3) - 3
|
この関数Gは、Lが非負の数のみであればLの各要素に対するFのリストを返す。 それ以外では、GはLの最初の負の要素を"捉え(catch)"、それを"放擲する(throw)"。
TRANSLATE_FILE("filename").
|
Compile_and_load(FILENAME):=
LOAD(COMPILE_LISP_FILE(TRANSLATE_FILE(FILENAME)[2]))[2]);
|
これらのファイル操作の命令ではCOMPILE、COMPFILEとTRANSLATE SAVEの組合せで利用 すると良い。
ここで、
DEFINE_VARIABLE(FOO,TRUE,BOOLEAN); |
可能な"モード"のリストについてはDESCRIBE(MODE_DECLARE);を見よ。 オプションの第四の引数は文書文字列である。TRANSLATE_FILEが文書文字列を含む パッケージに用いられた時、第二のファイルはマニュアル、用例ファイルや (例えば)DESCRIBE向けに適した書式の文書文字列を含むLISPのファイル出力である。 ANY以外のモードでDEFINE_VARIABLEとされた任意の変数でVALUE_CHECK属性を与える 事が可能であり、この属性は、利用者がその変数に設定しようとする値に関して 呼出された1引数の関数である。
PUT('G5,LAMBDA([U],IF U#'G5 THEN ERROR("Don't set G5")),
'VALUE_CHECK);
|
DEFINE_VARIABLE(G5,'G5,ANY_CHECK, "this ain't supposed to be set by anyone but me.")を用いると、ANYY_CHECKはANYと同じモードであるが、DEFINE_VARIABLEを 最適化せずに、割当てた属性のままとなる事を意味する。
DISPFUN(ALL)は全ての下添字された関数で定数の下添字を持つもの除いたFUNCTIONS とARRAYSリストで与えられる利用者定義関数を表示する。例えば、利用者が関数F(X) を定義していれば、DISPFUN(F);で、その定義内容を表示する。
(* 訳者注:
(C2) neko(x):=block(local(a),a:abs(x),if x<1 then a:x,return(a));
(D2) neko(x) := BLOCK(LOCAL(a), a : ABS(x), IF x < 1 THEN a : x, RETURN(a))
(C3) f(x):=x^2+1;
2
(D3) f(x) := x + 1
(C4) mike:1;
(D4) 1
(C5) functions;
(D5) [f(x), neko(x)]
(C6)
この様に定義した関数のみがリストで表示されている。
*)
|
(* 訳者注: (C8) fundef(neko); (D8) neko(x) := BLOCK(LOCAL(a), a : ABS(x), IF x < 1 THEN a : x, RETURN(a)) (C9) dispfun(neko); (E9) neko(x) := BLOCK(LOCAL(a), a : ABS(x), IF x < 1 THEN a : x, RETURN(a)) (D9) DONE (C10) この様に、fundefでは内容がD行で表示されるが、dispfunではE行で表示されている。 *) |
(* 訳者注:
要するに、こんな事。
(C14) funmake(f,[x,y,z]);
(D14) f(x, y, z)
(C15) funmake(neko,[x,y,z]);
(D15) neko(x, y, z)
(C16) funmake(expand,[128,"うちのタマ知りませんか?"]);
(D16) EXPAND(128, うちのタマ知りませんか?)
(C17) a;
(D17) a
(C18) funmake(a,[1,2,3]);
(D18) a(1, 2, 3)
(C19) a:10;
(D19) 10
(C20) funmake(a,[1,2,3]);
Bad first argument to FUNMAKE: 10
-- an error. Quitting. To debug this try DEBUGMODE(TRUE);)
(C21) funmake('a,[1,2,3]);
(D21) a(1, 2, 3)
これらの例で示す様に、引数が出鱈目でも評価をしないので何も問題は無い。
又、関数名である必要は無いが、値が束縛されている時は内部で評価されるので、
この場合は、'を先頭に付けて名詞型とする必要がある。
*)
|
可能な設定は:
ARRAY(yi, dim1, dim2, ...) |
もし、配列の全ての要素がFIXNUM(FLOAT)のモードであれば、COMPLETEの代りに FIXNUM(FLOAT)を用いる。又、配列の全ての要素が同じモード、例えばmであれば、
MODE_DECLARE(COMPLETEARRAY(yi),m)) |
例えば:
MODE_DECLARE(COMPLETEARRAY(A[10,10]),FLOAT) |
その式の例は、
MODE_DECLARE([FUNCTION(F1,F2,...),X],FIXNUM,Q,
COMPLETEARRAY(Q),FLOAT)
|
MODE_DECLAREは関数定義の内部か大域変数に対するトップレベルのどちらかを用いる。 TRANSLATE(変換)とCOMPULE(翻訳)でのMODE_DECLAREの幾つかの例は PRINTFILE(MCOMPI,DOC,MAXDOC);を実行せよ。
MODE_IDENTITY(FLONUM,X)は3.3を返す。 これは色々な使い方があり、例えば、FIRST(L)が数(NUMBER)を返す事が判っていれば、 MODE_IDENTITY(NUMBER,FIRST(L))と書いても良いだろう。しかし、より効率的な方法 は、新しいプリミティブ、
FIRSTNUMB(X)::=BUILDQ([X],MODE_IDENTITY(NUMBER,X)); |
例えば:
F(X1,X2,...):=BLOCK([v1,v2,...],
MODE_DECLARE(v1,mode1,v2,mode2,...),...)
|
とし、ここで、X1,X2,...は関数の引数で、v1,v2,...は局所変数である。変換された 関数の名前は、SAVEDEFがFALSE(以下を見よ)であればFUNCTIONリストから削除され、 PROPSリストに追加される。関数は虫取りが完遂されるまで変換すべきではない。 更に、式は簡易化されていると仮定されている。そうでなければ、動作は正しい ものの、最適化されていないコードが生成されてしまう。それ故、利用者はSIMP スイッチをFALSEに設定して変換されるべき式の簡易化を禁じてはならない。
ファイル指定の引数をTRANSLATEに与える事で、ファイルに保管された関数の変換が 可能である。これは[fn1,fn2,DSK,dir]の形式のリストで、ここで、fn1,fn2は MACSYMA関数のファイル名、dirはファイルのディレクトリ名である。TRANSLATEに よって返される結果はTRANSLATEで変換された関数の名前のリストである。ファイル 変換の場合、そのリストに対応する変換結果のLISPのコードを含んでいる第一と 第二の新しいファイル名のリストになる。これはfn1 LISPで、 ディスクディレクトリのdirにある。LISPのコードはMACYSMAにてLOADFILE関数を 用いると読込める。
[TRANSCOMPILE, TR_SEMICOMPILE, TR_WARN_UNDECLARED, TR_WARN_MEVAL, TR_WARN_FEXPR, TR_WARN_MODE, TR_WARN_UNDEFINED_VARIABLE, TR_FUNCTION_CALL_DEFAULT, TR_ARRAY_AS_REF,TR_NUMER] |
設定 | 動作 ------------------------------------------------------------ FALSE | 警告を表示しない COMPFILE | COMPFILEであれば警告する TRANSLATE | TRANSLATEやTRANSLATE:TRUEであれば警告する ALL | COMPFILEやTRANSLATEであれば警告する ------------------------------------------------------------ |
MODE_DECLARE(<変数>,ANY)を実行して変数が一般のMACSYMAの変数である事を宣言 する(つまり、FLOAT、又はFIXNUMである事に限定されない)。COMPILEで翻訳される べきコードの中の変数を宣言する特別な動作は全て無効にしなければならない。
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |