ご挨拶
おひさしブリトニースピアーズ!
半年ぶりぐらいの生存報告も兼ねての投稿です。
天才ブリリアントシュミグラマのnk.さんです。
ひょんなことからMSN(Med State Notation)という言語を使うことになったので自分用の備忘録を兼ねた記事になります。
ぶっちゃけ、アクセス数とかを稼ごうとかそういうの一切ありません。
ほら、やっぱりこれぐらい尖ってないと天才とは呼べないでしょ。
MSNって何やねん
尖ってるとか言いつつもこうやって補足情報をちゃんと書くあたりが僕らしいですよね。知ってる。
MSNはMed State Notationの略で、MEDという実験器具?システム?を制御するための専用言語です。
詳しくはMEDState Notation Repositoryを見て。
実験と一口に言ってもいろいろな実験がありますね。いやそんなにないか。ないわ。
とりあえずMEDはいわゆるスキナー箱(operant chamber)の統合環境みたいです。他にも用途あるのかもしれないけれどわざわざ調べません。だって使わないし。
まあそのMEDの制御のための専用言語がMSNなわけです。Microsoftがやってるポータブルサイトではありません。
普通のプログラマ向けのMSN解説
MSNのマニュアルにザッと目を通したんですが、node.jsみたいなイベント駆動型言語と捉えたら理解が速いかもしんないです。というか僕はそう捉えた。そしたら1日で理解できた(と思う)
マニュアルにも最小の時間解像度云々みたいな話が書いてあったし、基本的には同じような実装だろう。たぶん。
基本的にはずっとイベント生起待ち状態が続いていて、特定のイベントなりが生起した場合に何らかの処理をして別の状態に移行するというのがこの言語の基本的な仕様みたいです。
基本的な命令は次のような感じ。
バックスラッシュでそれ以降がコメントアウトになる。
MSN用に表示させるアレがなくてjavascriptモードで描画してるから色とかがカオスだけどそれはご愛嬌。
[js]
Sn, \ Sn: state identifier. for instance, S1 and so on
#event: operation(s) —> Sm \ Sm: next state identifier
Sm,
#event2: operation(s) —> Sp \ Sp: next state identifier
[/js]
次みたいに捉えたらいいんとちゃうかなと思った。
[js]
// for javascript
function hoge(){
addEventListner(‘event’, function(){
some operations;
fuga();
});
}
function fuga(){
addEventListner(‘event2’, function(){
some operations;
piyo();
});
}
…
[/js]
あるステート(命令のまとまり、上の例の中ではSnとかSmとか)の中では複数のイベントの補足ができるので、1つのステートが関数1つに対応してると思えばいいと思う。
そしてこのステートを複数集めたのがステートセット(SS)と呼ばれるもの。
1つのプログラム中で、複数のステートセット(最大32個)が同時に実行可能。これは便利やね。
MSNでCRFスケジュール
MSNの解説については、キリがないんで今回はとりあえずこれぐらいにして、CRFスケジュール(連続強化スケジュール; 反応ごとに強化子(と推定されるもの)を呈示するスケジュール)を書いたらどうなるか。
プログラム書き終えてからMEDのサイト見たらサイトにも載ってたんだけど、まあそういうのはいいよね。
[js]
\ CRF(continuous reinforcement) schedule
\ in Med State Notation
\ made by nk. ( https://vivibit.net/ )
\ in 2015/08/19
\—————————+
\ ENVIRONMENTAL SETTING |
\—————————+
\ INPUT
^InL = 1 \ Left Lever
\ OUTPUT (MANIPULATION)
^OpL = 1 \ Left Lever
^Feed = 4 \ Feeder
^LitH = 5 \ Light on House
\—————————+
\ EXPERIMENTAL SETTING |
\—————————+
\ Const Integer
^MaxTrial = 20
\—————————+
\ ARRAY DEFINITION |
\—————————+
\ Times for Lever Response
DIM L = ^MaxTrial
\—————————+
\ USED VARIABLE LIST |
\—————————+
\ L: Array of the Times when Lever Response occurred in each trial (100ms)
\ N: Trial Counter
\ T: Local Trial Counter used in reinforce timer
\ V: Timer for Reinforcement
\—————————+
\ INITIATION ON EXPERIMENT |
\ RECEIVED Z25 |
\ SEND Z1 |
\—————————+
S.S.1,
S1,
#START:
ON ^LitH;
ON ^OpL;
Z1 \ Main Reinforce Trial Start (send Start Signal to #SS2)
—> S1
#Z25: \ received end singal of main trial loop
OFF ^OpL;
OFF ^LitH
—> stopabort;
\—————————+
\ MAIN REINFORCE TRIAL |
\ RECEIVED Z1, Z11 |
\ SEND Z6, Z7, |
\ Z10, Z25 |
\—————————+
S.S.2,
S1,
#Z1:
SET N = 0; \ reset Trial Counter
Z6 \ reinforce timer start
—> S2
S2,
#R^InL:
Z7; \ reinforce timer stop and record
Z10; \ feeder control ON
ADD N; \ Trial counter UP
IF N < ^MaxTrial [@continue, @break]
@continue:
—> S3 \ wait for feeder end
@break:
—> S4
S3,
#Z11: \ received Feeder end signal
Z6 \ reinforce timer start
—> S2
S4,
#Z11:
Z25 \ send end signal to #SS1
—> S1
\—————————+
\ FEEDER CONTROL |
\ RECEIVED Z10 |
\ SEND Z11 |
\—————————+
S.S.3
S1,
#Z10: \ received ON signal
ON ^Feed
—> S2
S2,
0.5":
OFF ^Feed;
Z11 \ send feeder OFF signal
—> S1
\—————————+
\ REINFORCE TIMER |
\ RECEIVED Z6, Z7 |
\—————————+
S.S.6
S1,
#Z6:
SET T = N; \ set Local Trial Counter
SET L(T) = 0; \ reset the time
SET V = 0 \ reinforce Timer Reset
—> S2
S2,
0.1":
ADD V; \ Timer Count Up
SHOW 2, CRFtimer, V
—> SX
#Z7: \ responce Occurred
SET L(T) = V
—> S1
[/js]
重大な告知
さて、ここまで書いておいてなんなんだけど、これまだコンパイルとかもしてないんだよね。だから動くか不明。
自分が実際に使う予定のプログラムは別に作ってあるんだけどそれは公開するわけにもいかないし、かといって暇だから自分で練習のために書いたようなもの。
累積反応グラフを作るためには反応の時間情報が必要だから、それを記録するようにしてある。
そんで一定時間CRFスケジュールにさらすっていうのもアリ(暇が続けばそれも書くつもりではある。まあ、いわゆるフラグだな)だし普通はそうだろうけど、より色んな意味で制御が楽な試行数(強化数)制限にした。20試行で終わるプログラムになっている。
とりあえず今回はこんな感じ。
間違いとかあれば教えてください。
追記(2015.09.05)
上のプログラム多分エラー出るわ。
他のプログラム(実際に使う予定のやつ)をコンパイルしてみて分かった仕様を補足しておく。
- 数値定義はヘッダ部分では利用不能(ヘッダ部分では定義しかできん)
- 1ステートの途中で改行入れたらダメな箇所がある
- LISTコマンドによる変数宣言はランダムアクセス不能(たぶん)
- IF節の分岐ラベル(@continueとか)は重複不可(多分)
上2つは上記のプログラムにも当てはまる事柄だけど、下2つは実際に使う予定のやつをコンパイルしてみて気づいた仕様。
数値定義はヘッダ部分では利用不能(ヘッダ部分では定義しかできん)
マニュアルには「定義はどこでも使えるぜ!」みたいなことが書いてあった(気がする)けど、実際にはステートセット中にしか利用できないっぽい。
[js]
\ Const Integer
^MaxTrial = 20
\—————————+
\ ARRAY DEFINITION |
\—————————+
\ Times for Lever Response
DIM L = ^MaxTrial
[/js]
こういう書き方はダメっぽい。
使い勝手悪すぎでしょ。どういうつもりでこういう仕様にしたのか理解に苦しむ。
[js]
\—————————+
\ ARRAY DEFINITION |
\—————————+
\ Times for Lever Response
DIM L = 20
[/js]
こういう感じにしたら該当箇所のコンパイルエラーは出なくなりました。
1ステートの途中で改行入れたらダメな箇所がある
マニュアルには「空白文字は無視されます」的なこと書いてあった(気がする)から可読性を考えて改行を挟みまくってたんだけど、どうやら改行を挟んではいけない箇所がある様子。ただし、改行コードが\r\nのやつでしか試していない。もしかしたら\nなら改行挟んでも大丈夫なのかもしれんが、;の後は改行(\r\n)があっても大丈夫なところを見るとそういうんじゃなさそう。
[js]
#START:
ON ^LitH;
ON ^OpL;
Z1 \ Main Reinforce Trial Start (send Start Signal to #SS2)
—> S1
[/js]
これはエラーが出る。
イベント指定後のコロンの次(上の例だと#START:部)に改行があるとダメっぽい。
また、移動矢印の前(—> S1の—>の前)にも改行があるとダメっぽい。
つまり、イベントとイベント後の最初の命令(上の例だとON ^LitH)は同じ行で、移動とその直前の命令も同じ行じゃないとダメっぽい。
これもまたコメントの可読性を大幅に下げる要因のひとつだと思う。だってコメントの1行が長くなってその行が何なのかわかりにくくなるでしょ。「~のとき」「~をする」「~に移動」の3つを1行で記述しないといけなくなる場合も出るわけで。
[js]
#START: ON ^LitH;
ON ^OpL;
Z1 —> S1
[/js]
プログラミングにおけるコメントアウトという聖域について、MSNの言語設計者は想いを馳せたことがあるのだろうか。
それとも、開発担当は全員が僕みたいな1ライナー至上主義変態Perlerなんだろうか。
LISTコマンドによる変数宣言はランダムアクセス不能(たぶん)
色々と常識とかけ離れた設計だなと感じたのがこれ。
LISTコマンドというのは例えばVIスケジュールでインターバルの一覧を
[js]
LIST A = 20", 30", 40"
[/js]
というようにヘッダ部で指定しておいて、この中からRANDIとかのコマンドで無作為(細かい仕様読んでないけどきっと疑似乱数でしょーね)に選んで使ったり、
[js]
LIST X = A(K)
[/js]
みたいな感じにしておくとアクセスごとにKがインクリメントされてリストの中身がローテーションされたりといった感じで使うらしい。
こういう引数指定っぽいやつができるんだったら、
[js]
LIST X = A(0)
[/js]
のようなランダムアクセスでも使えるだろうとふつーのプログラマなら推測するとは思うんだけど、どうやらこの使い方はできないっぽい。ちゃんと検証はしてないけど。逆ならまだわかるんだけどなあ。
IF節の分岐ラベル(@continueとか)は重複不可(多分)
違うSS内でも、違うラベルをつけなきゃだめっぽい。(ちゃんとは検証してない)
条件分岐ってプログラム終了するかどうか、つまりループを抜けるかどうかみたいなとこで多用するとは思うんだけど、この仕様のおかげでラベルが@continue1てな感じでアホみたくなるか、@continue_mainroutineみたいに長くなってしまう。
まあでも、これはギリ許せるわ。
余談
MSNって内部的にはPascal使ってるっぽいね。
上にいろいろ書いた仕様も、ひょっとしたらそこらへんに起源があるアレなんかもしれんね。
誰か他に知ってる情報あったら教えてください。って言うほどこのブログに閲覧者が居るとも思えんケド。
コメント