2段呼び出しテクニックは, なぜ存在するのか。ソレは大きな問題なのら。なんてねー。
まず前回で, 「変数保護の解除」について触れたんだけど, 今回は, 名前でアクセスってとこだお。
関数に 変数名を渡してアクセスする ってゆー, C言語でゆーところの「ポインター渡し」っぽいことができるのら。
つっても, ソレだけなら実は簡単なのら。コレ(↓)は, 指定された変数に数を加える関数にょ。
/* 変数名を与えることで, ソレにアクセスする */ num = 10 call ADD 'num', 1 say num /* ← 11 が表示される */ exit ADD: parse arg vname, val n = value(vname) /* 取り出す */ call value vname, n +val /* セットする */ return
でも, procedure instruction (procedure命令)は使っていない。
と, ゆーのも, ソレをアレすると n = value(vname)
は, ローカル変数 "num"を取り出すってことになるのら。メイン側のソレではないっつーことだお。
そーだ, ひらめいた。こーすればよいんだ。・・ んで, もし, 他の変数を渡したいばやいは, num
のとこ書替える訳だね。 (←って, ダメじゃん)
ADD: procedure expose num parse arg vname, val n = value(vname) /* 取り出す */ call value vname, n +val /* セットする */ return
◇
さて, ソレ(↑)を発展させたのが, コレ(↓)。今度のは 呼び出し側に手間がかかるけど, ソレなりにオッケーかな。
';'を使って, 2行にまたがる呼び出しを 1行にすると 少しはマシになるかも。・・ 焼け石に水って気がしないでもないけど。えっへへへ。
/* 変数名を与えることで, ソレにアクセスする */ num = 10 EXP = 'num' call ADD 'num', 1 say num /* ← 11 が表示される */ exit ADD: procedure expose (EXP) parse arg vname, val n = value(vname) /* 取り出す */ call value vname, n +val /* セットする */ return
んで, さらにさらにソレを発展させたのがコレだもん。「2段呼び出し」することで, 変数名の指定のアクセスができるようになる訳にょ。
ま, ちょっと複雑かもしれないけど, ブラックボックスみたいな感じで 普段はソレを詳しく考えることなく使うとよいかも。
/* 変数名を与えることで, ソレにアクセスする */ num = 10 call ADD 'num', 1 say num /* ← 11 が表示される */ exit ADD: parse arg EXP call ADD_2 arg(1), arg(2) return ADD_2: procedure expose (EXP) parse arg , val parse var EXP vname n = value(vname) /* 取り出す */ call value vname, n +val /* セットする */ return
stemを指定して, Compound Symbols (複合記号) 扱うばやいも, 基本的にコレと同じ。
でも, ホントは言語側でサポートしてもらいたいんだけどね, 毎回コレだと大変だし, それに, 少し問題もあるから。 (←何だソレ)
◇
ところで, 「電波がピ」完成版に含まれている, Yahoo掲示板の切り出し文字列。アレって, 形式が変わったのか上手く切り出せないみたいだから, 訂正にょ。 アーンド, nii.nさんの掲示板の切り出しもついでに付けちゃおう。ふとっぱらー。
http://www.benricgi.com/bbs/fast/fast.cgi?id=niin&subdir=a#The Conti入院g Story fast.cgi http://messages.yahoo.co.jp/bbs?action=topics&board=yahoo.05.02.835093&type=r#Yahoo!掲示板 OS/2 <p></td></tr></table> </body>
実をゆーと, 連載を予定していた (つーか連載中だった)パーソナルアンテナ「電波がピ」 ってのを他のとこで完成させちゃったのだ。あははのはー。
ソレはどこなのかとゆーと, あちゃいんさんの Lovely本。
え, 何? そんな話 聞いてないぞ, な〜んて方は通販して手に入れよう。(^^) えへ。
てことで, 通信プログラムとしては, いつか他の何かを扱う予定にょ。・・ 予定は未定ともゆーけど。
でぇ, 今回は σ(^^) が使っている特殊な呼び出し方の解説だお。
「電波がピ」完成版にも含まれているソレは, たとえばこんな感じ。
URL: parse arg URL; return URLCore(, arg(2), arg(3)); URLCore: procedure expose (URL) /* 指定された URLを分解する */ parse arg , cnt, chg 〜
呼び出し方は, これといって特別なアレじゃなくて Rc = URL('PageUpChk.', cnt, '')
・・ て感じ。
呼び出し元の Compound Symbols (複合記号) を関数内で使いたい。 内部から参照したい。ただそれだけなんだけど, ソレがちょいと訳ありってことなのら。
まず, Rexxでは, ローカル変数・グローバル変数の考え方とは 少し違ってるよーなとこがあるんだよね。
procedure instruction (procedure命令)から return instruction (return命令)まで, 変数が保護されるとこは 他の言語と似ているけど, 違うのはグローバル変数がないってとこ。
その代わりにあるのは, expose keywordで 保護するのを解除する機能があるとこ。
ソレを呼び出した側の変数を操作することは可能なんだけど, いきなしトップレベルの変数はアクセスできない。
グローバル変数を扱えるのなら, いきなし "メイン"の変数をアレできるって訳だけど, exposeだと, ひとつ前の "乙"の変数になるってことなのら。分かるかにゃ。
んで, Compound Symbols (複合記号) のばやいは どーかっちゅーと, こう(↓)。
expose keywordの指定に, stem (=名前の後ろにピリオドを付けたもの) を指定するだけぴょん。
URL: procedure expose PageUpChk. do i = 1 to PageUpChk.0 say 'URL' i ':'PageUpChk.i end return
◇
でも, 最初から名前が分かっていたらよいのだけど, そーじゃないときは ちょっと大変。
しかもソレが Compound Symbols (複合記号) のために, stemを引数に渡したとするってーと ・・・
あ, 時間ないや。うじゃ。(^^)/~
Rexxには特殊な変数が 2つほどあって, そのひとつは σ(^^) が作ったプログラムにソレなりに頻繁に出てくる RC
てやつだお。
σ(^^) の使い方は特殊っぽくないので分かりづらいかも知んないけどぉ。えへ。
で, もひとつは SIGL。今回コレをアレしてみよー。
view rexx signal
と入力してみると, ソレが何なのかを知ることができるだよ。
つまりアレだね, signalの飛び先で, 「どこからsignalして来たのけ?」ってのを調べることができる訳なのら。
でもソレが分かったところで いったいどんなメリットが ・・ ?
単純なトラップのばやい, コレはそれほどの意味はないのかも知れない。
数カ所から signalで飛んでくる可能性があったとしても, その数って多くはないはずだよね。
その数が多いのだったら, ソレは, プログラムの構造に問題がありそうだから。
こーゆーときは, 小手先のアレで signal元を調べるよか, 再構造化したほうがよいにょ。 (← リストラ?)
それよっか, 問題になるのは条件トラップ。
ある条件が整ったとき (つっても条件ってエラーだとかそーゆーのだけど), 自動的に制御が移るってやつ。
条件トラップに引っ掛かったけど, ソレはどこ? ・・ 調べるためには条件トラップを一時的に無効にして〜, それから ・・・
そーゆーのを少しは緩和出来るかも, なのだ。
役に立ちそうな, そうでもないような, そんなサンプルを紹介してみよう。 (←って何だ)
コレは, Ctrl-Breakでプログラムを停止させたときに, そのときの行番号を表示して, 「後片づけ」を実行するものだお。
/* SIGLを使ってみるもん */ signal on halt name HaltProgram do /* * 何かの処理 (ループだとかいろいろ) */ end SIGL = 0 HaltProgram: if SIGL > 0 then say 'stop at' SIGL /* 後片づけの処理をここに入れる */ exit
SIGL に 0を代入しているのは, Ctrl-Breakが押されなくて, 正常に(?)終了するばやい, 表示をもっともらしくするため。
よっし, コレでどこで止まっても大丈夫。 (←ってことはない)
Webページの更新日時をチェックしてくれる, そんな便利なページがいくつかあるよね。
んで今回, ソレのパーソナル版を ちょいと手作りしてみよー とゆー訳。
つっても, ソレはまだ完成していないのら。
つまり連載と並行して作り上げよーとゆー, 無謀とも思える計画なんだお。
うーみゅ, ちょっと脳天気 入ってるかも。 >σ(^^)
てことで, Webページへプログラムを使って通信するんだけど, んじゃ どーゆーふーにアレするのかとゆーと, HTTPプロトコルを使うのら。 詳しいとこは RFCを調べてもらうとして, うじゃ 早速アクセスしてみよー。
/* 「電波がピ」テスト版 */ options 'ETmode' True = 1; False = 0; nl = '0d0a'x call RxFuncAdd 'SockLoadFuncs', 'rxsock', 'SockLoadFuncs' call SockLoadFuncs parse value 0 1 2 with how._From how._To how._Both /* このURLは適当に変えてくんろ */ URLlist.0 = 3 URLlist.1 = 'http://www.ibm.co.jp' URLlist.2 = 'http://www.vector.co.jp:80/authors/VA014690/' URLlist.3 = 'http://www.vector.co.jp' 'http://www.ibm.co.jp' signal on halt name ServHalt sklist = '' do cnt = 1 to URLlist.0 parse var URLlist.cnt server req parse var server proto'://'servhost '/'+0 str parse var servhost servhost':'servport if proto \== 'http' then iterate if servport == '' then servport = 'http' sk = ClientSocket(servhost, servport) if sk < 0 then iterate if req == '' then req = str if req == '' then req = '/' Rc = SockSend(sk, 'HEAD' req 'HTTP/1.0'nl||nl) sklist = sk RcvBuff = '' do forever Rc = SockRecv(sk, 'txt', 1500) if Rc < 0 then say '# Receive err='errno else if Rc == 0 then leave /* 受信: 遮断された */ else RcvBuff = RcvBuff ||txt /* 受信: 電文受信 */ end if SockShutDown(sk, how._To) < 0 then say '# ShutDown err='errno if SockSoClose(sk) < 0 then say '# Close err='errno sklist = '' p = pos(nl||nl, RcvBuff) if p == 0 then p = length(RcvBuff) +1 RcvHead = left(RcvBuff, p -1) say say URLlist.cnt say RcvHead end /* -- 後処理 */ ServHalt: do cnt = 1 to words(sklist) sk = word(sklist, cnt) if SockShutDown(sk, how._To) < 0 then say '# ShutDown err='errno if SockSoClose(sk) < 0 then say '# Close err='errno end exit
んで, コレ(↓)は某FDに書き込まれた分(・・の少し訂正版)。
ClientSocket: procedure expose DBG parse arg rem =1 n'.', serv if \datatype(n, 'W') then do Rc = SockGetHostByName(rem, 'host.') if Rc then rem = host.addr end if \datatype(serv, 'W') then serv = getserv(serv) if DBG == 1 then say '#' rem':'serv sk = SockSocket('AF_INET', 'SOCK_STREAM', 0) remaddr.family = 'AF_INET' remaddr.port = serv remaddr.addr = rem if SockConnect(sk, 'remaddr.') < 0 then do if SockSoClose(sk) < 0 then say '# Close err='errno return -1 end return sk getserv: procedure /* getservbyname のサブセット 名前から番号を求める事ができる。 けど, プロトコルは指定できない。 */ parse arg serv'/'proto pth = value('ETC',, 'OS2environment') if DBRight(pth, 1) \== '\' then pth = pth'\' pth = pth'services' call stream pth, 'c', 'open read' do while lines(pth) > 0 txt = linein(pth) if abbrev(txt, serv) then leave end call stream pth, 'c', 'close' parse var txt str'#' parse value translate(str,, '9'x) with . port'/' if \datatype(port, 'W') then return -1 return strip(port)
んで, 結果のひとつはこんな雰囲気のはずにょ。
もし, Last-Modified:
があれば, ソレが求める情報って訳だけど, そのフィールドが無かったばやい,
あるいはそもそも HTTP/1.0 200 OK
てゆーレスポンスコードじゃなかったばやい, そんなときはどーしよー。
HTTP/1.0 302 Moved Temporarily Server: Netscape-Enterprise/3.6 SP3 Date: Tue, 27 Mar 2001 01:02:03 GMT Location: http://hp.vector.co.jp/authors/VA014690/ Content-type: text/html
(↑)のばやいは, 302 っつーコードで, 『ホントはココをアクセスしてね』って感じで Location:
に 場所が指定されてる訳なので, もう一度そこにアクセスするって方法があるよね。
んで, 更新日時が入っていないばやいは, リクエストの HEAD
のところを, 今度は GET
にして, ページの中身で調べるって方法が考えられるね。
その本文のハッシュ値でも計算して, 変化があったらページが更新されたってことにしてもいいかも。ふふ。(^^)
そんな感じで, いまのところ どう転ぶか分かんないけど, 期待しないで待っててちょ。(^^)/~
あ, さてー, ジツをゆーと σ(^^) ってば, コミケとゆーのに参加したのら。
いや, 参加っつーとアレだけど, ちょびっと文書を書いたとか, その程度。えへへ 〜。(^^)
てことで, ソレに関していくつか取り上げてみたいと そー思う訳にょ。
そー, あの TCP/IP とかなんとか, そーいったあたりをゴニョゴニョゴニョ。 IPv4 をモゴモゴモゴ。
んじゃ補足説明でもするんだろーか ・・, そー思ったあなたは甘い。 それこそもー 全く役に立たなさそーなのを紹介してみるのだ。あっはっはー。
dotd2IP: procedure
numeric digits 10
parse arg addr
dig = xrange(0,9)
val = 0
tmp = 1
do i = 1 to 4
p = verify(addr, dig,, tmp)
if p == 0 then p = length(addr) +1
n = substr(addr, tmp, p -tmp)
if substr(addr, p, 1) \== '.' then leave
/* val += (n &0xff) <<(4 -i) *8 */
val = val +c2d(d2c(n, 1)copies('00'x, 4 -i))
tmp = p +1
end
bin = d2c(n, 4 -i +1)
val = val +c2d(bin)
return val
コレは, ドット10進表記のIPアドレスを, 1つの10進数の数字に変換するもの。
C言語では そーゆー関数がソケットライブラリとしてあるんだけど, Rexxでは自動的にアレしてくれるんで使う必要がないのだ。
でも, 必要なくても仕組みが分かれば面白いかな〜ってことで公開している訳。
けっしてネタに困ってる訳じゃないのら。ホントだよ。ホントに違うんだよ。
で, 使い方はっつーと, たとえば '192.168.0.1' とかを引数として与えるとソレと同等の数字が返る訳で, もちろんソレを IPアドレスとして使うこともできちゃう。
注意しておくことは, コレはあくまで IPv4の話であって IPv6とは違うのらよ。IPv6はアドレスが4バイトではないのら。
もーひとつは, procedureとしてコレをコレを動かしたことがないってこと。だから, もしかすると実行できずにエラーになるかもしんない。 (←ぉぃ)
んで, 実はコメント部分はC言語の処理なんだけど, Rexxではソレがどのよーになるのか, ソレの比較もできるとゆー訳。
でも, よく考えるとC言語で示してた方が分かりやすかったかも。(^^)
ま, いいか。うじゃ。(^^)/~