マクロ実行中の経過を確認するときや,視覚効果を狙うとき以外は,基本的に実行経過を表示しない方がよいでしょう。画面の書換が発生しないので実行速度が上がって,処理時間が短くて済むからです。
画面表示を停止するには,システム関数&s
(サイレントモード)を使用します。サイレントモードを解除するには,システム関数&d
(画面の再表示)を使用し,必要に応じてコマンド#]
(ステータスラインの再描画)も併せて実行してやります。
大きなファイルを先頭から一行ずつ処理していくようなマクロの場合は,実際に実行されているのか,それともフリーズしている状態なのか判別できないということがあります。このような場合は,現時点の処理行数をシステム関数&m
で表示してやると安心できます。凝ったことをするなら,処理開始前にファイル末尾へジャンプして総行数を取得し,1行処理するたびにデクリメントして表示するというのも,なかなか悪くない手法です。カウントダウンで進捗状況がわかりやすいという利点は大きいですね。
コマンドラインに出ることがあるならば&s
では不十分で,変数ss(サイレントフラグ)を使用します。ss=1
で通常のサイレントモード。ss=2
でコマンドラインもサイレントになります。
実はVZにはサイレントに係るバグが複数あり,そのうちで最も影響が大きいと考えられるのは,サイレントモードで非ブロックのカット&ペーストを行うと改行コードの一部が欠落するというものです。影響を回避するには,1行ブロックによるカット&ペーストで対応することです。
サイレントでマクロを実行しても,メッセージはそのまま表示されます。これは途中経過を示したりするのに必要なわけですが,マクロの終了後にメッセージを残したくないときは,空メッセージを表示します。
&m("") ;メッセージを消す
現在表示されているメッセージを,ヌルで上書きするということになります。コマンドや関数にメッセージを「消去」する機能はなく,見えないメッセージを表示することによって,あたかもメッセージが消えたと思わせるだけです。「&m("")
」によって消去されたメッセージラインは,本来そこに表示されるべきテキストも隠されている状態であることが確認できるでしょう。
そういった「1行損」の状態が気持ちが悪いと感じられる諸兄は,&d
によって再表示してください。
&m("") ;メッセージを消す
&d ;メッセージラインを含め再表示する
蛇足ですが,メッセージを表示しているとき,「メッセージ表示フラグ」と呼ばれるスイッチが入っている状態になっています。このフラグは内部ワークエリアのpn.-15
にあり,その値によってメッセージがどの程度のカーソル移動で消去されるかが決定します。
マクロの中で検索を行ったり,ブロック部分を#Gで取得する一方で,入力を促す窓やメニューを表示するというマクロでは,ステータスラインのページングモードの表示がころころ変わって,やや見苦しいと感じることがあります。
あるいは,ページングモード(mp)をプッシュ・ポップして起動時の状態に復帰しているものの,再表示のタイミングによっては,ページングモードが表示ページング[P]に戻っているはずなのに検索モード[S]のままになっている場合があります。
このような場合は,ページングモードを強制的に表示ページングにして特殊コマンド#]
(ステータスラインの再描画)を実行することにより,あたかもページングモードが変遷していないような静かな表示にすることができます。
バッファの先頭にある文字列,つまり最新の文字列であれば簡単です。単純にバッファを指定して出力すればよいのです。
&?("%s",ps)
このコードは,バッファがps(検索文字列バッファ)に限り,コマンド#I
(文字列の複写=#24
)でも同じです。また,履歴を遡って選択出力する場合は,コマンド#61
(文字列の選択複写)を使用する必要があります。これらのコマンドはいずれも検索文字列バッファを対象としており,他のバッファの文字列を出力させるコマンドはありません。
しかし,バッファをすり替えることによって,他のバッファの文字列も出力することが可能です。次の例はpr(リネーム用バッファ)の先頭文字列を出力します。
1 ^^ "" ?.
ps[=pr,
#I
ps],
同様に#61
の場合も,バッファをすり替えることで,色々なヒストリーバッファの文字列を履歴を遡って選択出力させることができます。
さて,バッファを指定して出力させるのはよいとして,バッファの数だけマクロを用意するのは無駄というものですね。次の例は,最初に表示されるメニューでバッファを指定して,選択出力させることができます。
= KAn'ihis.def = by H_ear
簡易ヒストリー
* mad for vz!
1 ^KA "" ?. (ks&1)??{ #61. }
!01 (r<0)?.
ps[, (r) &> ps=r, #61 &Cr ps],
0: >?{(pa)(pf)(pw)(px)(pr)(pt)}
* pmenu
1 "KAn'i History",25,6
"pA アプリケーション"
"pF ファイル名"
"pW 一般ワーク"
"pX コマンドライン"
"pR リネームワーク"
"pT タイトルサーチ"
*
元々#61
にアサインされているキーを使い回します。通常に起動すれば,コマンド#61
をそのまま実行して終了します。シフトキーを押しながら起動すると,メニューを表示し,選択されたバッファから文字列を選択複写できるようになります。コマンドラインで実行したDOSコマンドをバッチファイルに貼り付けるときなどは,なかなか調法するのではないでしょうか。
当日の日付や曜日の情報はシステム時計が持っています。この情報をVZで出力するには,ファンクションコールを使用してシステムから教えてもらい,好きな書式で整形した文字列にするという手順を踏みます。
日付を取得するファンクションコールの設定値はAX=2A00h
(AH=2Ah
/AL=00h
)で,返り値はCXに年,DXに月日(上位のDHに月,下位のDLに日),AXに曜日(ALのみ使用)となっています。それぞれの返り値の内容は,ファイルのタイムスタンプが格納されているDTA(Disk Transfer Address)の圧縮形式と異なり,あまり複雑ではありません。年はそのまま出力することができる西暦4桁,月日は上位・下位を分けて取り出せばよく,曜日は日曜日を0とする0から6の数値です。
月の取出し方は,DXの上位バイトを下位にシフトするという方法で,dx>>8
という式になります。ビットイメージで示すと次のようなものになり,上位バイトにある数値を取り出すことができます。上位バイトには下位バイトの値がずれてくるわけではなく,すべてのビットがオフになります。
上位 ← → 下位 0000 0111 0001 1010 1818 (71Ah) ↓ DX>>8 0000 0000 0000 0111 7 (7h)
日の取出し方は,DXの邪魔な上位バイトを取り去るためにFFhでビット毎の積をとります。上位バイトにはすべて0が掛けられるためすべて0となり,下位バイトはすべて1が掛けられるため変化しません。これにより下位バイトだけを取り出すことができます。
上位 ← → 下位 0000 0111 0001 1010 1818 (71Ah) and 0000 0000 1111 1111 255 (FFh) ↓ DX & $FF 0000 0000 0001 1010 26 (1Ah)
さて,それでは年月日を出力してみましょう。
ax=$2A00, &i($21)
&?("%4d年%02d月%02d日",cx,dx>>8,dx&$ff)
もしも,年を2桁で出力したい場合は,cxを100で割った余りを出力します。21世紀となったばかりの現在では当分問題になりませんが,20世紀中に作成されたプログラムではcxから1900を引くことで2桁にしていたケースが多く,これが2000年問題として顕在化したことはそれほど古い記憶ではありません。Windowsの某エディタのファイラーで2000年に年が100になっていましたね。
&?("%02d年%02d月%02d日",cx%100,dx>>8,dx&$ff)
次に曜日を出力する方法です。曜日はAXの下位バイトに入ってきていますので,日と同様にFFhのビット積で取り出します。しかし取り出した値は0から6の数値ですから,中国人以外にはぴんときません(中国語では月曜が星期一,土曜が星期六,日曜が星期日)。「日,月,火」あるいは「Sun,Mon,Tue」のように,文字列で出力しなければなりませんので,ひと工夫が必要です。
まず,出力したい同じ長さの文字列を羅列した文字列を作って,この文字列の先頭番地を変数wとしておきます。
w="日月火水木金土",
先頭の「日」の番地がwであれば,「月」の番地はw+2
,「土」の番地はw+12
ということになりますね。数値としての曜日は0から6なのですから,それぞれの数値を2倍すればよいわけですね。そして出力する文字数を制限してやればよいわけです。制限しないと,「火曜日」と出力するつもりが「火水木金土曜日」になってしまいます。
w="日月火水木金土",
a=(ax&$ff)*2,
&?("%2s曜日",w+a)
少し整理してやると,次のようになります。
&?("%2s曜日","日月火水木金土"+(ax&$ff)*2)
あるいは,英語の略で。
&?("%2s","SuMoTuWeThFrSa"+(ax&$ff)*2)
3文字なら。
&?("%3s","SunMonTueWedThuFriSat"+(ax&$ff)*3)
出力形式は思いのままにできます。
ごく普通 | 2005/08/17 |
ちょっとひねり | '05/08/17(Wed) |
全角で出力 | 2005年8月17日(水曜日) |
和暦と古称 | 平成17年、葉月 |
干支(笑) | 乙酉 |
こんなふうに色々な書式をメニューで選択できるマクロを作ってみたことがあります。実は作ってみただけでほとんど使わないですね。どのように加工するかは,ここでは詳しく述べませんが,興味のある方はTimeMenu.defを参照してみてください。
前項で日付の出力について述べましたが,同様の手順で時刻を出力することができます。年の2桁化や曜日のようなひねりがないだけ簡単です。
現在時刻を取得するファンクションコールの設定値はAX=2C00h
(AH=2Ch
/AL=00h
)で,返り値はCXに時分(上位のCHに時,下位のCLに分),DXに秒(DHを使用)となっています。なお,時は24時制で0から23の値をとります。
時刻の出力はおよそひねりがありません。やるとすれば午前・午後くらいですが,日常会話ならともかく,PCの出力ではあまり見ませんね。
1 ^^ "" ?.
ax=$2C00, &i($21)
&?("%02d:%02d:%02d",cx>>8,cx&$ff,dx>>8)
h=cx>>8%12, ;12時制
i=cx>>8/12, ;0:AM / 1:PM
&?(" (%2d%2s)",h,"AMPM"+i*2)
ずばり,高橋版VZで表示スイッチDs(ステータスラインへのシステム情報表示)をオンにしましょう。
…と,これだけではナニですので,高橋版VZの登場以前にどのような手法でステータスラインに時刻を表示していたのか,m.gann師の華麗なワザをご覧ください。
;= ganwatch =
1 ^Q@ "On/Off" (qt!)?>>
1 "!Timer"
r[, &i("$(b4,2c,cd,21,c3)") ;Digital Watch /Status
&Sp(si=pa,"$(1b)[;46H$(1b)[1m%2d:%02d$(1b)[m$(8)",
cx>>8%12,cx&63) &v(0) Se+, cx11, ;cx11: (cx){ BS }
&i("$(ac,3c,9,cd,29,79,f9,e2,fa,c3)") ;int 29
(r]) ;変数 r の保存
はい,これだけです。
1 ^^ "" ?.
ax=$2C00, &i($21)
&m("[%02d:%02d:%02d]",cx>>8,cx&$ff,dx>>8)
例えば,自分の生まれた日は何曜日だったのだろう…なんてことを誰でも一度は確かめてみるものですよね。父親の古い手帳や,図書館で新聞の縮刷版を見て確認したり,あるいは換算表をつかったり。
実は,凄い学者さんがとんでもなく奇抜な方法で計算できる式を発見しているのです。それが「ツェラーの式」です。式についての詳しい説明は省きますが,日数が特殊でしかも変動する2月を前年の終わりの月と見なすことにより,日数計算を単純化してしまうということのようなのです。
この手のマクロは,正しい日付を入力させるためのインターフェイスが重要なのですが,好みもあることですから,これを省略して基本的な部分だけを提示します。日付は最初に3個の変数に入れておきます。
1 ^^ "" ?.
y=2005, m=10, d=13, ;矢野徹氏の一周忌
y[, m[, &> m], y],
&m("%4d/%02d/%02d(%2s)",y,m,d,"日月火水木金土"+w*2)
0: ;指定日の曜日を算出(ツェラーの式)
(m<3)?{ y--, m+=12, }
w=((y+ y/4+ y/400+ (m*13+8)/5+ 1- y/100) +d -1) %7,
擬似ブロックとは,ブロックモードを解除した状態でありながら,ブロック選択しているようにテキストの一部が反転して見える現象を意図的に生じさせ,選択された部分を強調して見せる技術です。この技術を使用することによって,例えばカーソルの存在する行がどこか,あるいは正規表現検索文字列にマッチした文字列の範囲などを視覚的に表現することが可能になります。
基本的な手順は,#b
とカーソル移動コマンドで強調したい文字列や行をブロックし,そのままの状態でブロックモードだけをmb=0
で解除します。それだけでもよいのですが,ステータスラインのブロックモードアイコンが反転しているままなので,スクリーンの書換を行わずにステータスラインだけを#]
で書き換えます。
#b #f #B mb-, #]
#b #x #B mb-, #]
このようにして表示された擬似ブロックは,カーソルを移動させたり,#w
や#z
で多少画面をスクロールしたくらいでは消えないので,たいへん便利に使用できます。
ヌルコードの出力とヌル出力が異なることに注意してください。
ヌルコード(0x00h)を出力する方法はいくつかあります。
''
で出力&o($00)
または &o($0)
または &o(0)
で出力&o('')
で出力一方,ヌル出力とは「何も出力しない」ことです。文字列へのポインタが指すアドレスの位置がヌルコードがあれば,何も出力されません。例えば文字列バッファの先頭アドレスにヌルを格納すれば,何も出力されないヌル出力になります。
= test
* m
1 ^\ "" ?.
(mr==$80)?.
"''でヌルコード出力 ->" '' "<- 出力しました" #m
"&o($$00)でヌルコード出力 ->" &o($00) "<- 出力しました" #m
"&o($$0)でヌルコード出力 ->" &o($0) "<- 出力しました" #m
"&o(0)でヌルコード出力 ->" &o(0) "<- 出力しました" #m
"&o('')でヌルコード出力 ->" &o('') "<- 出力しました" #m
"&?($"$")でヌル出力 ->" &?("") "<- 何も出力しません" #m
.
*
通常,&o()
でのコード指定は十六進数で行いますので,$FFのような形式になります。ヌルコードを出力するなら$00になりますが,1桁で収まっていますから$0となり,さらに9以下の数値であれば十進と十六進を明示的に区別する必要はないので,&o(0)
で出力できます。また,&o('A')
のように文字コードを指定できますが,ヌルであれば&o('')
のように指定することもできます。いずれにせよ,''
で出力するのが最短です。
文字列の中にヌルを含んで出力することはできません。書式指定で文字出力を指定してもヌルコードは無視されます。
&?("%c",1) ;01hが出力される
&?("%c",0) ;出力されない
単純に制御文字を出力したい場合などは,システム関数&o()
が使えますが,文字列の中に混在させたいときなどは,"文字列" &o(9) "文字列"
のように煩雑になってしまいます。例えば,置換文字列としてタブコードを出力したい場合など,ステップが増え,あまり美しいと言えません。
文字列中に制御コードを仕込む場合は,制御文字'$'
を使用します。
#56 "</td><td>" #m "</td>$(9)<td>" #m "ay"