ユーザ言語 Asir
**********

User language Asir
******************

Asir の組み込み函数は, 因数分解, GCD などの計算を行うもの, ファイル入出力を行うもの, あるいは数式の一部を取り出すものなどさまざまなものが用意されているが, ユーザが実際に行いたいことを実行させるためには一般にはユーザ言語によるプログラムを書く必要がある. ユーザ言語も Asir と呼ばれる. 以下では, ユーザ言語の文法規則および実際のユーザ言語プログラムを例としたプログラムの書き方について述べる.

Asir provides many built-in functions, which perform algebraic computations, e.g., factorization and GCD computation, file I/O, extract a part of an algebraic expression, etc. In practice, you will often encounter a specific problem for which Asir does not provide a direct solution.  For such cases, you have to write a program in a certain user language.  The user language for Asir is also called Asir.  In the following, we describe the Syntax and then show how to write a user program by several examples.

文法 (C 言語との違い)
=============

Syntax --- Difference from C language
=====================================

Asir の文法は C 言語に準拠している. おもな相違点は次の通りである. 以下で, 変数とは Asir におけるプログラム用の変数, すなわち大文字で始まる文字列を意味することとする.

The syntax of Asir is based on C language. Main differences are as follows. In this section, a variable does not mean an indeterminate, but a program variable which is written by a string which begins with a capital alphabetical letter in Asir.

  * 変数の型がない.

     No types for variables.

     既に説明したとおり, Asir で扱われる対象自身は全て何らかの型を持っている. しかし, プログラム変数自体は, どのような対象でも代入できるという意味で型がないのである.

     As is already mentioned, any object in Asir has their respective types.  A program variable, however, is type-less, that is, any typed object can be assigned to it.

          [0] A = 1;
          1
          [1] type(A);
          1
          [2] A = [1,2,3];
          [1,2,3]
          [3] type(A);
          4

  * 函数内の変数は, デフォルトでは仮引数をこめてすべて局所変数. ただし, extern 宣言された変数は, トップレベルにおける大域変数となる. すなわち, 変数のスコープは大域変数と局所変数の 2 種類に単純化されている. トップレベル, すなわちプロンプトに対して入力された変数は全て大域変数として登録される. また函数内では次のいずれかとなる.

     Variables, together with formal parameters, in a function (procedure) are all local to the function by default. Variables can be global at the top level, if they are declared with the key word extern. Thus, the scope rule of Asir is very simple. There are only two types of variables: global variables and local variables. A name that is input to the Asir's prompt at the top level is denotes a global variable commonly accessed at the top level. In a function (procedure) the following rules are applied.

      1. 函数が定義されるファイルにおいて, その函数定義以前に, ある変数が extern 宣言されている場合, 函数内のその変数も大域変数として扱われる.

          If a variable is declared as global by an extern statement in a function, the variable used in that function denotes a global variable at the top level. Furthermore, if a variable in a function is preceded by an extern declaration outside the function but in a file where the function is defined, all the appearance of that variable in the same file denote commonly a global variable at the top level.

      2. extern 宣言されていない変数はその函数に局所的となる.

          A variable in a function is local to that function, if it is not declared as global by an extern declaration.

          % cat afo
          def afo() { return A;}
          extern A$
          def bfo() { return A;}
          end$
          % asir
          [0] load("afo")$
          [5] A = 1;
          1
          [6] afo();
          0
          [7] bfo();
          1

  * プログラム変数は大文字で始まり, 不定元, 函数は小文字で始まる.

     Program variables and algebraic indeterminates are distinguished in Asir.

     この点は, 既存の数式処理システムのほとんどと異なる点である. Asir がこの仕様を採用したのは, ユーザが不定元のつもりで使用した変数になんらかの値が代入されていた場合に混乱を招く, という, 既存のシステムにありがちな状況を避けるためである.

     The names of program variables must begin with a capital letter; while the names of indeterminates and functions must begin with a small letter.

     This is an unique point that differs from almost all other existing computer algebra systems.  The distinction between program variables and indeterminates is adopted to avoid the possible and usual confusion that may arise in a situation where a name is used as an indeterminate but, as it was, the name has been already assigned some value. To use different type of letters, capital and small, was a matter of syntactical convention like Prolog, but it is convenient to distinguish variables and indeterminates in a program.

  * switch 文, goto がない.

     No switch statements, and goto statements.

     goto がないため, 多重ループを一度に抜けるのがやや複雑になる場合がある.

     Lack of goto statement makes it rather bothering to exit from within multiple loops.

  * コンマ式は, for (A;B;C) または, while(A) の A, B, C にのみ使うことができる.

     Comma expressions are allowed only in A, B and C of the constructs for (A;B;C) or while(A).

     これは, リストを正式なオブジェクトとして加えたことによる.

     This limitation came from adopting lists as legal data objects for Asir.

以上は制限であるが, 拡張としては次の点が挙げられる.

The above are limitations; extensions are listed as follows.

  * 有理式に対する計算を, 通常の C における計算と同様にできる.

     Arithmetic for rational expressions can be done in the same manner as is done for numbers in C language.

  * リストが扱える.

     Lists are available for data objects.

     構造体を用いるまでもない要素の集合体を, リストで表すことができ, C で直接書く場合に比較してプログラムが短く, 読みやすく書ける.

     Lists are conveniently used to represent a certain collection of objects. Use of lists enables to write programs more easily, shorter and more comprehensible than use of structure like C programs.

  * ユーザ定義函数における一行ヘルプ.  Emacs-Lisp に類似した機能である. 詳しくは, see ユーザ定義函数 を見よ.

  * ユーザ定義函数におけるオプション指定.

     Options can be specified in calling user defined functions.

     これに関しては, see オプション指定.

     See option.

Asir では次の語句がキーワードとして定められている.

  * C 言語に由来:

     break, continue, do, else, extern, for, if, return, static, struct, while

  * C 言語からの拡張:

     def, endmodule, function, global, local, localf, module

  * 函数:

     car, cdr, getopt, newstruct, map, pari, quote, recmap, timer

ユーザ定義函数の書き方
===========

Writing user defined functions
==============================

ユーザ定義函数
-------

User defined functions
----------------------

ユーザによる函数の定義は def 文で行う. 文法エラーは読み込み時にある程度チェックされ, おおよその場所が表示される. 既に(引数の個数に関係なく)同名の函数が定義されている場合には, その函数は再定義される. ctrl() 函数により verbose フラグが on になっている場合,

To define functions by an user himself, def statement must be used. Syntactical errors are detected in the parsing phase of Asir, and notified with an indication of where Asir found the error. If a function with the same name is already defined (regardless to its arity,) the new definition will override the old one, and the user will be told by a message,

     afo() redefined.

というメッセージが表示される. ある函数の定義において, まだ未定義の函数を呼び出していても, 定義時にはエラーにならない. 実行時に未定義の函数を呼び出そうとした場合にエラーとなる.

on the screen when a flag verbose is set to a non-zero value by ctrl(). Recursive definition, and of course, recursive use of functions are available. A call for an yet undefined function in a function definition is not detected as an error.  An error will be detected at execution of the call of that yet undefined function.

     def f(X) {
         if ( !X )
             return 1;
         else
             return X * f(X-1);
     }

     def c(N)
     {
         A = newvect(N+1); A[0] = B = newvect(1); B[0] = 1;
         for ( K = 1; K <= N; K++ ) {
             A[K] = B = newvect(K+1); B[0] = B[K] = 1;
             for ( P = A[K-1], J = 1; J < K; J++ )
                 B[J] = P[J-1]+P[J];
             }
         return A;
     }

     def add(A,B)
     "add two numbers."
     {
         return A+B;
     }

2 つ目の例では, 長さ N+1 のベクトル (Aとする) が返される. A[I] は長さ I+1 の配列であり, そのそれぞれの要素が

In the second example, c(N) returns a vector, say A, of length N+1.  A[I] is a vector of length I+1, and each element is again a vector which contains

ICJ を要素とする配列である.

as its elements.

3 つ目の例では, 引数並びのあとに文字列が置かれているが、これは Emacs-Lisp の関数定義に類似の機能で、ヘルプ用の文字列である。この例の場合、help(add) によってこの文字列が出力される。

参照

References
     help.

以下では, C によるプログラミングの経験がない人のために, Asir 言語によるプログラムの書き方を解説する.

In the following, the manner of writing Asir programs is exhibited for those who have no experience in writing C programs.

変数および不定元
--------

既に述べた通り, Asir においてはプログラム変数と不定元を明確に区別している.

variables and indeterminates
----------------------------

変数
     大文字で始まり, アルファベット, 数字, _ からなる文字列

variables (program variables)
     A program variable is a string that begins with a capital alphabetical letter followed by any numbers of alphabetical letters, digits and _.

     変数あるいはプログラム変数とは, Asir のさまざまな型の内部形式を格納するための箱であり, 格納された内部形式が, この変数の値である.  変数が式の要素として評価される時は, そこに収められた値に置き換えられる. すなわち, 内部形式の中にはプログラム変数は現れない. 変数は全て 0 で初期化されている.

     A program variable is thought of a box (a carrier) which can contain Asir objects of various types.  The content is called the `value' of that variable.  When an expression in a program is to be evaluated, the variable appearing in the expression is first replaced by its value and then the expression is evaluated to some value and stored in the memory.  Thus, no program variable appears in objects in the internal form. All the program variables are initialized to the value 0.

          [0] X^2+X+1;
          1
          [1] X=2;
          2
          [2] X^2+X+1;
          7

不定元
     小文字で始まり, アルファベット, 数字, _ からなる文字列, またはシングルクオートで囲まれた文字列, もしくは函数形式. 不定元とは, 多項式環を構成する際に添加される変数をいう.  Asir においては, 不定元は値をもたない超越的な元であり, 不定元への値の代入は許されない.

indeterminates
     An indeterminate is a string that begins with a small alphabetical letter followed by any numbers of alphabetical letters, digits and _.

     An indeterminate is a transcendental element, so-called variable, which is used to construct polynomial rings. An indeterminate cannot have any value.  No assignment is allowed to it.

          [3] X=x;
          x
          [4] X^2+X+1;
          x^2+x+1
          [5] A='Dx'*(x-1)+x*y-y;
          (y+Dx)*x-y-Dx
          [6] function foo(x,y);
          [7] B=foo(x,y)*x^2-1;
          foo(x,y)*x^2-1

引数
--

parameters and arguments
------------------------

     def sum(N) {
         for ( I = 1, S = 0; I <= N; I++ )
             S += I;
         return S;
     }

これは, 1 から N までの自然数の和を求める函数 sum() の定義である. この例における sum(N) の N が引数である. この例は, 1 引数函数の例であるが, 一般に引数の個数は任意であり, 必要なだけの個数を , で区切って指定することができる. 引数は値が渡される. すなわち, 引数を受けとった側が, その引数の値を変更しても, 渡した側の変数は変化しない. ただし, 例外がある. それは, ベクトル, 行列を引数に渡した場合である. この場合も, 渡された変数そのものを書き替えることは, その函数に局所的な操作であるが, 要素を書き換えた場合, それは, 呼び出し側のベクトル, 行列の要素を書き換えることになる.

This is an example definition of a function that sums up integers from 1 to N.  The N in sum(N) is called the (formal) parameter of sum(N). The example shows a function of the single argument. In general, any number of parameters can be specified by separating by commas (,). A (formal) parameter accepts a value given as an argument (or an actual parameter) at a function call of the function. Since the value of the argument is given to the formal parameter, any modification to the parameter does not usually affect the argument (or actual parameter).  However, there are a few exceptions: vector arguments and matrix arguments.

Let A be a program variable and assigned to a vector value [ a, b ]. If A is given as an actual parameter to a formal parameter, say V, of a function, then an assignment in the function to the vector element designator V[1], say V[1]=c;, causes modification of the actual parameter A resulting A to have an altered value [ a c ].  Thus, if a vector is given to a formal parameter of a function, then its element (and subsequently the vector itself) in the calling side is modified through modification of the formal parameter by a vector element designator in the called function. The same applies to a matrix argument. Note that, even in such case where a vector (or a matrix) is given to a formal parameter, the assignment to the whole parameter itself has only a local effect within the function.

     def clear_vector(M) {
         /* M is expected to be a vector */
         L = size(M)[0];
         for ( I = 0; I < L; I++ )
             M[I] = 0;
     }

この函数は, 引数のベクトルを 0 ベクトルに初期化するための函数である. また, ベクトルを引数に渡すことにより, 複数の結果を引数のベクトルに収納して返すことができる. 実際には, このような場合には, 結果をリストにして返すこともできる. 状況に応じて使いわけすることが望ましい.

This function will clear off the vector given as its argument to the formal parameter M and return a 0 vector.

Passing a vector as an argument to a function enables returning multiple results by packing each result in a vector element. Another alternative to return multiple results is to use a list. Which to use depends on cases.

コメント
----

comments
--------

C と同様 /* と */ で囲まれた部分はコメントとして扱われる.

The text enclosed by /* and */ (containing /* and */) is treated as a comment and has no effect to the program execution as in C programs.

     /*
      * This is a comment.
      */

     def afo(X) {

コメントは複数行に渡っても構わないが, 入れ子にすることはできない. /* がいくつあっても最初のもののみが有効となり, 最初に現れた */ でコメントは終了したと見なされる. プログラムなどで, コメントを含む可能性がある部分をコメントアウトした場合には, #if 0, #endifを使えばよい. (see プリプロセッサ.)

A comment can span to several lines, but it cannot be nested. Only the first /* is effective no matter how many /*'s in the subsequent text exist, and the comment terminates at the first */.

In order to comment out a program part that may contain comments in it, use the pair, #if 0 and #endif. (see preprocessor.)

     #if 0
     def bfo(X) {
     /* empty */
     }
     #endif

文
-

statements
----------

Asir のユーザ函数は,

     def 名前(引数,引数,...,引数) {
         文
         文
         ...
         文
     }

     An user function of Asir is defined in the following form.

          def name(parameter, parameter,...,parameter) {
              statement
              statement
              ...
              statement
          }

     という形で定義される. このように, 文は函数の基本的構成要素であり, プロ
     グラムを書くためには, 文がどのようなものであるか知らなければならない.
     最も単純な文として, 単文がある. これは,

     As you can see, the statement is a fundamental element of the
     function.
     Therefore, in order to write a program, you have to learn what
     the statement is.  The simplest statement is the simple statement.
     One example is an expression with a terminator (; or $.)

          S = sum(N);

     のように, 式に終端記号 (; または $) をつけたものである.
     この単文及び類似の return 文, break 文などが文の最小構成
     単位となる. if 文や for 文の定義 (文法の詳細) を見れ
     ばわかる通り, それらの本体は, 単なる一つの文として定義されている. 通常
     は, 本体には複数の文が書けることが必要となる.  このような場合,
     { と } で文の並びを括って, 一つの文として扱うことがで
     きる. これを複文と呼ぶ.

     A `return statement' and `break statement' are also
     primitives to construct `statements.'
     As you can see the syntactic definition of `if statement' and
     `for statement', each of their bodies consists of a single
     `statement.'  Usually, you need several statements in such a body.
     To solve this contradictory requirement, you may use the `compound
     statement.'  A `compound statement' is a sequence of `statement's
     enclosed by a left brace { and a right brace }.
     Thus, you can use multiple statement as if it were a single statement.

          if ( I == 0 ) {
              J = 1;
              K = 2;
              L = 3;
          }

     } の後ろには終端記号は必要ない. なぜなら, { 文並び
     }が既に文となっていて, if 文の要請を満たしているからで
     ある.

     No terminator symbol is necessary after },
     because { statement sequence } already forms a statement,
     and it satisfies the syntactical requirement of the
     `if statement.'

return 文
--------

return statement
----------------

return 文は,

There are two forms of return statement.

     return 式;

     return expression;

     return;

の 2 つの形式がある. いずれも函数から抜けるための文である. 前者は函数の値として 式 を返す. 後者では, 函数の値として何が返されるかはわからない.

Both forms are used for exiting from a function. The former returns the value of the expression as a function value. The function value of the latter is not defined.

if 文
----

if statement
------------

if 文には

There are two forms of if statement.

     if ( 式 )             if ( 式 )
         文       及び         文
     else
         文

     if ( expression )             if ( expression )
          statement       and           statement
     else
          statement

の 2 種類がある. これらの動作は明らかであるが, 文の位置に if 文が来た場合に注意を要する. 次の例を考えてみよう.

The interpretation of these forms are obvious.  However, be careful when another if statement comes at the place for `statement'. Let us examine the following example.

     if ( 式 )
         if ( 式 ) 文
     else
         文

     if ( expression1 )
         if ( expression2 ) statement1
     else
         statement2

この場合, 字下げからは, else 以下は, 最初の if に対応するように見えるが, パーザは, 自動的に 2 番目の if に対応すると判断する. すなわち, 2 種類の if 文を許したために, 文法に曖昧性が現れ, それを解消するために, else 以下は, 最も近い if に対応するという規則が適用されるのである. 従って, この例は,

One might guess statement2 after else corresponds with the first if ( expression1 ) by its appearance of indentation. But, as a matter of fact, the Asir parser decides that it correspond with the second if ( expression2 ). Ambiguity due to such two kinds of forms of if statement is thus solved by introducing a rule that a statement preceded by an else matches to the nearest preceding if.

Therefore, rearrangement of the above example for improving readability according to the actual interpretation gives the following.

     if ( 式 ) {
         if ( 式 ) 文 else 文
     }

     if ( expression1 ) {
         if ( expression2 ) statement1 else statement2
     }

という意味となる. 字下げに対応させるためには,

On the other hand, in order to reflect the indentation, it must be written as the following.

     if ( 式 ) {
         if ( 式 ) 文
     } else
         文

     if ( expression1 ) {
         if ( expression2 ) statement1
     } else
         statement2

としなければならない.

関数の中でなく, top level で if 文を用いるときは $ または ; で終了する必要がある. これらがないと次の文がよみとばされる.

When if is used in the top level, the if expression should be terminated with $ or ;. If there is no terminator, the next expression will be skipped to be evaluated.

ループ, break, return, continue
----------------------------

loop, break, return, continue
-----------------------------

ループを構成する文は, while 文, for 文, do 文の 3 種類がある.

There are three kinds of statements for loops (repetitions): the while statement, the for statement, and  the do statement.

  * while 文

     while statement

     形式は,

     It has the following form.

          while ( 式 ) 文

          while ( expression ) statement

     で, これは, 式 を評価して, その値が 0 でない限り 文 を実行するという意味となる. たとえば 式 が 1 ならば, 単純な無限ループとなる.

     This statement specifies that statement is repeatedly evaluated as far as the expression evaluates to a non-zero value. If the expression 1 is given to the expression, it forms an infinite loop.

  * for 文

     for statement

     形式は,

     It has the following form.

          for ( 式並び-1; 式; 式並び-2 ) 文

          for ( expr list-1; expr; expr list-2 ) statement

     で, これは

     This is equivalent to the program

          式並び-1 (を単文並びにしたもの)
          while ( 式 ) {
              文
              式並び-2 (を単文並びにしたもの)
          }

          expr list-1 (transformed into a sequence of simple statement)
          while ( expr ) {
              statement
              expr list-2 (transformed into a sequence of simple statement)
          }

     と等価である.

  * do 文

     do statement

          do {
              文
          } while ( 式 )

          do {
              statement
          } while ( expression )

     は, 先に 文を実行してから条件式による判定を行う所が while 文と異なっている.

     This statement differs from while statement by the location of the termination condition: This statement first execute the statement and then check the condition, whereas while statement does it in the reverse order.

ループを抜け出す手段として, break 文及び return 文がある. また, ループの制御をある位置に移す手段として continue 文がある.

As means for exiting from loops, there are break statement and return statement.  The continue statement allows to move the control to a certain point of the loop.

  * break

     break 文は, それを囲むループを一つだけ抜ける.

     The break statement is used to exit the inner most loop.

  * return

     return 文は, 一般に函数から抜けるための文であり, ループの中からでも有効である.

     The return statement is usually used to exit from a function call and it is also effective in a loop.

  * continue

     continue 文は, ループの本体の文の末端に制御を移す. 例えば for 文では, 最後の式並びの実行を行い, while 文では条件式の判定に移る.

     The continue statement is used to move the control to the end point of the loop body. For example, the last expression list will be evaluated in a for statement, and the termination condition will be evaluated in a while statement.

構造体定義
-----

structure definition
--------------------

構造体とは, 各成分の要素が名前でアクセスできる固定長配列と思ってよい. 各構造体は名前で区別される. 構造体は, struct 文により宣言される. 構造体が宣言されるとき, asir は内部で構造体のそれぞれの型に固有の識別番号をつける.  この番号は, 組み込み関数 struct_type により取得できる. ある型の構造体は, 組み込み関数 newstruct により生成される. 構造体の各メンバは, 演算子 -> によりアクセスする. メンバが構造体の場合, -> による指定は入れ子にできる.

A structure data type is a fixed length array and each component of the array is accessed by its name. Each type of structure is distinguished by its name. A structure data type is declared by struct statement. A structure object is generated by a builtin function newstruct. Each member of a structure is accessed by an operatator ->. If a member of a structure is again a structure, then the specification by -> can be nested.

     [1] struct rat {num,denom};
     0
     [2] A = newstruct(rat);
     {0,0}
     [3] A->num = 1;
     1
     [4] A->den = 2;
     2
     [5] A;
     {1,2}
     [6] struct_type(A);
     1

参照

References
     newstruct, struct_type

さまざまな式
------

various expressions
-------------------

主な式の構成要素としては, 次のようなものがある.

Major elements to construct expressions are the following:

  * 加減乗除, 冪

     addition, subtraction, multiplication, division, exponentiation

     冪は, ^ により表す. 除算 / は, 体としての演算に用いる. 例えば, 2/3 は有理数の 2/3 を表す. 整数除算, 多項式除算 (剰余を含む演算) には別途組み込み函数が用意されている.

     The exponentiation is denoted by ^. (This differs from C language.) Division denoted by / is used to operate in a field, for example, 2/3 results in a rational number 2/3. For integer division and polynomial division, both including remainder operation, built-in functions are provided.

          x+1  A^2*B*afo X/3

  * インデックスつきの変数

     programming variables with indices

     ベクトル, 行列, リストの要素はインデックスを用いることにより取り出せる. インデックスは 0 から始まることに注意する. 取り出した要素がベクトル, 行列, リストなら, さらにインデックスをつけることも有効である.

     An element of a vector, a matrix or a list can be referred to by indexing. Note that the indices begin with number 0.  When the referred element is again a vector, a matrix or a list, repeated indexing is also effective.

          V[0] M[1][2]

  * 比較演算

     comparison operation

     等しい (==), 等しくない (!=), 大小 (>, <, >=, <=) の 2 項演算がある. 真ならば有理数の 1, 偽ならば 0 を値に持つ.

     There are comparison operations == for equivalence, != for non-equivalence, >, <,>=, and <= for larger or smaller. The results of these operations are either value 1 for the truth, or 0 for the false.

  * 論理式

     logical expression

     論理積 (&&), 論理和 (||) の 2 項演算と, 否定 (!) が用意されている. 値はやはり 1, 0 である.

     There are two binary logical operations && for logical conjunction(and), || for logical disjunction(or), and one unary logical operation ! for logical negation(not). The results of these operations are either value 1 for the truth, and 0 for the false.

  * 代入

     assignment

     通常の代入は = で行う. このほか, 算術演算子と組み合わせて特殊な代入を行うこともできる.

     Value assignment of a program variable is usually done by =. There are special assignments combined with arithmetic operations.

     (+=, -=, *=, /=, ^=)

          A = 2  A *= 3 (これは A = A*3 と同じ; その他の演算子も同様)

          A = 2  A *= 3 (the same as A = A*3; The others are alike.)

  * 函数呼び出し

     function call

     函数呼び出しも式の一種である.

     A function call is also an expression.

  * ++, --

     これらは, 変数の前後について, それぞれ次のような操作, 値を表す.

     These operators are attached to or before a program variable, and denote special operations and values.

          A++  値は元の A の値, A = A+1
          A--  値は元の A の値, A = A-1
          ++A  A = A+1, 値は変化後の値
          --A  A = A-1, 値は変化後の値

          A++  the expression value is the previous value of A, and A = A+1
          A--  the expression value is the previous value of A, and A = A-1
          ++A  A = A+1, and the value is the one after increment of A
          --A  A = A-1, and the value is the one after decrement of A

プリプロセッサ
-------

preprocessor
------------

Asir のユーザ言語は C 言語を模したものである. C の特徴として, プリプロセッサ cpp によるマクロ展開, ファイルのインクルードがあるが, Asir においてもユーザ言語ファイルの読み込みの際 cpp を通してから読み込むこととした. これによりユーザ言語ファイル中で #include, #define, #if などが使える.

he Asir user language imitates C language.  A typical features of C language include macro expansion and file inclusion by the preprocessor cpp.  Also, Asir read in user program files through cpp.  This enables Asir user to use #include, #define, #if etc. in his programs.

  * #include

     UNIX では インクルードファイルは, Asir のライブラリディレクトリ (環境変数 ASIR_LIBDIR で指定されたディレクトリ) と #include が書かれているファイルと同じディレクトリをサーチする. UNIX 以外では cpp に特に引数を渡さないため, #include が書かれているファイルと同じディレクトリのみをサーチする.

     Include files are searched within the same directory as the file containing #include so that no arguments are passed to cpp.

  * #define

     これは, C におけるのと全く同様に用いることができる.

     This can be used just as in C language.

  * #if

     /*, */ によるコメントは入れ子にできないので, プログラムの大きな部分をコメントアウトする際に, #if 0, #endif を使うと便利である.

     This is conveniently used to comment out a large part of a user program that may contain comments by /* and */, because such comments cannot be nested.

次の例は, defs.h にあるマクロ定義である.

the following are the macro definitions in defs.h.

     #define ZERO 0
     #define NUM 1
     #define POLY 2
     #define RAT 3
     #define LIST 4
     #define VECT 5
     #define MAT 6
     #define STR 7
     #define N_Q 0
     #define N_R 1
     #define N_A 2
     #define N_B 3
     #define N_C 4
     #define V_IND 0
     #define V_UC 1
     #define V_PF 2
     #define V_SR 3
     #define isnum(a) (type(a)==NUM)
     #define ispoly(a) (type(a)==POLY)
     #define israt(a) (type(a)==RAT)
     #define islist(a) (type(a)==LIST)
     #define isvect(a) (type(a)==VECT)
     #define ismat(a) (type(a)==MAT)
     #define isstr(a) (type(a)==STR)
     #define FIRST(L) (car(L))
     #define SECOND(L) (car(cdr(L)))
     #define THIRD(L) (car(cdr(cdr(L))))
     #define FOURTH(L) (car(cdr(cdr(cdr(L)))))
     #define DEG(a) deg(a,var(a))
     #define LCOEF(a) coef(a,deg(a,var(a)))
     #define LTERM(a) coef(a,deg(a,var(a)))*var(a)^deg(a,var(a))
     #define TT(a) car(car(a))
     #define TS(a) car(cdr(car(a)))
     #define MAX(a,b) ((a)>(b)?(a):(b))

C のプリプロセッサを流用しているため, プリプロセッサは $ を正しく処理できない. たとえば LIST が定義されていても LIST$は置換されない. $ の前に空白をおいて LIST $ と書かないといけない.

Since we are utilizing the C preprocessor, it cannot properly preprocess expressions with $. For example, even if LIST is defined, LIST in the expression LIST$ is not replaced.  Add a blank before $, i.e., write as LIST $ to make the proprocessor replace it properly.

オプション指定
-------

option
------

ユーザ定義関数が N 変数で宣言された場合, その関数は, N 変数での呼び出しのみが許される.

If a user defined function is declared with N arguments, then the function is callable with N arguments only.

     [0] def factor(A) { return fctr(A); }
     [1] factor(x^5-1,3);
     evalf : argument mismatch in factor()
     return to toplevel

不定個引数の関数をユーザ言語で記述したい場合, リスト, 配列を用いることで可能となるが, 次のようなより分かりやすい方法も可能である.

A function with indefinite number of arguments can be realized by using a list or an array as its argument. Another method is available as follows:

     % cat factor
     def factor(F)
     {
         Mod = getopt(mod);
         ModType = type(Mod);
         if ( ModType == 1 ) /* 'mod' is not specified. */
             return fctr(F);
         else if ( ModType == 0 ) /* 'mod' is a number */
             return modfctr(F,Mod);
     }

     [0] load("factor")$
     [1] factor(x^5-1);
     [[1,1],[x-1,1],[x^4+x^3+x^2+x+1,1]]
     [2] factor(x^5-1|mod=11);
     [[1,1],[x+6,1],[x+2,1],[x+10,1],[x+7,1],[x+8,1]]

2 番目の factor() の呼び出しにおいて, 関数定義の際に宣言された引数 x^5-1の後ろに |mod=11 が置かれている. これは, 関数実行時に, mod という keyword に対して 11 という値を割り当てることを指定している. これをオプション指定と呼ぶことにする. この値は getopt(mod) で取り出すことができる. 1 番目の呼び出しのように mod に対するオプション指定がない場合には, getopt(mod) は型識別子 -1 のオブジェクトを返す. これにより, 指定がない場合の動作を if 文により記述できる. | の後ろには, 任意個のオプションを, , で区切って指定することができる.

In the second call of factor(), |mod=11 is placed after the argument x^5-1, which appears in the declaration of factor(). This means that the value 11 is assigned to the keyword mod when the function is executed. The value can be retrieved by getopt(mod). We call such machinery OPTION. If the option for MOD is not specified, getopt(mod) returns an object whose type is -1. By this feature, one can describe the behaviour of the function when the option is not specified by IF statements. After | one can append any number of options seperated by ,.

     [100] xxx(1,2,x^2-1,[1,2,3]|proc=1,index=5);

さらに, オプションを key1=value1,key2=value2,... のように , で区切って渡す代わりに, 特別なキーワード option_list とオプションリスト [["key1",value1],["key2",value2],...] を用いて渡すことも可能である.

Optinal arguments may be given as a list with the key word option_list as option_list=[["key1",value1],["key2",value2],...]. It is equivalent to pass the optional arguments as key1=value1,key2=value2,....

     [101] dp_gr_main([x^2+y^2-1,x*y-1]|option_list=[["v",[x,y]],["order",[[x,5,y,1]]]]);

特に, 引数なしの getopt() はオプションリストを返すので, オプションをとる関数から, オプションをとる関数を呼び出すときには有用である.

Since getopt() returns an option list, the optional argument option_list=... is useful when we call functions with optional arguments from a function with optional arguments to pass the all optional parameters.

     % cat foo.rr
     def foo(F)
     {
         OPTS=getopt();
         return factor(F|option_list=OPTS);
     }

     [3] load("foo.rr")$
     [4] foo(x^5-1|mod=11);
     [[1,1],[x+6,1],[x+2,1],[x+10,1],[x+7,1],[x+8,1]]

モジュール
-----

module
------

ライブラリで定義されている関数, 変数をカプセル化する仕組みがモジュール (module) である. はじめにモジュールを用いたプログラムの例をあげよう.

Function names and variables in a library may be encapsulated by module. Let us see an example of using module

     module stack;

     static Sp $
     Sp = 0$
     static Ssize$
     Ssize = 100$
     static Stack $
     Stack = newvect(Ssize)$
     localf push $
     localf pop $

     def push(A) {
       if (Sp >= Ssize) {print("Warning: Stack overflow\nDiscard the top"); pop();}
       Stack[Sp] = A;
       Sp++;
     }
     def pop() {
       local A;
       if (Sp <= 0) {print("Stack underflow"); return 0;}
       Sp--;
       A = Stack[Sp];
       return A;
     }
     endmodule;

     def demo() {
       stack.push(1);
       stack.push(2);
       print(stack.pop());
       print(stack.pop());
     }

モジュールは module モジュール名  〜 endmoduleで囲む. モジュールは入れ子にはできない. モジュールの中だけで使う大域変数は static で宣言する. この変数はモジュールの外からは参照もできないし変更もできない. モジュールの外の大域変数は extern で宣言する.

Module is encapsulated by the sentences module module name and endmodule. A variable of a module is declared with the key word static. The static variables cannot be refered nor changed out of the module, but it can be refered and changed in any functions in the module. A global variable which can be refered and changed at any place is declared with the key word extern.

モジュール内部で定義する関数は localf を用いて宣言しないといけない. 上の例では push と pop を宣言している. この宣言は必須である.

Any function defined in a module must be declared forward with the keyword localf. In the example above, push and pop are declared. This declaration is necessary.

モジュール moduleName で定義された関数 functionName をモジュールの外から呼ぶには moduleName.functionName(引数1, 引数2, ... ) なる形式でよぶ. モジュールの中からは, 関数名のみでよい. 次の例では, モジュールの外からモジュール stack で定義された関数 push, pop を呼んでいる.

A function functionName defined in a module moduleName can be called by the expression moduleName.functioName(arg1, arg2, ...) out of the module. Inside the module, moduleName. is not necessary. In the example below, the functions push and pop defined in the module stack are called out of the module.

      stack.push(2);
      print( stack.pop() );
      2

モジュールで用いる関数名は局所的である. つまりモジュールの外や別のモジュールで定義されている関数名と同じ名前が利用できる.

Any function name defined in a module is local. In other words, the same function name may be used out of the module to define a different function.

モジュール機能は大規模ライブラリの開発を想定している. ライブラリを必要に応じて分割ロードするには, 関数 module_definedp を用いるのが便利である. デマンドロードはたとえば次のように行なえば良い.

The module structure of asir is introduced to develop large libraries. In order to load libraries on demand, the command module_definedp will be useful. The below is an example of demand loading.

     if (!module_definep("stack")) load("stack.rr") $

asir では局所変数の宣言は不要であった. しかしモジュール stack の例を見れば分かるように, local A; なる形式で局所変数を宣言できる. キーワード local を用いると, 宣言機能が有効となる. 宣言機能を有効にすると, 宣言されてない変数はロードの段階でエラーを起こす. 変数名のタイプミスによる予期しないトラブルを防ぐには, 宣言機能を有効にしてプログラムするのがよい.

It is not necessary to declare local variables in asir. As you see in the example of the stack module, we may declare local variables by the key word local. Once this key word is used, asir requires to declare all the variables. In order to avoid some troubles to develop a large libraries, it is recommended to use local declarations.

モジュール内の関数をそのモジュールが定義される前に呼び出すような関数を書くときには, その関数の前でモジュールを次のようにプロトタイプ宣言しておく必要がある.

When we need to call a function in a module before the module is defined, we must make a prototype declaration as the example below.

     /* Prototype declaration of the module stack */
     module stack;
     localf push $
     localf pop $
     endmodule;

     def demo() {
       print("----------------");
       stack.push(1);
       print(stack.pop());
       print("---------------");
     }

     module stack;
       /* The body of the module stack */
     endmodule;

モジュールの中からトップレベルで定義されている関数を呼ぶには, 下の例のように :: を用いる.

In order to call functions defined in the top level from the inside of a module, we use :: as in the example below.

     def afo() {
       S = "afo, afo";
       return S;
     }
     module abc;
     localf foo,afo $

     def foo() {
       G = ::afo();
       return G;
     }
     def afo() {
       return "afo, afo in abc";
     }
     endmodule;
     end$

     [1200] abc.foo();
     afo, afo
     [1201] abc.afo();
     afo, afo in abc

参照

References
     module_list, module_definedp, remove_module.