数値計算 第5回課題
(大石先生)

G99P043-4
河邊昌彦

出題日 :2001/06/29
提出期限 :2001/08/15
提出日 :2001/08/15

1  問題

数値計算用インタプリタを作れ。

2  概要

作成したインタプリタを以下ではTNC (Tool for Numerical Computation) と呼ぶことにする。

TNCの特徴や機能は次の通りである。

3  メイク

TNCをメイクするには、TNCのメイクファイルがあるディレクトリでmakeを実行すれば良い。 ただし、バージョン3.0以降のg++にパスが通っていなければならない。 また、/usr/local/liblapack.ablas.aというファイル名でLAPACK(バージョン3.0以降)と BLASのライブラリがあるものとする。

TNCはVisual C++ Ver.6.0でもビルドすることが可能である。 ただし、環境によってはsource/ include/common.hppにおけるマクロの定義を変更することが必要である。

3.1  cygwin上での注意点

TNC自体はcygwin上で問題なく動作するが、gcc 3.0をcygwin上でメイクする時には次の点に注意しなければならない。 即ち、configureに対して--enable-threads=single --enable-sjlj-exceptionsとして設定しなければならない点である。 このオプションを付けずにメイクすると、try-catchによる例外の捕捉に失敗するコードを生成するgccがメイクされるようである。

4  データ構造

TNCで扱うデータは全て、``セル''と呼ばれるものである。 セルは以下のような継承関係がある。 ただし、Nilセルは、アトムセルでありかつコンスセルでもある。

4.1  セル

セルは、全てのセルの派生元であり、Java言語におけるObjectクラスのようなものである。 後述するが、シンボルセルに対してバインドできるのはセルであるので、即ちどのようなセルでもバインドできることになる。

4.2  アトムセル

基本的に、これ以上分解することのできないセルはアトムセルに分類されている。

4.3  シンボルセル

プログラム上に現れる識別子などの名前は、シンボルセルとして扱われる。 シンボルセルには、それを表すシンボルが関連付けられている。 また、一つのシンボルセルに対して、一つのセルを自由にバインドすることができる。 従って、シンボルセルは変数としての役割も果たす。

4.4  データセル

数値計算で扱う数値を保持するのが、データセルである。 実際には、実数データセル、複素数データセル、実行列データセル、複素行列データセルはそれぞれ違うクラスになっている。

4.5  文字列セル

文字列を保持するセルである。 現在のところ、文字列に対する操作はまだ用意されていない。

4.6  ストリームセル(入力/出力)

コンソールやファイルに対する入出力は、対応するストリームに対して操作を行うことで実現される。 ストリームは、ストリームセルへの参照がなくなった時点で、自動的にクローズされる。

4.7  Nilセル/Trueセル

これらのセルは、論理値の偽と真として働く。 実際には、Nilセル以外は全て論理値解釈では真として扱われる。 また、Nilセルは、空のコンスセルとしても扱われる。

4.8  関数セル

関数を保持しているセルである。 ビルトインの関数も、ユーザ定義の関数も、同じ関数セルとして扱う。 また、関数のオーバーロードは、二つの関数セルを組み合わせて新しい関数セルを作成することで、実現される。

4.9  クラスセル

セルのクラスを表すセルである。 セルのクラスを識別するために、同じクラスのセルに対しては、同じインスタンスのクラスセルが対応付けられている。 また、クラスセルのクラスは、自分自身となっている。

4.10  コンスセル

コンスセルは、リストを構成するセルである。 Lisp言語におけるコンスセルと違い、そのcdr部分にはコンスセル(Nilセルを含む)しか入れられない。 car部分はLisp言語と同様、どのようなセルでも入れることができる。

4.11  構造セル

構造セルは、Prolog言語における構造データに相当するものである。 関数記号と引数を持ち、評価の際には関数呼び出しを表す。

4.12  引用セル

引用セルは、Lisp言語のquoteと同じ機能を持っている。 即ち、評価の際に評価されたくないセルは、引用セルで括ることによって未評価のままにすることができる。

5  字句

TNCの字句は次のものがある。

シンボルは、C言語などの識別子と同じである。 さらに、```''と``'''に括られた文字の列もシンボルとなる。 実数は、C言語などの整数定数と浮動小数点定数を併せたものにほぼ一致する。 虚数は、実数の直後にサフィックスとして``i''を付加したものである。 文字列は、``"''で括られた文字の列である。 予約語は、次のものである。

nil, true, if, then, else, while, lambda, try, catch, local
記号は、``('', ``)''等といったものである(正確にはsource/lex.lを参照)。 なお、シングルクオート、ダブルクオートの中では、Javaライクなエスケープシーケンスを使うことができる。

字句解析で扱うものとしては、この他に、コメントとエスケープされた改行、空白類がある。 コメントはC言語のように``/*''で始まって``*/''までのものと、シェルスクリプトのように``#''で 始まって改行までのものの、二通りが使える。 エスケープされた改行は、``\''の直後に改行が来ているもので、この改行は無視される。 空白類は、スペース、水平タブ、キャリッジリターン、垂直タブ、フォームフィードであり、これらは無視される。

source/include/common.hppでUNICODEというマクロが定義されていると、入出力はUnicode文字を使って行われる。 この時、flexの生成する字句解析器はシングルバイト文字しか処理できないので、一旦ユニコードエスケープ(\uXXXX)に変換して 字句解析器を通し、再びユニコード文字に戻している。 従って、入出力がUnicodeでも、ASCII(7bit)文字以外の文字は、クオートの中でしか使えないことになる。

6  構文

TNCの構文は、以下の文法によって規定される。 構文解析の段階では、ほぼ完全に文脈自由文法の範疇に収まっている。 以下では、JLS (Java Language Specification) 等で用いられているBNFライクな表記で文法を記述する。

各構文要素の解析の結果はセルとして得られる。 最終的に、expression-input の解析結果のセルが、構文解析の結果となる。

6.1  入力

       ¯ expression-input :
  END-INPUT
  expression END-INPUT

expression-input は文法の開始記号である。 これが、一行の入力、或いは、ファイル全体となる。 END-INPUT は、インタラクティブな入力ならば改行記号とEOF、非インタラクティブな入力ならばEOFのみとなる。

6.2  一次式

       ¯ primary-expression :
  nil
  true
  symbol
  real-constant
  imaginary-constant
  string-constant
  parenthesis-expression
  list-expression
  matrix-expression
  if-expression
  while-expression
  lambda-expression
  try-expression
  local-expression

primary-expression の解析結果は、次の通りである。 nil ならばNilセル、true ならばTrueセル、symbol ならばそのシンボルを持つシンボルセル、 real-constant ならばその数を持つ実数データセル、imaginary-constant ならばその数を持つ複素数データセル、 string-constant ならばその文字列を持つ文字列セルとなる。 その他の場合は、下位の解析結果がそのまま解析結果となる。

6.2.1  括弧式

       ¯ parenthesis-expression :
  ( expression )

parenthesis-expression の解析結果は、expression の解析結果となる。

6.2.2  リスト式

       ¯ list-expression :
  { expression-list opt }

expression-list :
  expression
  expression-list , expression

list-expression の解析結果は、各expression を要素にもつリストの先頭のコンスセルとなる。

6.2.3  行列式

       ¯ matrix-expression :
  [ two-dimensional-expression-list ]

two-dimensional-expression-list :
  two-dimensional-expression-list-sub
  two-dimensional-expression-list ; two-dimensional-expression-list-sub

two-dimensional-expression-list-sub :
  assignment-expression
  two-dimensional-expression-list-sub , assignment-expression

matrix-expression の解析結果は、``[]''を関数記号とし、two-dimensional-expression-list の表す 二次元的なリストを引数とする構造セルとなる。

6.2.4  if式

       ¯ if-expression :
  if parenthesis-expression then parenthesis-expression
  if parenthesis-expression then parenthesis-expression else if-expression
  if parenthesis-expression then parenthesis-expression else parenthesis-expression

if-expression の解析結果は、関数記号を``if''とし、引数を二つ或いは三つの下位のセルとする構造セルである。 ただし、二番目と三番目のセルは、引用セルで括られる。

6.2.5  while式

       ¯ while-expression :
  while parenthesis-expression parenthesis-expression

while-expression の解析結果は、関数記号を``while''とし、引数を三つの下位のセルとする構造セルである。 ただし、二番目と三番目のセルは、引用セルで括られる。

6.2.6  lambda式

       ¯ lambda-expression :
  lambda parenthesis-expression opt list-expression parenthesis-expression

lambda-expression の解析結果は、関数記号を``lambda''とし、第一引数をlist-expression の解析結果、 第二引数を最後のparenthesis-expression の解析結果、そして一つ目のparenthesis-expression が省略されなかった場合は その結果を第三引数とする構造セルである。 ただし、全ての下位セルは、引用セルで括られる。

6.2.7  try式

       ¯ try-expression :
  try parenthesis-expression
  try parenthesis-expression catch parenthesis-expression

try-expression の解析結果の構造セルは、関数記号を``try''とし、第一引数を一つ目の parenthesis-expression の解析結果とし、二つ目のparenthesis-expression が記された場合は 第二引数をその結果とする。 ただし、一つ目のparenthesis-expression の結果は、引用セルで括られる。

6.2.8  local式

       ¯ local-expression :
  local list-expression parenthesis-expression

local-expression の解析結果の構造セルは、関数記号として``local''を持ち、第二引数としてlist-expression の結果を、第三引数としてparenthesis-expression の結果を持つ。 ただし、第二、第三引数は引用セルで括られる。

6.3  後置式

       ¯ postfix-expression :
  primary-expression
  postfix-expression ( expression-list )
  postfix-expression . primary-expression
  postfix-expression postfix-operator

postfix-operator : one of
  ' .'

postfix-expression の解析結果は、次の通りである。

``primary-expression ''の場合は、その結果が解析結果となる。

``postfix-expression ( expression-list ) ''の場合は、関数記号をpostfix-expression の結果、 引数をexpression-list の各要素とする構造セルとなる。

``postfix-expression . primary-expression ''の場合は、関数記号を``.''とし、引数を postfix-expression の結果とprimary-expression の結果とする構造セルとなる。 ただし、第二引数は引用セルで括られる。

``postfix-expression postfix-operator ''の場合は、関数記号をpostfix-operator の結果とし、 引数をpostfix-expression とする構造セルとなる。

6.4  単項式

       ¯ unary-expression :
  postfix-expression
  unary-operator unary-expression
  ' unary-expression
unary-operator : one of
  + - !

unary-expression の解析結果は、次の通りである。 一番目の場合は、postfix-expression の結果が解析結果となる。 二番目の場合は、関数記号としてunary-operator の結果を持ち、引数としてunary-expression の結果を持つ構造セルとなる。 三番目の場合は、unary-expression の結果を内容として持つ引用セルとなる。

6.5  冪乗式

       ¯ power-expression :
  unary-expression
  unary-expression power-operator power-expression

power-operator : one of
  '136 . '136 ** .**

power-expression の解析結果は、次の通りである。 一番目の場合は、unary-expression の結果が解析結果となる。 二番目の場合は、関数記号としてpower-operator の結果を持ち、引数として unary-expression の結果とpower-expression の結果を持つ構造セルとなる。

6.6  乗除式

       ¯ multicative-expression :
  power-expression
  multicative-expression multicative-operator power-expression

multicative-operator : one of
  * / % .* ./ .% .

multicative-expression の解析結果は、次の通りである。 一番目の場合は、power-expression の結果が解析結果となる。 二番目の場合は、関数記号としてmulticative-operator の結果を持ち、引数として multicative-expression の結果とpower-expression の結果を持つ構造セルとなる。

6.7  加減式

       ¯ additive-expression :
  multicative-expression
  additive-expression additive-operator multicative-expression

additive-operator : one of
  + - .+ .-

additive-expression の解析結果は、次の通りである。 一番目の場合は、multicative-expression の結果が解析結果となる。 二番目の場合は、関数記号としてadditive-operator の結果を持ち、引数として additive-expression の結果とmulticative-expression の結果を持つ構造セルとなる。

6.8  シフト式

       ¯ shift-expression :
  additive-expression
  shift-expression shift-operator additive-expression

shift-operator : one of
  << >> <<< >>>

shift-expression の解析結果は、次の通りである。 一番目の場合は、additive-expression の結果が解析結果となる。 二番目の場合は、関数記号としてshift-operator の結果を持ち、引数として shift-expression の結果とadditive-expression の結果を持つ構造セルとなる。

6.9  ベクトル生成式

       ¯ vector-creation-expression :
  shift-expression
  shift-expression : shift-expression
  shift-expression : shift-expression : shift-expression

vector-creation-expression の解析結果は、次の通りである。 一番目の場合は、shift-expression の結果が解析結果となる。 二番目の場合は、関数記号として``:''を表すシンボルセルを持ち、引数として 二つのshift-expression の結果を持つ構造セルとなる。 三番目の場合は、関数記号として``:''を表すシンボルセルを持ち、引数として 三つのshift-expression の結果を持つ構造セルとなる。

6.10  比較式

       ¯ relational-expression :
  vector-creation-expression
  relational-expression relational-operator vector-creation-expression

relational-operator : one of
  < > <= >=

relational-expression の解析結果は、次の通りである。 一番目の場合は、vector-creation-expression の結果が解析結果となる。 二番目の場合は、関数記号としてrelational-operator の結果を持ち、引数として relational-expression の結果とvector-creation-expression の結果を持つ構造セルとなる。

6.11  等値式

       ¯ equality-expression :
  relational-expression
  equality-expression equality-operator relational-expression

equality-operator : one of
  != '176= <>

equality-expression の解析結果は、次の通りである。 一番目の場合は、relational-expression の結果が解析結果となる。 二番目の場合は、関数記号としてequality-operator の結果を持ち、引数として equality-expression の結果とrelational-expression の結果を持つ構造セルとなる。

6.12  論理積式

       ¯ logical-and-expression :
  equality-expression
  logical-and-expression && equality-expression

logical-and-expression の解析結果は、次の通りである。 一番目の場合は、equality-expression の結果が解析結果となる。 二番目の場合は、関数記号として``&&''を表すシンボルセルを持ち、引数として logical-and-expression の結果とequality-expression の結果を持つ構造セルとなる。 ただし、第二引数は引用セルで括られる。

6.13  論理和式

       ¯ logical-or-expression :
  logical-and-expression
  logical-or-expression || logical-and-expression

logical-or-expression の解析結果は、次の通りである。 一番目の場合は、logical-and-expression の結果が解析結果となる。 二番目の場合は、関数記号として``||''を表すシンボルセルを持ち、引数として logical-or-expression の結果とlogical-and-expression の結果を持つ構造セルとなる。 ただし、第二引数は引用セルで括られる。

6.14  代入式

       ¯ assignment-expression :
  logical-or-expression
  unary-expression assignment-expression assignment-expression

assignment-operator : one of
  '136= . '136= **= .**= *= .*= /= ./= %= .%= .
  += .+= -= .-= <<= >>= <<<= >>>=

assignment-expression の解析結果は、次の通りである。 一番目の場合は、logical-or-expression の結果が解析結果となる。 二番目の場合は、関数記号としてassignment-operator の結果を持ち、引数として unary-expression の結果とassignment-expression の結果を持つ構造セルとなる。

6.15  複合式

       ¯ compound-expression :
  assignment-expression
  compound-expression ;
  compound-expression ; assignment-expression

compound-expression の解析結果は、次の通りである。 一番目の場合は、assignment-expression の結果が解析結果となる。 二番目の場合は、関数記号として``;''を表すシンボルセルを持ち、引数として compound-expression の結果を持つ構造セルとなる。 三番目の場合は、関数記号として``;''を表すシンボルセルを持ち、引数として compound-expression の結果とassignment-expression の結果を持つ構造セルとなる。

6.16  式

       ¯ expression :
  compound-expression

expression の解析結果は、compound-expression の結果である。

7  評価

構文解析によってセルが得られると、そのセルを評価することによって計算結果を得る。 ここでは、その評価の方法について記述する。

7.1  アトムセル(シンボルセルを除く)

アトムセルを評価すると、そのアトムセル自身が評価結果となる。 ただし、シンボルセルだけは例外である。

7.2  シンボルセル

シンボルセルを評価すると、そのシンボルセルにバインドされているセルが評価結果となる。 シンボルセルには何もバインドされていない可能性もあるが、その場合は、エラーとなる。

7.3  コンスセル

コンスセルの評価結果は、そのcarとcdrの評価結果を持つコンスセルとなる。 つまり、評価を eval という関数で書くとすると、コンスセル x に対しては、


eval( x ) = cons( eval( car( x ) ), eval( cdr( x ) ) )
と定義される。 ただし、carの評価はcdrの評価よりも先にされる。

7.4  構造セル

構造セルの関数記号をfunctor、引数をargsとすると、構造セルの評価は次のように定義される。 まず、functorを評価し、次にargsを評価する。 そして、``()''というシンボルセルにバインドされている関数セルを取り出す。 ここで、セルがバインドされていないか、或いは、バインドされているセルが関数セルでない場合にはエラーとなる。 取り出した関数セルに対して、functorの評価結果とargsの評価結果の二つを引数として関数呼び出しを行う。 評価結果は、この関数の戻り値とする。

7.5  引用セル

引用セルを評価すると、その引用セルが括っているセルが(未評価のまま)評価結果となる。

8  組込み関数セル

以上で、TNCの実行規則は定義されたが、これだけではほとんど何も行うことができない。 そこで、TNCに組込まれている関数セルについて説明していく。 これらの関数セルは、TNCの起動時に自動的にシンボルセルにバインドされる。 従って、そのシンボルセル自体は特別なものではないので、後から書きかえることもできる。

以下の表記では、例えば

``()''( Function f, Cons args )
ならば、第一引数に関数セルを受け取り、第二引数にコンスセルを受け取る関数セルが``()''というシンボルセルにバインドされている、 ということを表している。

8.1  基本関数

8.1.1  関数呼び出し

``()''( Function f, Cons args )

fに対して、argsという引数で関数呼び出しを行う。 戻り値は、この関数呼び出しの戻り値とする。

8.1.2  関数定義

``lambda''( Cons params, Cell body )
``lambda''( Cons params, Cell body, Cell tail )

引数仕様をparamsとし、本体をbodyとする関数セルを作成し、それを戻り値とする。 また、tailが記された場合は、引数リストをparamsとし、本体をbody、tailを末端部とする関数セルを作成する。 ここで作成された関数セルに対して関数呼び出しが行われると、次のような処理を行う。

  1. bodyの中に出現するparams又はtailの構成要素のシンボルセルを全て別のものに付けかえ、実引数をバインドする。
  2. 1の処理を行った結果を評価する。
  3. 末端部が存在するならば、bodyと同様にシンボルセルを付け替えて評価する。
  4. 最後の評価の結果を戻り値とする。

引数仕様は、シンボルセルとクラスセルの二つからなるリストの、リストである。 シンボルセルとクラスセルの組が、その引数の型を表している。

8.1.3  条件分岐

``if''( Cell c, Cell t )
``if''( Cell c, Cell t, Cell e )

cを論理値解釈し、それが真であるならばtを評価してその結果を戻り値とする。 偽であり、かつ二引数で呼ばれたならば、nilを戻り値とする。 偽であり、かつ三引数で呼ばれたならば、eを評価してその結果を戻り値とする。

8.1.4  繰り返し

``while''( Cell cond, Cell body )

  1. condを評価し、その結果を論理値解釈する。
  2. 1の結果が偽であるならば、戻り値nilを伴って関数を終了する。 真であるならば、bodyを評価し、再び1へ。

8.1.5  例外処理

``try''( Cell body )
``try''( Cell body, Function c )

bodyを評価し、その結果を戻り値とする。 ただし、bodyの評価中に例外が投げられた場合は、次のようにする。 一引数で呼ばれた場合は、その例外の持つセルを戻り値とする。 二引数で呼ばれた場合は、その例外の持つセルを引数としてcに対して関数呼び出しを行い、その戻り値を戻り値とする。 ただし、その例外の持つセルがcの引数仕様に適合しないのならば、同じセルを伴って再び例外を投げる。

``throw''( Cell arg )

argを持つ例外を作成し、その例外を投げる。

8.1.6  ローカリティ

``local''( Cons vars, Cell body )

bodyの中に出現する、varsの構成要素のシンボルセルを、新しいものに全て付け替えてからbodyを評価し、 その結果を戻り値とする。

8.1.7  論理演算

``!''( Nil n )
``!''( True t )

一番目の場合は、trueを返す。 二番目の場合は、nilを返す。

``&&''( Nil n, Cell c )
``&&''( True t, Cell c )

一番目の場合は、nilを返す。 二番目の場合は、cを評価し、その論理値解釈を返す。

``||''( Nil n, Cell c )
``||''( True t, Cell c )

一番目の場合は、cを評価し、その論理値解釈を返す。 二番目の場合は、trueを返す。

8.1.8  代入

``=''( Symbol s, Cell c )

sに対してcをバインドする。 cを戻り値とする。

8.1.9  複合

``;''( Cell c1 )
``;''( Cell c1, Cell c2 )

一番目の場合は、nilを返す。 二番目の場合は、c2を返す。

8.2  算術演算

``+'', ``-'', ``.+'', ``.-'', ``*'', ``/'', ``.*'', ``./'', ``^''等が、各数値型に対して定義されている。

また、基本的な算術関数として、``sqrt'', ``exp'', ``log'', ``log10'', ``sin'', ``sinh'', ``cos'', ``cosh'', ``tan'', ``tanh'', ``sec'', ``sech'', ``csc'', ``csch'', ``cot'', ``coth'', ``asin'', ``asinh'', ``acos'', ``acosh'', ``atan'', ``atan2'', ``atanh'', ``asec'', ``asech'', ``acsc'', ``acsch'', ``acot'', ``acoth''が複素数上で定義されている。

そして、``sign'', ``max'', ``min'', ``floor'', ``ceil'', ``round'', ``fix''も定義されている。

複素数を扱う関数として、``real'', ``imag'', ``abs'', ``angle'', ``conj''が定義されている。

比較演算子としては、``=='', ``!='', ``<'', ``>'', ``<='', ``>=''が定義されている。

行列を扱う関数として、``''', ``.''', ``size'', ``abs'', ``norminf'', ``diag'', ``\'', ``inv'', ``eig'', ``[]'', ``rand'', ``zeros'', ``ones'', ``eye''等が定義されている。 これらは、MATLABと同様の仕様である。

丸め方向の制御として、``up'', ``down'', ``near'', ``chop''が定義されている。

8.3  入出力

標準入力は``stdin''、標準出力は``stdout''、標準エラー出力は``stderr''にストリームセルとしてバインドされている。

8.3.1  オープン

``fopen''( String filename, Cons options )

filenameで示されるファイル名のファイルを開く。 この時、optionsの構成要素に``read''というシンボルセルが含まれているならば読取専用として、 ``write''というシンボルセルが含まれているならば書き出し専用として、開く。 このどちらかは必ず指定しなければならない。 また、``binary''というシンボルセルが含まれているならばバイナリストリームとして開く。 戻り値は、開いたストリームのストリームセルである。

8.3.2  終端判定

``feof''( Stream s )

sがEOFまで読まれているかどうかを判定し、読まれていた場合はtrueを、そうでない場合はnilを返す。

8.3.3  読み取り

``fget''( InputStream s )

sから行を単位に読み取り、構文解析をし、その結果のセルを返す。

``fload''( InputStream s )

sからストリーム全多淫を単位に読み取り、構文解析をし、その結果のセルを返す。

8.3.4  書き出し

``fprint''( String s, OutputStream str )

strに文字列sを書き出す。

``fput''( Cell c, OutputStream str )

strにcを文字列に変換して書き出す。

8.3.5  表示精度

``precision''( OutputStream s, Real prec )

sに数値を出力する際の表示精度をprecとする。

8.4  クラス

各クラスは、``Cell'', ``Atom'', ``Symbol'', ``Real'', ``Complex'', ``RealMatrix'', ``ComplexMatrix'', ``String'', ``Stream'', ``InputStream'', ``OutputStream'', ``Cons'', ``Nil'', ``True'', ``Structure'', ``Function'', ``Quoted'', ``Class'' にバインドされている。

8.4.1  クラス抽出

``classof''( Cell c )

cのクラスを表すクラスセルを返す。

8.4.2  インスタンス判定

``instanceof''( Cell c, Class class )

cがclassのインスタンス又はclassのサブクラスのインスタンスであるならば、trueを返す。 そうでないならば、nilを返す。

8.5  時間

``timing''( Cell c )

cを評価し、その評価にかかった時間を秒単位で返す。

8.6  一般関数

``exit''()

TNCの実行を終了する。

``eval''( Cell c )

cを評価し、その結果を戻り値とする。

``tostr''( Cell c )

cを文字列に変換して、それを戻り値とする。

8.7  オーバーロード

``overload''( Function f1, Function f2 )

f1とf2から新たな関数セルを作成し、それを戻り値とする。 新たな関数セルに対して関数呼び出しが行われると、まず、f1の引数仕様に実引数が適合するかどうかを調べ、 適合する場合は、f1を呼び出す。 そうでない場合は、f2を呼び出す。

8.8  リスト操作

``cons'', ``car'', ``cdr'', ``append'', ``reverse''が定義されている。

``=''( Cons dst, Cons src )

dstとsrcのcar部分同士とcdr部分同士にそれぞれ``=''を呼ぶ。

9  TNCの起動

9.1  シェル

TNCはオプションを付けないと、普通に起動し入力待ちの状態になる。 ここで、``-f''を付け、その後ろにファイル名をつけると、そのファイルが尽きるまでファイルから入力を読み取って評価する。 従って、ファイルの先頭に、
	#! ./tnc -f

とインタプリタ指定を書いておき、ファイルを実行可能にしておけば、自動的にTNCで実行することができる。

9.2  初期化ファイル

TNCは起動時に、カレントディレクトリの``init.tnc''というファイルを読みこんで評価する。 従って、毎回設定したいことは、このファイルに書いておけば良い。

10  実行例

[]のp.106, pp.108-110, p.112の各プログラムに相当するものをsample.tnc, lin.tnc, eig.tncとして作成した。 この内、sample.tncはメイクした時点で実行可能になっているので、シェルから直接実行できる。

	$ sample.tnc
	ans = nil
	ans = nil
	ans = 
	 0.0999999999999999917 
	 -0.100000000000000006 
	 0.0999999999999999917 

	ans = 
	 0.0999999999999999917 
	  0.199999999999999983 
	 -0.300000000000000044 

	ans = nil
	ans = 
	   0.100000000000000006 
	 -0.0999999999999999917 
	   0.100000000000000006 

	ans = 
	  0.100000000000000006 
	  0.200000000000000011 
	 -0.299999999999999989 

	ans = 
	   0.100000000000000006 
	 -0.0999999999999999917 
	   0.100000000000000006 

	ans = nil
	ans = 
	  0.100000000000000006 
	  0.200000000000000011 
	 -0.299999999999999989 

	ans = nil
	ans = 
	 -0.0399999999999999731 

	ans = nil
	ans = 
	 -0.0400000000000000147 

	ans = nil

また、lin.tncは関数として、eig.tncはスクリプトとして書かれている。 従って、lin.tncを試す時は、

	TNC:1> load( "lin.tnc" )
	ans = function

	TNC:2> A=[1,2,-3; -2,3,4; 3,-4,5];
	ans = nil

	TNC:3> b=[1; 2; -3.3];
	ans = nil

	TNC:4> {x,e}=linear(A,b)
	ans = {
	  -0.2705128205 
	   0.5564102564 
	 -0.05256410256 
	,
	 2.761323933e-16 
	 2.675922162e-16 
	 8.824849683e-17 
	}

とする。

そして、eig.tncを試す時は、

	TNC:5> exec( "eig.tnc" )

	  -1.46585729663240727 
	 -0.667606283389616806 
	   2.34968353448908829 
	   4.78378004553293756 


	 8.45945518408088917e-15 
	 7.41980936377228185e-15 
	 9.61055457365009797e-15 
	 1.27807331540120258e-14 

	ans = nil

とする。

execやloadはinit.tncの中で定義されている。 また、init.tncでは``()''をオーバーロードして行列 A の( r, c )成分(0オリジン)を

	A( r, c )

で得られるようにしている。

なお、実際に実行すると、execやload等で``end of file''という例外が発生するが、これは現在の仕様であり特に問題はない。 feofは、EOFまで読みこんだストリームに対してtrueを返すので、EOFの直前まで読みこんだ状態ではtrueが返ってこないためである。

11  考察

記号処理に関するアイディアは、List言語やProlog言語をヒントにしたため、比較的簡単にまとめることができた。 また、構文解析と評価をほぼ完全に独立させたため、相互の依存関係が少なくなり、モジュール性が高くなっている。 そして、構文解析の結果と評価の結果、即ち、プログラムとデータが共にセルで表されているため、今回は試していないが、 メタインタプリタを作ることも十分可能だろう。

それに対して、数値計算部分は問題点が多い。 例えば、eigで得られる固有値の対角行列は、現在のところ普通のフル行列のデータ構造に入っている。 これは、時間的にも空間的にも無駄が多い。 また、LU分解を扱うようになると、置換行列を表すデータ構造も必要になるであろう。 このような、数値計算部分に関する問題は、主に解決すべきことの多さによるので、今回は時間との兼ね合いから 適当なところで打ち切ることにした。

時間との兼ね合いということでは、オブジェクトセルの実装も実現することができなかった。 関数のオーバーロードは既に可能であるので、後は、ユーザ定義クラスが作成できれば、それなりの オブジェクト指向プログラミングも可能となる。 ユーザ定義クラスで区間クラスなども実装してみたかったので、この点が最も残念な点である。

ところで、この課題を行うに当たって、octaveやMATLAB等の仕様を調べてみたが、プログラミング言語として見ると、 これらの数値計算ツールで使われている言語は、必ずしもモダンではないように感じる。 例えば、演算子の前後に空白を入れるだけで意味が変わる構文がある点である([1 -2;2 -1][1-2;2-1]など)。 或いは、``'''という文字が共役転置の記号と、シングルクオートの記号の両方に使われているため、 字句解析の段階で文脈に応じて判断している点もそうである。 このような直交性についてだけ言えば、TNCはoctaveやMATLABに勝っている可能性がある。

A  捕捉

今回の課題とは直接関係ないが、TNCを作成していて気付いたことを記す。

cygwinの環境は、必ずしも完全ではないようである。 即ち、sscanfはどうやらまともに解析をしないようなのである。 特に顕著なのが、浮動小数点数を読みこもうとすると、小数点を読み飛ばした数に解析される点である。 そこでTNCでは、C++のstd::istringstreamを使うことでこの問題を回避した。 同様の問題は、以前、cygwin上にoctaveをインストールしようとした時にも発生したので、調べてみると、 octaveの字句解析でも浮動小数点数の解析にsscanfが使われていることが分かった(octave 2.0.16ではsrc/lex.l(1420))。 従って、ここをstd::istringstreamを使う方法に変えれば、cygwin上にもoctaveをインストールできるのではないだろうか。

References

[]
大石進一, ``Linux数値計算ツール'', コロナ社, 2000



File translated from TEX by TTH, version 2.80.
On 16 Aug 2001, 21:41.