MAPエムウェブ9のつくりかた > 2回めまでにやっておきたいこと

すべてをここから始め、ここで終わらせる巨大なワンボディ。 情報を、ここに集め、ここを起点とし、ここで完結する「しくみ」で経営をまわす。 販売、仕入、受発注、精算、在庫、給与計算‥‥等々、 うじゃうじゃと散乱しがちな企業活動の業務処理を、 ひとつのデータベースに一元集約化しようという開発思想にもとづくERPライクなシステム。 それがエムウェブ。

2回めまでにやっておきたいこと

以下、
必須ではありませんが習得時間の短縮には必ず役立ちますので、
独学独習のガイダンスとして活用してください。

言語のアウトライン
いまはCaché ObjectScriptという名前がついていますが、
かつてはマンプス(Mumps)という言語でした。
これをインターシステムズが仕様拡張して、
自社製品としたものです。
マンプスはのちにM言語(M Language)と呼ばれたこともあり、
それがエムトーンの社名の由来ともなっています。
エムウェブは、
まだCaché ObjectScriptが存在しなかった時代に生まれましたので、
グローバル変数の扱いもネイティブなマンプスそのものです。
オブジェクト指向ではないのし、
ゆえにクラス定義もありません。
言語としてのマンプスに愛着があるので、
このチュートリアルの中でもマンプスと呼ぶことがあります。
マンプスのコマンドは異常にカンタンで強力なので、
わずか数時間のレッスンで実用的なルーチンを書くことができます。
ただしエムウェブ9はウェブアプリケーションなので、
HTMLの理解(スタイルシートを含む)が必須になります。
プログラマーと言語のあいだにも相性というものがあるでしょうが、
マンプスはある意味、
非常に個性の強い言語です。
相性が悪ければエムウェブ開発にも身が入らないので、
まずはこの言語の特性を好きになれるかどうか、
自問自答しながら読み進めてください。
なお、
言語仕様について解説するこの項目は、
前から順を追って読み進めてもらうことを前提としています。
すぐに覚えたいコマンド
マンプスはもともとコマンドの数がきわめて少ないのですが、
それをさらにエムウェブの開発用途に絞るなら、
知っておくべきコマンドは、
D、E、F、G、I、K、M、N、Q、R、S、W、
くらいとなります。
ほかにもいくつかのコマンドはありますが、
ほとんど使うことがないので、
当面は知らなくていいでしょう。
関数の説明はあとにまわすとして、
それぞれのコマンドは以下の英単語の省略形です。
Do、Else、For、Goto、If、Kill、Merge、New、Quit、Read、Set、Write。
ソースコードの記述にあたっては、
DO は DO と書いても Do でも do でも D でも d でもよいことになってますが、
エムウェブでは大文字の省略形 D を用いるのが大半です。
かつてはそれで統一していましたが、
なんとなく世間一般で小文字が主流になってきたこともあり、
新しい文法に則って、
プロシージャを用いる(カモメ括弧{ }で区切ってコードを書く)場合などは、
トラディショナルな文法と区別するために、
小文字を使うことも増えてきています。
ではさっそく、
ターミナルを開いてコマンドを試してみます。
プロンプトで、
YOU>s a=3,b=5,c=8
と書いて、
YOU>w
で確認してみると、
a=3
b=5
c=8

のように表示されますから、
3つの変数に値がセットされたことがわかったら、
ここで、
YOU>k a
とし、
そのあとまた w で確認してみると、
a がなくなっていることがわかります。
以上の説明で、
R、W、K、S

4つが理解できました。
YOU>s (a,b,c,d,e)=0 w
みたいな書き方もやってみといてください。
以下、
やや踏みこんで、
まちがいやすいところやつまづきそうなところなどをピックアップしながら解説してみます。
ターミナルを使って実際に確認しながら読んでほしい‥‥
と、
言いたいところですが、
ターミナルでは試せない記述も多いので、
スタジオがうまく使えるようになったら是非お試しください。
D
do
ルーチンを呼び出して実行する。
D IDT^uDT,ODT^uDT
のように記述すると複数のルーチンを連続して実行させることができる。
また、
同一ルーチンの中で使った場合には、
D XYZ
のようにラベル名 XYZ だけで呼び出して実行することができる。
また、
引数を省略する形式もあり、
その場合は、
I A=5 D
. S X=1
. S Y=0
. W "OK"

のように記述する。
この意味は、
I A=5 S X=1,Y=0 W "OK"
と記述するのと同じである。
だらだらと横長に記述したくないときに使うと便利なので使用頻度は高い。
E
else
IFとセットで使って、条件を満たさないときの処理を指定する。例えば、
I A=5 S B=1
E S B=0

のように記述する。
その意味は、「A の値が 5 なら、B に 1 をセットし、そうでないなら B に 0 をセットする」。
ここで、E のうしろにはスペースを2つ空ける(E には引数がない)ことに注意する。
E を使ううえで危険なのは、
I A=5 D X^RTN
E D Y^RTN

のような記述である。
一見、上の例と同じように見えるが、A=5 で X^RTN が実行されているあいだに、
その中で別の IF が使われていたりすると E の判定の意味が変わってしまうからである。
この危険は、次のように記述すると避けられることを理屈抜きで知っておこう。
I A=5 D
. D X^RTN
E D Y^RTN

F
for
処理を繰り返して実行するときに使う。
F J=1:1:5 S P(J)=0
のように書くと、
「 J が 1 から始め、J に 1 ずつ加え、J が 5 になるまで P(J)=0 をセットする」という意味になる。
F J=1:1 S P(J)=0 Q:J>10
のように書くと、F の引数としては終了条件が与えられていないので、
「 J が 1 から始め、J に 1 ずつ加え、何度でもくりかえして P(J)=0 をセットする」という意味になってしまうが、
Q 命令によって、J>10で止められることになる。
ちなみに F は、引数を省略すると、いつまでも無限にくりかえして処理を実行することになる。
S J=0
F S J=J+1,P(J)=0 Q:J>10

となる。このときFのうしろにはスペースを2つ空けることに注意する。
また、これは
F J=1:1 D Q:J>10
. S P(J)=0

のように記述しても同じ意味になるが、
このとき D のうしろにはスペースを2つ空けることに注意する。
G
go
指定するルーチンやラベルの位置へジャンプする。
G TOP^sSLIN
のように記述すると、別のルーチンの指定するラベルの位置へ処理が移る。
同一ルーチンの中で使った場合には、
G XYZ
のようにラベル名 XYZ だけで移動することができる。ローカル変数は、そのまま有効である。
I
if
命令を実行する際の条件を指定。
A=1、B=2、C=3 として、
I (A<B)&(B<C) S D=4
のように記述すると、「もし A が B より小さく、かつ、B が C より小さければ、D に 4 をセットする」という意味になる。
しかし、
I A<B,B<C S D=4
のように記述すると、「もし A が B より小さければ、次の条件判定に進み、B が C より小さければ、D に 4 をセットする」という意味になる。
どちらの記述でも結果としては成立して D=4 になるのだが、
後の記述では、条件が左から順に判定されるため A<B が真でなければ、B<C かどうかの評価は行われないということを知っておく。
つまり、
I A<B S:B<C D=4
と記述するのと同じ意味である。「または」の場合は、記述はひとつしかないので、こんなややこしいことは考えなくてよく、次のようにすればよい。
I (A<B)!(B<C) S D=4
K
kill
変数を削除する。どんな大きなデータベースも「あっ」というまに削除してしまうので実行には細心の注意が必要である。
ここにグローバル変数として、^DATA(1,1,1)、^DATA(1,1,2)、^DATA(1,1)、^DATA(1,2)、^DATA(1)、の5つがそれぞれ定義されているとして、
K ^DATA(1,1)
のように記述すると、グローバル変数 ^DATA(1,1) と、その下位ノードがすべて削除されて、残りの変数は ^DATA(1) と ^DATA(1,2) となる。
K ^DATA(1)
と記述すると、全部消えて何も残らないが、はじめに、^DATA=0 という上位ノードの定義があったとすれば、これは消されないことになる。
以上の理屈はグローバル変数でもローカル変数でも同じことであるが、
次のように引数なしでこのコマンドを使うと、
K
すべてのローカル変数を削除するという意味になり、
K (A,B,C)
のように記述すると、指定したローカル変数以外のすべてのローカル変数を削除するという意味になる。
M
merge
変数の構造ごと丸ごとコピーする。例えば、^X(1)=0、^X(1,1)=0、^X(1,2)=0、^X(1,1,1)=0、の4つの変数が定義されているとして、
M ^Y=^X
のように記述すると、変数 ^Y の中味は、^Y(1)=0、^Y(1,1)=0、^Y(1,2)=0、^Y(1,1,1)=0 となる。また、
M ^Z(9)=^X
のように記述すると、変数 ^Z の中味は、^Z(9,1)=0、^Z(9,1,1)=0、^Z(9,1,2)=0、^Z(9,1,1,1)=0 となり、
指定したノードの以下にそのままの階層がコピーされることがわかる。
また、ローカル変数とグローバル変数のいずれも同じ理屈で扱うことができ、
M W=^X(1)
と記述すると、変数 W の中味は、W=0、W(1)=0、W(2)=0、W(1,1)=0、となる。
N
new
あるサブルーチンの中でローカル変数に対して使うと、そのサブルーチン内でのみ利用できるローカル変数とすることができる。
例えば、
N DATE
とすると、このサブルーチンの実行以前に DATE=20020420 と定義されていたとしても、
このサブルーチン内では、新たなローカル変数として DATE を使うことができ、
このサブルーチンの処理が終わると、DATE の元の値 20020420 はそのまま有効になる。
N (DATE)
と記述すると、このサブルーチン内では、DATE 以外のすべてのローカル変数が、このサブルーチンでのみ有効という意味になり、
このルーチンの前のルーチンから DATE 以外のローカル変数を受け取ることができないし、
このルーチンの次のルーチンに DATE 以外のローカル変数を引き渡すことができない。
なお、いずれの記述も、カンマで区切って複数のローカル変数を指定することができる。
Q
quit
D コマンドや F コマンドによる処理やルーチンを終了する。
引数のある場合は、外部関数として呼び出したときにその値を返す。
R
read
データを読みこむ。
R X:3
と記述すると、制限時間を指定したことになり、データ入力(または返り値など)を3秒待つ。
S
set
変数に値をセットする。
W
write
データを出力する。
W "Good morning!",!,"How are you?"
と記述すると、
Good morning!
How are you?

という結果が得られる。
2つめの ! は、改行の意味である。
変数の添字
ものすごく重要なポイント──
変数には添字というものを付けることができます。
添字を付けることによって、
値をひとまとまりの集合体として扱うことができます。
説明を聞くとむずかしいので、
やってみたほうが早くわかります。
(長々とした丁寧な説明を好む性格の方は、
エムウェブの開発には向いていないともいえます。)
ここからはプロンプトは省略しますが、
ここまでと同様にターミナルで試してください。
s a=3

とすると
a に 3 がセットされることはすでにおわかりのとおりですが、
s a(5)=8
とすると、
こんどは a(5) が 8 となる。
s a(5,3)=10
などとすることもできます。
ここまでのところで、
a と a(5) と a(5,3) は、
それぞれ独立した変数なのですが、
a という親の下にちゃんと上下関係ができあがっていて、、
a(5,3) は a(5) の下位ノードであるといいます。
(言葉は覚えなくてもけっこうです。)
いわば、
a(5,3) と a(5,5) は兄弟で、
その親はa(5)、
a(5) は a の子なので、
a(5,3) と a(5,5) は a の孫‥‥
みたいな関係。
添字は数字にかぎったものでもなく、
a("A")という変数もアリ。
a("A") と a(5) もやっぱり兄弟分の関係で、
a の一族です。
ゆえに、
わかりやすい例として、
k a
を実行すると、
先ほどの a(5) も a(5,3) も消えてしまいます。
k a(5)
なら、
a(5,3) は消えるが a("A") は消えません。
ここまで、
実際にターミナルで打ちこんで試してください。
変数は、
いくつでも添字をもつことができ、
また、
添字の階層はいくら深くてもかまわず、
変数自身が別の添字となることもできます。
つまり a=5 であれば、
a(a) と a(5) は同じものですが、
a(a) と a("A") はちがう意味です。
では、
f j=1:1:5 s a(j)=j
というコマンドの実行結果がおわかりでしょうか。
f は他の言語にもよくある for コマンドで、
このラインの意味は、
j という変数が 1 からはじめて 1 を加えていって 5 になるまで a(j) にjをセットし続ける
です。
結果を w で確認してください。
添字の説明の最後に、
わかっていてもけっこうやってしまいがちなミスとして、
空文字列(ヌル)を添字に使ってしまうとエラーとなることを覚えておいてください。
定数と演算子
定数や演算子については、
どの言語も似たり寄ったりなので、
ソースを書いたり読んだりしているうちに自然とわかるはずです。
マンプスに特徴的なものをいくつか例にとって説明しますので、
ターミナルを開いて1回でも実行してみたら理解できるでしょう。
I "ABC"["B" W 10#3
これは、
文字列 "ABC" が文字 "B" を含んでいたら、
10を3で割った余りを書けという意味。
なので、
表示されるのは 1 。
ちなみに、
条件をあらわす IF は、
次のようにコロンで簡易に代替することができ、
しょっちゅう使うってことをここで覚えておいてほしい。
W:"ABC"["B" 10#3
さて次は、 10 を 3 で割った解の整数部分が 3 より大きいか、
または変数 A が 5 でなければ変数 B に文字 "K" をセットしろという意味。
I (10\3>5)!(A'=5) S B="K"
A が定義されていなければエラーとなりますから、
先に A になにかセットしておきましょう。
10\3 は 3 なので、
はじめの条件はクリアしていないので、 A に 5 をセットしなかったとすれば B には "K" がセットされます。
演算子の使い方はざっとこんな感じ。
最後に、
エムウェブのコーディングに必要な演算子をまとめておきます。
この中で、
ちょっと使い方がややこしいのは「パターン照合」ってやつですが、
めったに使わないので、
この段階では知らなくてもよいことにします。
' 否定(でない)
+ 足し算
- 引き算
* 掛け算
/ 割り算
\ 整数除算
# 余り
< 小なり
> 大なり
= イコール
[ 含む
? パターン照合
_ 連結
& かつ
! または
標準/ページトップ

チュートリアル初日──2時間


チュートリアル2日め──2時間


UP