一歩進んだ Rexx

Classic REXX (REstructured eXtended eXecutor)
File #12

2段呼び出しテクニック (2) -- 2001.9.5

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>

2段呼び出しテクニック -- 2001.8.21

実をゆーと, 連載を予定していた (つーか連載中だった)パーソナルアンテナ「電波がピ」 ってのを他のとこで完成させちゃったのだ。あははのはー。
ソレはどこなのかとゆーと, あちゃいんさんの 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で 保護するのを解除する機能があるとこ。 ソレを呼び出した側の変数を操作することは可能なんだけど, いきなしトップレベルの変数はアクセスできない。

メイン
call kou
kou: procedure
   call otu
otu: procedure
   call hei
hei: procedure expose v
   v = 3.141
   /* この変数は"乙"の変数 */

グローバル変数を扱えるのなら, いきなし "メイン"の変数をアレできるって訳だけど, 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を引数に渡したとするってーと ・・・
あ, 時間ないや。うじゃ。(^^)/~


条件トラップのつおい味方 (Ctrl+Breakとかの後処理) -- 2001.7.31

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が押されなくて, 正常に(?)終了するばやい, 表示をもっともらしくするため。
よっし, コレでどこで止まっても大丈夫。 (←ってことはない)


パーソナルアンテナ「電波がピ」(1) -- 2001.3.27

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にして, ページの中身で調べるって方法が考えられるね。 その本文のハッシュ値でも計算して, 変化があったらページが更新されたってことにしてもいいかも。ふふ。(^^)

そんな感じで, いまのところ どう転ぶか分かんないけど, 期待しないで待っててちょ。(^^)/~


ドット10進アドレス -- 2001.1.22

あ, さてー, ジツをゆーと σ(^^) ってば, コミケとゆーのに参加したのら。 いや, 参加っつーとアレだけど, ちょびっと文書を書いたとか, その程度。えへへ 〜。(^^)
てことで, ソレに関していくつか取り上げてみたいと そー思う訳にょ。 そー, あの 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言語で示してた方が分かりやすかったかも。(^^)
ま, いいか。うじゃ。(^^)/~


Copyright (C) 1998-2004 Rexx使いの織華
email: ori@drive.co.jp