Mは変数の型やサイズの事前の定義は必要としません。Mの変数は、ローカルまたはグローバルです。変数は、添字なしか添字があるでしょう。
Mでは、変数の添字は、散在配列の要素を識別します。散在配列は、存在している添字とデータノードで構成します。スペースは、潜在的データノードのために全く予約されません。これらの配列は、一般的に、論理的に役に立ちます。それは、数学的と言うよりむしろ、目的です。
M配列の添字は式であり、数値によって制限はされません。
Mグローバルまたはローカル変数のフォーマット:
[^]name[(expr1[,...])]
キャレット記号 (^)にて先行するオプションは、グローバル変数をあらわします。
名前は、特定の配列を指定します。
オプションの式は、添字を指定し、かっこで囲み、コンマ (,) で区切られる必要があります。
M標準の本体は、変数名に全く制限無く配置します。ただし、標準における移植性セクションでは、個々の添字式の長さと変数名の合計の長さでは、制限をお勧めしません。名前の長さの計測は、グローバル変数名自体の長さと、すべての評価添字の長さの合計と、添字の数の2倍のオーバーヘッドを差し引いた値を含みます。合計は237を超えていけません。グローバルについて、GT.Mは、GDEによって変更でき、この合計を最大255まで許可します。ローカル変数については、GT.Mは、最大32,767長の文字列に、個々の添え字の長さを制限します。GT.Mは、ローカル変数もグローバル変数も添字の数を31個までに制限します。
Mは、非正規の数値を表すその先行するゼロのような文字列を含むすべての文字列添字より先に、すべての正規な数値添字を照合します。数値添字はマイナスからプラスの値の順序で照合します。文字列の添字は、ASCIIの順序で照合します。また、GT.Mは、ほとんどのコンテキストで空の文字列の添字が可能です(nullまたは空の文字列は、すべての正規数値添字より先んじて照合します)。
GT.Mは、代替照合シーケンスの定義が可能です。この機能を有効にする方法の完全な解説については、 第12章:" 国際化 " を参照してください。
Mのローカル変数は、1つのプロセスの範囲内でのみ単独で使用する変数のことを指します。ローカル変数名は、先頭に区切り文字は全くありません。
Mは、変数が最初にSETされる時からKILLされるまで、または、Mを実行しているプロセスが停止するまでのプロセス内で、実行されるすべてのルーチンにより変更することを条件として利用可能となるローカル変数にしるしをつけます。ただし、NEWコマンドへの引数としてその変数が表す後に、または、パラメータパッシングで形式的に使用される要素として表す後に、Mは、ローカル変数を "保護 (protects) " します。Mがローカル変数を保護する時、それは変数の値のコピーを保存し、その変数は未定義になります。Mは、 "NEWまたは形式的な"保護"にて関連付けらたプロセススタックレベルを終了するQUITの実行中に、その保存された値へ変数を復元します。NEWとQUITの詳細は、 第6章:" コマンド " を参照してください。
Mは、ローカル変数へ次のように変数の用途を制限します:
FOR コマンドの制御変数
"排他的 (exclusive) " KILL のかっこ内の要素
TSTART [ローカル変数のリスト]
引数なしKILLは、現在のすべてのローカル変数を削除します。
NEWコマンドの引数
実際の名前 (Actualnames)は、参照渡しパラメータパッシングによって使用されます。
Mは、類似のシンタックスを持つグローバル名またはロックのリソース名(nrefs)にて、オプションの環境仕様を認識します。グローバル変数名は、区切り記号として先導するキャレット記号(^)があります。
Mでは、その変数が最初のSETされる時からKILLされるまで、環境ですべてのプロセス内で実行されるすべてのルーチンによって変更することを条件として、グローバル変数が使用可能になります。
Mは、いくつかの状況下でグローバル名の省略を受け入れます。先導するキャレット記号(^)が、すぐに、添字を区切る左カッコよりも先になる時、グローバル変数の参照はネイキッド参照(Naked References)と呼びます。Mは、ネイキッド参照(Naked References)によって指定された添字リストへ、その最後の添字を除き、最後に使用したグローバル変数名の接頭辞によって、ネイキッド参照(Naked References)を評価します。接頭辞部分はネイキッド指標(naked indicator)として知られています。事前にグローバル参照が存在しない時、または、添字を含んでいない時、ネイキッド参照(Naked Reference)を使用する試行は、エラーを生成します。
Mは、グローバル変数のすべての評価の副作用として維持できる、ただ1つのプロセス全体のネイキッド指標(naked indicator)があるので、ネイキッド参照(Naked Reference)の使用は、Mの実行シーケンスの理解が必要です。Mの実行は一般的に、制御の流れを変更する命令を仮定して、1行内で左から右に進みます。しかし、Mは、左側を評価する前に、等号記号の右側にあるSETコマンドの引数の部分を評価します。また、Mは、真として選択された引数を検出した後は、$select()関数の引数をそれ以上は評価しません。
一般的に、非常に限定された状況でのみネイキッド参照(Naked Reference)を使用することは、ネイキッド指標(naked indicator) に関連する問題を防ぎます。
Mは、グローバル名のオプション環境仕様を認識します。環境仕様は、1つを代替データベースファイルのいくつかのセットの1つを命名します。
環境仕様を含むグローバル変数名のシンタックスは:
^|expr|name[(subscript[,...])]
GT.Mでは、式は、グローバル変数をマッピングするためのグローバルディレクトリを識別します。
環境仕様は、現在のデータベース内のアクティブな変数の他の"コピー"を含む、代替データベース内のグローバル変数に簡単なアクセスを許可します。環境仕様は、時々、拡張されたグローバルシンタックス(extended global syntax)または拡張された値のシンタックス(extended value syntax)として参照されます。
GT.Mも可能です:
^|expr1,expr2|name[(subscript[,...])]
最初の式では、グローバルディレクトリを識別する場所ですが、2番目の式は受け入れられますがGT.M.によって無視されます。
いくつかの他のM実装との互換性を向上させるために、GT.Mはまた、別の非標準シンタックスを受け入れます。このシンタックスでは、先頭とアップバー (|)の末尾は、それぞれ、左角括弧([)、右角括弧(])に置き換えられます。このシンタックスは、また、式と言うよりむしろexpratoms (アトム式) が必要です。 expratoms の詳細については、 "式" を参照してください。
非標準シンタックスのフォーマットは:
^[expratom1]name[(subscript...)]
または
^[expratom1,expratom2]name[(subscript...)]
expratom1は、どこでグローバルディレクトリを識別するかの場所で、expratom2は、ダミー変数です。それぞれのフォーマットで括弧の最初のセットは、シンタックスの一部であることに注意してください。角括弧の2番目のセットは、オプショナルな要素を識別しているメタ言語の一部です。
例:
$ gtmgbldir=Test.GLD $ export gtmgbldir $ GTM GTM>WRITE $ZGBLDIR TEST.GLD GTM>WRITE ^A THIS IS ^A IN DATABASE RED GTM>WRITE ^|"M1.GLD"|A THIS IS ^A IN DATABASE WHITE GTM>WRITE $ZGBLDIR TEST.GLD GTM>HALT $ echo gtmgbldir TEST.GLD
WRITE ^"M1.GLD"A ステートメントは、グローバルディレクトリ M1.GLD の 変数 ^A を出力しますが、現在のグローバルディレクトリを変更しません。
例:
GTM>WRITE $ZGBLDIR M1.GLD GTM>WRITE ^A THIS IS ^A IN DATABASE WHITE GTM>WRITE ^|"M1.GLD"|A THIS IS ^A IN DATABASE WHITE
WRITE ^"M1.GLD"A ステートメントは、 WRITE ^A と同等です。
別のグローバルディレクトリの指定は、いつも別のデータベースを使用して変換するとは限りません。
例:
GTM>WRITE ^|"M1.GLD"|A,!,^|"M2.GLD"|A,!,^|"M3.GLD" |A,! THIS IS ^A IN DATABASE WHITE THIS IS ^A IN DATABASE BLUE THIS IS ^A IN DATABASE WHITE
この例では、WRITEは、3つのGT.Mデータベースファイルから ^A を表示しません。グローバルディレクトリエディタ(GDE)で指定されたマッピングは、グローバルディレクトリを指し示すデータベースファイルを決定します。
この結果は、次のマッピングの状況下で発生した可能性があります:
^|"M1.GLD"|A --> REGIONA --> SEGMENTA --> FILE1.DAT ^|"M2.GLD"|A --> REGIONA --> SEGMENT1 --> FILE2.DAT ^|"M3.GLD"|A --> REGION3 --> SEGMENT3 --> FILE1.DAT
グローバルディレクトリの詳細については、 GT.M管理および操作ガイド の "グローバル ディレクトリ エディタ" の章を参照してください。
環境仕様にある非グローバルディレクトリ情報(通常はUCIとVOL)から、グローバルディレクトリを動的(実行時)に特定したいユーザのために、GT.Mは適切な解釈を追加するインタフェースを提供します。
この機能の使用は、環境仕様を使用するすべてのグローバルアクセスのパフォーマンスに影響を与えます。グローバルディレクトリの静的な特定が可能でないときにのみそれを使用することを確認してください。使用した時には、変換ルーチンは非常に効率的に保つためにあらゆる努力をする。
この機能の使用は、以下のエントリポイントを持つ 共有ライブラリ のパスが含まれている 環境変数 gtm_env_translate の定義によって有効になります:
もし共有オブジェクトにアクセスできない場合、または、エントリポイントにアクセスできない場合は、GT.Mはエラーを報告します。
gtm_env_xlate() ルーチンは、次のCプロトタイプを持ちます。
int gtm_env_xlate(gtm_string_t *in1, gtm_st ring_t *in2, gtm_string *in3, gtm_string_t *out)
where gtm_string_t is a structure defined in gtmxc_types.h as follows:
typedef struct { int length; char *address; }gtm_string_t;
機能の目的は、GT.M.によって環境仕様として使用可能な出力引数を派生し返すために、その3つの入力引数を使用することです。渡される入力値(IN1、IN2、IN3)は、Mの評価の結果であり、そして、けっして変更しないことに、注意してください。最初の2つの引数はアップバー(up-bars) "| |" または角括弧 "[ ]" で渡された式で、そして、3番目の引数は、$ZDIRECTORYで説明したように、現在の作業ディレクトリです。
ゼロ(0)以外の戻り値は、変換の誤りを示しGT.Mエラーで報告されます。
もし出力引数の長さがゼロで無い場合は、GT.Mは出力構造体のアドレスで検索されたテキストを含む、GTM-I-TEXTの2番目のメッセージを追加します。
GT.Mは出力引数に関連したメモリ管理を行っていません - 出力のためのメモリスペースは外部ルーチンによって割り当てる必要があります。ルーチンはそれが割り当てられたアドレスで返される環境の仕様を置くべきです、そして、それに応じて長さを調整する必要があります。正常に返された場合は、戻り値はゼロでなければなりません。もし変換ルーチンがGT.Mにエラーを伝達する必要がある場合、それは、ゼロ以外の値を返さなければなりません、そして、もしそれが追加のエラー情報を伝達する場合、環境が正常に行い、そして、エラーテキストの長さにマッチする長さに調整されるアドレスでエラーテキストを配置します。
戻り値の長さは、0から32767の範囲であり、そうでなければ、GT.Mはエラーを報告します。
長さがゼロ(空)の文字列は、$ZGBLDIRの現在の値を指定します。ゼロ以外の長さは、任意の <NUL> ターミネータを含まない、アドレスが指すファイル仕様の実際の長さを表している必要があります。もし出力引数のアドレスフィールドがNULLならば、GT.Mはエラーを発行します。
ファイル指定は、絶対パスでも相対パスでもかまいませんし、環境変数を含めることができます。もし指定されたファイルにアクセスできない場合、または、有効なグローバルディレクトリでない場合、GT.Mは、それが無効なグローバルディレクトリと同様にエラーを報告します。
それは、Mでこのルーチンを(call-in のように)書くことは可能ですが、しかし、そのようなルーチンでのグローバル変数は、環境参照が正常でないネイキッドインジケータを変更します。アプリケーションの規則に応じて、Mルーチンによって使用されるローカル変数を保護するような、名前空間の管理の問題が困難である可能性があります。
アプリケーション設計者が特定のインターフェイス定義の内で適当と認める任意のフォームを取るためにこのルーチンは可能ですが、次の段落では、ひとにぎりのグローバルリファレンスより多く呼び出されるルーチンが効率的であることの期待に基づいて、いくつかの勧告をします。
コンパイル時またはそれが最初に呼び出される時のどちらかの時に、ルーチンが1つ以上のテーブルをロードすることを期待します。ルーチンのロジックは、テーブルのセットのエントリ上でルックアップを実行します。ルックアップは、適切な衝突の規定で、文字列の長さと名前またはハッシュの中にある文字のいくつかのユニークなセットに基づく場合があります。
ルーチンは、入力の1つまたは両方がゼロの長さを持っているケースに対処する必要があります。これらのケースのサブセットは、2つの入力文字列(入力文字列がけっして変更されることがないことに注意)に相当するものとして再解釈する必要があるコンマ限られた文字列を保持している最初の文字列を持っている場合があります。ルーチンはまた、値(おそらく最初の)が、誤ってまたは故意に、すでにグローバルディレクトリ仕様であるケースを処理する必要があります。
例:
$ cat gtm_env_translate.c #include <stdio.h> #include <string.h> #include "gtmxc_types.h" static int init = 0; typedef struct { gtm_string_t field1, field2, ret; } line_entry ; static line_entry table[5], *line, linetmp; /* Since these errors may occur before setup is complete, they are statics */ static char *errorstring1 ="Error in function initialization, environment variable GTM_CALLIN_START not defined. Environment translation failed."; static char *errorstring2 ="Error in function initialization, function pointers could not be determined. Envrironment translation failed."; #define ENV_VAR"GTM_CALLIN_START" typedef int(*int_fptr)(); int_fptr GTM_MALLOC; int init_functable(gtm_string_t *ptr) { /* This function demonstrates the initialization of other function pointers as well (if the user-code needs them for any reason, they should be defined as globals) */ char *pcAddress; long lAddress; void **functable; void (*setup_timer) (); void (*cancel_timer) (); pcAddress = getenv(ENV_VAR); if (pcAddress == NULL) { ptr->length = strlen(errorstring1); ptr->address = errorstring1; return 1; } lAddress = -1; lAddress = atol(pcAddress); if (lAddress == -1) { ptr->length = strlen(errorstring2); ptr->address = errorstring2; return 1; } functable = (void *)lAddress; setup_timer = (void(*)()) functable[2]; cancel_timer = (void(*)()) functable[3]; GTM_MALLOC = (int_fptr) functable[4]; return 0; } void copy_string(char **loc1, char *loc2, int length) { char *ptr; ptr = (char *) gtm_malloc(length); strncpy( ptr, loc2, length); *loc1 = ptr; } int init_table(gtm_string_t *ptr) { int i = 0; char buf[100]; char *buf1, *buf2; FILE *tablefile; char *space = " "; char *errorstr1 = "Error opening table file table.dat"; char *errorstr2 = "UNDETERMINED ERROR FROM GTM_ENV_XLATE"; if ((tablefile = fopen("table.dat","r")) == (FILE *)NULL) { ptr->length = strlen(errorstr1); copy_string(&(ptr->address), errorstr1, strlen(errorstr1)); return 1; } while (fgets(buf, (int)sizeof(buf), tablefile) != (char *)NULL) { line= &table[i++]; buf1 = buf; buf2 =strstr(buf1, space); line->field1.length = buf2 - buf1; copy_string( &(line->field1.address), buf1, line->field1.length); buf1 = buf2+1; buf2 = strstr(buf1, space); line->field2.length = buf2-buf1; copy_string( &(line->field2.address), buf1, line->field2.length); buf1 = buf2+1; line->ret.length = strlen(buf1) - 1; copy_string( &(line->ret.address), buf1, line->ret.length); } fclose(tablefile); /* In this example, the last entry in the table is the error string */ line = &table[4]; copy_string( &(line->ret.address), errorstr2, strlen(errorstr2)); line->ret.length = strlen(errorstr2); return 0; } int cmp_string(gtm_string_t str1, gtm_string_t str2) { if (str1.length == str2.length) return strncmp(str1.address, str2.address, (int) str1.length); else return str1.length - str2.length; } int cmp_line(line_entry *line1, line_entry *line2) { return (((cmp_string(line1->field1, line2->field1))||(cmp_string(line1->field2, line2->field2)))); } int look_up_table(line_entry *aline, gtm_string_t *ret_ptr) { int i; int ret_v; for(i=0;i<4;i++) { line = &table[i]; ret_v = cmp_line( aline, line); if (!ret_v) { ret_ptr->length = line->ret.length; ret_ptr->address = line->ret.address; return 0; } } /*ERROR OUT*/ line = &table[4]; ret_ptr->length= line->ret.length; ret_ptr->address = line->ret.address; return 1; } int gtm_env_xlate(gtm_string_t *ptr1, gtm_string_t *ptr2, gtm_string_t *ptr_zdir, gtm_string_t *ret_ptr) { int return_val, return_val_init; if (!init) { return_val_init = init_functable(ret_ptr); if (return_val_init) return return_val_init; return_val_init = init_table(ret_ptr); if (return_val_init) return return_val_init; init = 1; } linetmp.field1.length= ptr1->length; linetmp.field1.address= ptr1->address; linetmp.field2.length= ptr2->length; linetmp.field2.address= ptr2->address; return_val = look_up_table(&linetmp, ret_ptr); return return_val; } > cat table.dat day1 week1 mumps day2 week1 a day3 week2 b day4 week2 c.gld
この例では、メカニズムを示しています。テーブルは適切なメモリ管理のための最初の時間をセットアップされており、それぞれの参照ごとに、テーブルルックアップが実行されます。単純化の目的のために、エラーチェックは行われないので、table.dat が、正しいフォーマットになっていると見なされ、そして、正確に4つのエントリを持っていることに注意してください。このルーチンは共有ライブラリとして構築する必要があります。 第11章: “外部 ルーチンの統合”を参照して、共有 ライブラリとして構築する方法を参照してください。 関数 init_functableは、GT.Mメモリ管理関数を設定するために必要です。