|
|
2011/11/13(Sun) 03:55:49|NO.42969
時々、ド初心者が聞くよな。
テキストファイルに書いてある変数をHSP内に適用したいだの何だの。
まあ、気持ちは分からんでもない。
そこで、今回は「プログラムを動かすプログラム」について、いじってみる。
|
|
2011/11/13(Sun) 03:56:28|NO.42970
まず例を考える。
たとえば
console 0,25,0,1
こんな命令が有るとする。
この文字列の命令名と引数を取得したい。
これは簡単だ。
1:先頭から1文字づつ調べてゆく。
2:最初の空白(及びTab)は、無視。
3:アルファベットに当たれば、其処からが命令名
4:更に、順番に1文字づつ調べてゆく。
5:空白に当たれば、其処までが命令名。
6:更に、順番に1文字づつ調べてゆく。
7:「,」に当たれば、其処までが引数
8:1行の終了まで、6、7を繰り返し、引数を取得してゆく
9:行の終了に当たれば、其処までが最後の引数
これをコーディングするだけだ。
サンプルとか無くても自力で作れる程度だろう。
命令名 : console
引数 : 0
引数 : 25
引数 : 0
引数 : 1
このように命令名と、引数が一通り確保できたら、
「命令の文字列」を調べて、対応する処理をするようプログラムを組めば良い。
命令名が分かった段階で、引数の必要数も確認できるだろう。
先に命令処理にとび、そこで必要な引数を確保するようにするのでも良い。
では、こんな例ではどうか?
getKey( enter )
変数の様な物が出てきた。
カッコも出てきた。
見た目からしても、何か「getkey」と言う命令に変数で何かを入れている感じだ。
というか、そう言う意図だ。
今回は少しややこしい。
変数の管理を自力で行なわなければならない。
その為、まず以下の前提を用意する。
・変数名称と値を引数として与えると、「それを覚えてくれる」サブルーチン
・変数名を指定すると、「変数の覚えている数値を返してくれる」サブルーチン
その上で
1:先頭から1文字づつ調べてゆく。
2:最初の空白(及びTab)は、無視。
3:アルファベットに当たれば、其処からが命令名
4:更に、順番に1文字づつ調べてゆく。
5:「(」に当たれば、其処までが命令名。
6:更に、順番に1文字づつ調べてゆく。
7:「)」に当たれば、其処までが最後の引数
これで命令名「getkey(」と引数「enter」が取得できた。
引数の「enter」を、先ほどの「変数の覚えている数値を返してくれる」サブルーチンに渡し、
帰ってきた答えを元に、「getkey」に対応する処理をする。
これで良い筈だ。
この程度も、態々サンプル貼るまでも無いだろう。
次に、結果を変数に入れる事を考える
ret = getkey( enter )
これは先刻の処理に、
「先頭から調べていて、「=」に当たったら、其処までは変数名」とし、
最後に命令名で処理した後、結果を変数に入れてしまえば良いとする。
更に、複雑にする。
ret = choose( getkey( enter ) , 0 , 1 )
命令の引数に命令が入る。
その上、何か結果を変数に入れている。
こうなるともう、「前から見ていく」では駄目だ。
いや、再帰処理出来ればHSPでも前から見ていく事くらい出来るだろうが
残念ながらHSPでは単純に再帰処理は出来ないし、こんな程度の事の為に自前でスタック管理とか面倒だ。
そこで、後ろから調べてゆく事にする。
考え方はこうだ。
「まず、引数の所に書かれてる命令を先に処理する。そうすれば、前のやり方で処理できる。
1:後ろから「(」を探して来る。
2:「(」が見つかったら、それは「命令のカッコ」
3:そこから更に遡ってゆく
4:「空白」か、別の「(」に当たるか、「=」に当たれば、其処までが変数名
5:今度は命令名の直後の「(」から、後ろに向かって調べてゆく。
6:「)」に当たれば、其処までが引数
7:「命令を実行し、結果を命令のあった場所に入れる
8:命令がなくなるまで繰り返す
・・・これで、最後に残った結果を変数retに入れれば、処理できる。
この程度であれば、ココまでの「やり方」を組み合わせてコーディングできれば、
さほど難しくは無い。
ちみっこ達にも「プログラムを動かすプログラム」が作れるであろう。
俺は作らん。やりたきゃ自分でやれ。
さて、それでは更に困った事態に突入する。
ret = choose( getkey( enter ) , 1 + 1 , 5 * 5 )
こんな意味不明な書き方自体が発生するかどうかは兎も角、計算である。
・・・。
こうなると、もう、ココまでのやり方は通じない。
基本的に、「命令」と「引数」に分割するまでは、今までのやり方で出来るはずだ。
つまり、
結果受け取り変数 : ret
命令その1 : choose
命令その1の引数/命令その2 : getkey
命令その2の引数 : enter
命令その1の引数 : 1 + 1
命令その1の引数 : 5 * 5
これらは取り出せるだろう。
問題は「1 + 1」とか「5 * 5」とかだ。
これはもう、
その「式を解釈して計算して結果を返してくれる」サブルーチン
を用意するしかない。
用意するしかないよな?
用意するしかないってば。
用意するんだよ。今から。
さて、うだうだ遠回りしてきたが
要するに「数式を逆ポーランド記法に変換し、それを計算する」
これがテーマ、とぶち上げてみる。
| |
|
2011/11/13(Sun) 03:57:00|NO.42971
逆ポーランド記法とは?
・・・ググれ。
要するに、
「 1 + 1 」と言う式は「 1 1 + 」に、
「 5 * 5 」と言う式は「 5 5 * 」に
変換してしまい、
それを計算させようという事だ。
なんでそんな事するのか?
逆ポーランド表記に変換したら、面倒な「掛け算は先に」とかのルールを無視できるのだ。
というか、逆ポーランド表記に変換する段階で、「先に計算する」部分は先に計算するように書けてしまうのだ。
例えば
5 + 2 * 5 + 5
と言う式は
5 2 5 * + 5 +
この様に変換する。
計算の仕方は簡単だ。
1:前から見て行き、
2:計算の記号に当たったら、
3:手前の2つの数字を取り出し、
4:その記号に合わせて計算する。
5:結果をその位置に戻し、
それを繰り返す。
先ほどの例なら、
最初は 5
次に 2
さらに 5
ここで *
*が出たので、手前の二つ「2と5」をかける ->10
これが2と5の換わりに、間に入る。
つまり
最初は 5
ここが 10 になる。
そして + ・・・
足し算なので、手前の2つ、5と10を足して->15
これで
最初が 15 になる。
そして 5 が来て
最後に + が来た。
合計20
な?
前から計算するだけで、計算の順番とか考えなくても正しく計算できるだろ?
理屈じゃねえ!!魂で感じろ!!
逆ポーランド表記で式を書くと、もの凄く簡単に計算できる事が分かった。
楽勝だよな!!
・・・つーか、それは要するに、
「簡単に計算できるような式に変換した」
のが真相で、それに変換するのが面倒なんだよな(ヲイ
では変換のルールを見てみよう。
1 + 1
まずは簡単なこの式だ。
前から順番に、1つづつ、数字か記号を取り出していく。
最初は1。
数字は基本的にスルーする。ので、結果に出力してしまう。
結果->「1」
次は+。
記号がでた。
ここに、特殊ルール発動。
・「今までにメモって居た記号があったら、全部ぶちまける!」
・「その後、今回の記号をメモる!」
と、まあ、この段階では何もメモって無いので、とりあえず今のをメモる。
結果->「1」 メモ「+」
次に1。
先刻も言ったが、数字はスルーする。
結果->「1 1」 メモ「+」
調べるものが無くなったら、メモ内容をぶちまける。
結果->「1 1 +」
これで変換が出来る。
ではもう少し長くする。
1 + 1 - 1
これで行って見る。
最初は1 結果「1」
次に + 記号の時はぶちまけルール発動!
・・・でも、何もメモって無いのでぶちまけられない。
とりあえず今回の記号だけ新たにメモる。
結果「1」 メモ「+」
次が 1 結果「1 1」 メモ「+」
さらに− こんどこそぶちまけルール発動!
先にメモってた+をぶちまけて、自分をメモ。
結果->「1 1 + 」メモ「-」
そして1 結果->「1 1 + 1」 メモ「-」
終わったので、メモ内容をぶちまける。
結果「1 1 + 1 - 」
なんと、これで足し算、引き算の変換が出来てしまった!
なんだ意外と簡単な事よ。
・・・と言う訳で、実際にプログラムを作ってみる。
f = "1+1-1"
gosub *rpnconv
mes rpn
stop
*rpnconv
・・・。
・・・・・・。
オイ。「メモ」って何だよ。
| |
|
2011/11/13(Sun) 03:57:30|NO.42972
実は逆ポーランド表記への変換には、「スタック」を使うとやり易い。
というか、スタックを使って処理しやすいから、使われるんだがな。
スタックってのは、「データを覚えとく場所」の事だ。
別に配列変数かなんかでも良いんだが、データの出し入れにルールがある。
データを入れると、入れた順番で、記憶していく。
データを出す時は、後に入れた物から順に出してくる。
硬派なプロのグラマーな世界では
「ファーストイン・ラストアウト(先に入ったモンが最後に出る)」
略して「FILO」と呼ぶらしい。
なんかめんどっちいのう。
しょうがないから、先にこれを作る。
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
とりあえず、モジュールにしてみた。内容は見りゃ分かるだろ、この程度。
エラー処理とかは無いから、使いまわしたい奴は自分でつけろ。
さて、これで変換プログラムが書ける。
//----------------------------------------------------------------------------
f = "1+1-1" // 変換前の式を書いた文字列変数
gosub *rpnconv // 逆ポーランド表記に変換するサブルーチン
mes rpn // 結果を表示
stop
*rpnconv
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
switch value
case "+" : case "-" // 足し算、割り算
if buf != "" {
rpn += buf + " " // 文字が溜まっていたら、結果に出力
buf = "" // そしてクリア
}
while( isEmpty() = 0 ) // 先に入っていた記号をぶちまける
rpn += pop() + " "
wend
push value // 記号をスタックに積む
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
swbreak
swend
index++
wend
rpn += buf + " " // 最後に残った数字を結果へ
while( isEmpty()=0 )
rpn += pop() + " " // 最後に残った記号を全て結果へ
wend
return
これで、どんな足し算引き算でもドンと来い、である。
| |
|
2011/11/13(Sun) 03:57:59|NO.42973
次は、掛け算割り算を考える。
掛け算割り算は、なんと足し算引き算より、優先的に処理しなければならない。
1 + 1 * 5
と言う式なら
1 1 5 * +
このように、掛け算記号の方が足し算より先に来なければならないのだ。
しかも・・・
1 + 1 * 5 / 5 + 1
等の場合、
1 1 5 * 5 / + 1 +
のように、掛け算割り算の所だけは、順番が入れ替わらない。
これはどのように表現すればよいか・・・?
ぶちまけルールに変更が必要だ・・・。
掛け算、割り算優先なのだから、
「メモぶちまけしようとして、足し算引き算に当たったら、そこでぶちまけ終了」
これで、足し算引き算は「次回記号が出て来るとき」まで、後回しにされる。
ヨシ、コレだ。
脳内シミュレーションしてみよう。
最初に 1 で 結果「1」
次が + で ぶちまけルール発動
・・・したいけどメモが無くて
結果「1」とメモ「+」
さらに 1 で 結果「1 1」とメモ「+」
続いて * で ぶちまけルール発動!
足し算引き算に当たるまで、ぶちまける!
・・・けど、足し算なのでそのまま残す。
結果「1 1」とメモ「+、*」
そして 5 で 結果「1 1 5」にメモ「+、*」
ついに / が!
ぶちまけルール発動!
足し算引き算に当たるまで、ぶちまける!
結果「1 1 5 *」 メモ「+、/」
後は 5 で 結果「1 1 5 * 5」 メモ「+、/」
そして+ ぶちまけルール発動!
結果「1 1 5 * 5 / +」で、メモが「+」に。
最後に1が入り、結果「1 1 5 * 5 / + 1」にメモ「+」
終わったのでメモを全部ぶちまけ。
「1 1 5 * 5 / + 1 +」
・・・できた!
ではコーディングする。
最後のスタックの中身が分からないと駄目なので、モジュールに一つ命令を加える。
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
//----------------------------------------------------------------------------
f = "1+1*5/5+1" // 変換前の式を書いた文字列変数
gosub *rpnconv // 逆ポーランド表記に変換するサブルーチン
mes rpn // 結果を表示
stop
*rpnconv
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
switch value
case "+" : case "-" // 足し算、割り算
if buf != "" {
rpn += buf + " " // 文字が溜まっていたら、結果に出力
buf = "" // そしてクリア
}
while( isEmpty() = 0 ) // スタックが空になるまで
rpn += pop() + " " // 先に入っていた記号をぶちまける
wend
push value // 記号をスタックに積む
swbreak
case "*" : case "/"
if buf != "" {
rpn += buf + " " // 文字が溜まっていたら、結果に出力
buf = "" // そしてクリア
}
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" { // 前に入れた記号が掛け算割り算なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
swbreak
swend
index++
wend
rpn += buf + " " // 最後に残った数字を結果へ
while( isEmpty()=0 )
rpn += pop() + " " // 最後に残った記号を全て結果へ
wend
return
| |
|
2011/11/13(Sun) 03:58:35|NO.42974
さて、それではさらに1step上に行こう。
1 + ( 2 + 1 ) * 5
・・・カッコの登場だ。
この式は
1 2 1 + 5 * +
になる。
なにせ、カッコは掛け算割り算よりも優先度が高い・・・。
また、状況に合わせて、「ぶちまけルール」が変わってしまう。
どうするか?
掛け算割り算の時は、「足し算引き算を後回しにする為」に、
ぶちまけルールで「足し算引き算は残す」様にした。
じゃあ、カッコも、カッコより手前の分を「後回し」にすれば先に処理が出来る?
じゃあ、目印になるように「(」をメモって置き、「)」が出たら、「(」までをぶちまければおk?
実は、足りない。
「足し算引き算」の時、誤って「(」までぶちまけないようにしないといけない。
「)」が出たとき、「(」を探すのだから、足し算引き算のぶちまけ時に「(」が消されてもいけない。
これで脳内シミュレーションしてみる!
1 + ( 2 + 1 ) * 5
だから・・・
最初が 1 で 結果「1」
次に + でぶちまけ・・・不能。
結果「1」でメモ「+」
進んで ( 。そのままメモる。 結果「1」メモ「+、(」
次に 2 で、結果「1 2」 メモ「+、(」
次が + で、ぶちまけ!・・・たいけど、先刻のが「(」なので、ぶちまけられない。
そのままメモって、
結果「1 2」メモ「+、(、+」
次。 1 。結果「1 2 1」メモ「+、(、+」
そして ) !
新ぶちまけルール発動!
カッコまでぶちまける!
そして、この時、「(」と「)」は結果に残さず、捨ててしまう!
結果「1 2 1 + 」メモ「+」
次に * で、連続ぶちまけルール発動!
・・・したいけど、足し算引き算なのでぶちまけ不能!
結果「1 2 1 +」メモ「+、*」
最後が 5 で、結果「1 2 1 + 5」メモ「+、*」
終わったので、メモをぶちまける。
後に入れた方から先に出るので
結果「1 2 1 + 5 * +」
デキタ。
ついでにTips。
式を入れるとき、最初から「()」で式を囲んでしまうと、
カッコ処理が発動して「最後にあまった結果を付け加える」処理が省ける
" "(空白)を無視してしまえば、結果が綺麗になる。
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
//----------------------------------------------------------------------------
f = " 15 + ( 2 + 10)*5" // 変換前の式を書いた文字列変数
gosub *rpnconv // 逆ポーランド表記に変換するサブルーチン
mes rpn // 結果を表示
stop
*rpnconv
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + f + ")" // ループ内で処理完了する為の処置
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
switch value
case "+" : case "-" // 足し算、割り算
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
swbreak
case "*" : case "/" // 掛け算、割り算
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" { // 前に入れた記号が掛け算割り算なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
if buf != "" : buf += " " // 文字が溜まっていたら、結果に出力、クリア
push value
swbreak
case ")"
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
swbreak
swend
index++
wend
return
とりあえず、「式->逆ポーランド表記」への変換ができた。
だが実は、これだけでは完璧ではない。
たとえば、「1+(-2+10)」や、他にも「-(1+5)」のような
「数値のマイナス表現」に対応していない。
まあ、それは「やる気の有る奴」が気合入れてやれば、出来る事だろう。
コピペで済まそうとか考えるファッキン軟弱野郎にはこの程度でお似合いだ。
「何が足りない」と教えてもらえるだけでも感謝するんだな!ウェーハッハ!!
| |
|
2011/11/13(Sun) 03:59:07|NO.42975
さて、最初に話したように「計算」の方は難しくない。
順番にスタックに積んで行き
計算の記号に突き当たったなら、スタックから二つ数字を取り出して、計算して、結果をスタックに戻す。
最後にスタックに残った数値が答えだ。
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
//----------------------------------------------------------------------------
f = "-(1+(-2+10))" // 変換前の式を書いた文字列変数
mes f
gosub *rpnconv // 逆ポーランド表記に変換するサブルーチン
mes rpn // 結果を表示
gosub *rpncomp // 逆ポーランド表記の式を計算する
mes ans // 答えを表示
stop
//----------------------------------------------------------------------------
*rpnconv
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + f + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
switch value
case "+" : case "-" // 足し算、割り算
if flg = 1 { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 記号は計算用で確定なのでスタックへ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
flg = 1 //「(」記号は、確実に計算用なので、1
swbreak
case "*" : case "/" // 掛け算、割り算
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" { // 前に入れた記号が掛け算割り算なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return
//----------------------------------------------------------------------------
*rpncomp
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
switch value
case "+"
work2 = double( pop() ) // 手前の
work1 = double( pop() ) // 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = double( pop() ) // 手前の
work1 = double( pop() ) // 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = double( pop() ) // 手前の
work1 = double( pop() ) // 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = double( pop() ) // 手前の
work1 = double( pop() ) // 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
ans = double( pop() ) // 最後にスタックに残った値が、答え
return
・・・・。
ん?
細かい事は気にするな!!
さて、計算も出来るようになった。
また一歩、野望に近づいた。
これで、テキストファイルに書いた式が計算できるだろう・・・!!
もう何も怖くない。
| |
|
2011/11/13(Sun) 03:59:31|NO.42976
で・・・だ。
ここまで来たからには・・・変数・・・にも・・・
手を出してみるべきなんじゃね?
最初の方に言ったな。
・変数名称と値を引数として与えると、「それを覚えてくれる」サブルーチン
・変数名を指定すると、「変数の覚えている数値を返してくれる」サブルーチン
と。
組み込んでみようじゃないですか。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#deffunc varInit int _length
sdim varName , 64 , _length
ddim varValue , _length
return
#global
varInit 100
//----------------------------------------------------------------------------
f = "-(1+(a+10))" // 変換前の式を書いた文字列変数
setVar "a" , 10.0 // 変数登録
mes f
gosub *rpnconv // 逆ポーランド表記に変換するサブルーチン
mes rpn // 結果を表示
gosub *rpncomp // 逆ポーランド表記の式を計算する
mes ans // 答えを表示
stop
//----------------------------------------------------------------------------
*rpnconv
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + f + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
switch value
case "+" : case "-" // 足し算、割り算
if flg = 1 { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 記号は計算用で確定なのでスタックへ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
flg = 1 //「(」記号は、確実に計算用なので、1
swbreak
case "*" : case "/" // 掛け算、割り算
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" { // 前に入れた記号が掛け算割り算なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return
//----------------------------------------------------------------------------
*rpncomp
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
switch value
case "+"
src = pop() : gosub *getParam : work2 = dst // 手前の
src = pop() : gosub *getParam : work1 = dst /// 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
src = pop() : gosub *getParam : work2 = dst // 手前の
src = pop() : gosub *getParam : work1 = dst /// 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
src = pop() : gosub *getParam : work2 = dst // 手前の
src = pop() : gosub *getParam : work1 = dst /// 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
src = pop() : gosub *getParam : work2 = dst // 手前の
src = pop() : gosub *getParam : work1 = dst /// 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
ans = double( pop() ) // 最後にスタックに残った値が、答え
return
//----------------------------------------------------------------------------
*getParam
if isAlphabet( peek( src , 0 ) ) = 1 { // 先頭一文字目がアルファベットなら変数名
dst = getVar( src ) // 変数の値を取得して帰る
return
}
dst = double( src ) // 数値化を試みる
return
変数管理もモジュールにしてみた。
最後に追加したgetParamというサブルーチンが
変数か数値かを判定している。
この部分、モジュールの中に入れてしまっても良いかも知れんね。
| |
|
2011/11/13(Sun) 03:59:57|NO.42977
先刻のテストでは、変数に値を入れるのに、setVarというモジュールの命令を使った。
これでは幻滅だ。「c = a + b」とか、書けてこそだろう!?
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value // 変数登録 未登録なら自動登録
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name // 変数内容取得
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#defcfunc varConv str _src , local src
src = _src
if isAlphabet( peek( src , 0 ) ) = 1 : return getVar( src )
return double( src )
#deffunc varList local data , local i // デバグ用
data = ""
for i,0,vIndex
data += "" + i + ":[" + varName( i ) + "] = " + varValue( i ) + "\n"
next
dialog data
return
#deffunc varInit int _length
sdim varName , 64 , _length
ddim varValue , _length
return
#global
varInit 100
//----------------------------------------------------------------------------
f = "b=-( 1+(a+10))" // 変換前の式を書いた文字列変数
setVar "a" , 10.0
mes f
gosub *rpnconv // 逆ポーランド表記に変換するサブルーチン
mes rpn // 結果を表示
gosub *rpncomp // 逆ポーランド表記の式を計算する
mes ans // 答えを表示
mes "b = " + getVar( "b" )
stop
//----------------------------------------------------------------------------
*rpnconv
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + f + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
switch value
case "+" : case "-" // 足し算、割り算
case "=" // さらに、イコール
if flg = 1 & ( value = "+" | value = "-" ) { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "=" { // =の後に+-が入ったなら、
rpn += "0 " // 手前に0を入れて調整
push value // 引き算として記号はプッシュ
swbreak
} // =の後に+-が入ったなら、手前に0を入れて調整
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 引き算として記号はプッシュ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」なら終了
if lastStack() = "=" : _break // 「=」なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
flg = 1 //「(」記号は、確実に計算用なので、1
swbreak
case "*" : case "/" // 掛け算、割り算
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" { // 前に入れた記号が掛け算割り算なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return
//----------------------------------------------------------------------------
*rpncomp
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
switch value
case "="
work2 = varConv( pop() ) // 代入する値取得
setVar pop() , work2 // 変数名取得して代入
push "1" // 計算では無いが、処理完了的意味で1を返す
swbreak
case "+"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
ans = varConv( pop() ) // 最後にスタックに残った値が、答え
return
| |
|
2011/11/13(Sun) 04:00:23|NO.42978
最後に、全部モジュール化してみた。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化する
sdim stack , 64 , _length
return
#global
stackInit 100
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value // 変数登録 未登録なら自動登録
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name // 変数内容取得
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#defcfunc varConv str _src , local src
src = _src
if isAlphabet( peek( src , 0 ) ) = 1 : return getVar( src )
return double( src )
#deffunc varList local data , local i // デバグ用
data = ""
for i,0,vIndex
data += "" + i + ":[" + varName( i ) + "] = " + varValue( i ) + "\n"
next
dialog data
return
#deffunc varInit int _length
sdim varName , 64 , _length
ddim varValue , _length
return
#global
varInit 100
//----------------------------------------------------------------------------
#module "rpn"
#defcfunc rpnConv str _src
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + _src + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "+" : case "-" // 足し算、割り算
case "<" : case ">" // より大きい、より小さい
case "<=" : case ">=" // 以上、以下
case "==" : case "!=" // 同じ、同じじゃない
case "=" // 代入
if flg = 1 & ( value = "+" | value = "-" ) { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "=" { // =の後に+-が入ったなら、
rpn += "0 " // 手前に0を入れて調整
push value // 引き算として記号はプッシュ
swbreak
} // =の後に+-が入ったなら、手前に0を入れて調整
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 引き算として記号はプッシュ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」 なら終了
if lastStack() = "<" : _break // 「<」 なら終了
if lastStack() = ">" : _break // 「>」 なら終了
if lastStack() = "<=" : _break // 「<=」なら終了
if lastStack() = ">=" : _break // 「>=」なら終了
if lastStack() = "==" : _break // 「==」なら終了
if lastStack() = "!=" : _break // 「!=」なら終了
if lastStack() = "=" : _break // 「=」 なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
flg = 1 //「(」記号は、確実に計算用なので、1
swbreak
case "*" : case "/" // 掛け算、割り算
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" { // 前に入れた記号が掛け算割り算なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「(」記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return rpn
//----------------------------------------------------------------------------
#defcfunc rpncomp str _src
rpn = _src
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "<"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 < work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 > work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "<="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 <= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 >= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "=="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 = work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "!="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 != work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "="
work2 = varConv( pop() ) // 代入する値取得
setVar pop() , work2 // 変数名取得して代入
push "1" // 計算では無いが、処理完了的意味で1を返す
swbreak
case "+"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) /// 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
return varConv( pop() ) // 最後にスタックに残った値が、答え
//----------------------------------------------------------------------------
#defcfunc compute str _src
return int( rpncomp( rpnconv( _src ) ) )
#global
//----------------------------------------------------------------------------
// sample
mes compute( "a = 10" )
mes compute( "b = 20" )
mes compute( "c = a + b" )
mes compute( "c" )
mes compute( "a > b" )
mes compute( "a < b" )
mes compute( "a+10 == b" )
stop
作ったものを、とりあえずぶちまけるが、
当然ながら俺は「コピペして終了」など許さぬ。
その点は安心してもらいたい。
端的に言うと「0で割り算」以外のエラー処理は行なってねえ!!
カッコの数が左右で違うとか。
計算の記号だけが幾つも続くとか。
色々と、エラーが起こる要因があろう。
さらに、バグだってあるかもしれん。
and とか or とか xor とか 割り算余り とかも付いてねぇ。
使いたい奴だけが勝手にバグ取りしてエラー処理して拡張して自分のプログラムに組み込むがいいさ。
| |
|
2011/11/13(Sun) 04:01:34|NO.42979
仕事が終わらなくて、現実逃避の余り、ついカっとして、遊ぶ金欲しさに、殺すつもりは無かった。
・・・すまん。
|
|
2011/11/13(Sun) 05:47:33|NO.42980
hsp愛を感じます
|
|
2011/11/13(Sun) 07:07:59|NO.42981
( ゚д゚ )ポカーン
|
|
2011/11/13(Sun) 07:08:49|NO.42982
なんか、こまごまコメントに間違いがあったので
直すついでに&,|,^,!追加
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用スタック内容リスト表示
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化
sdim stack , 64 , _length
return
#global
stackInit 100 // スタック最大100件で初期化
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value // 変数登録 未登録なら自動登録
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name // 変数内容取得
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#defcfunc varConv str _src , local src // 与えられた文字列を判別して変数内容か、数値化した値を返す
src = _src
if isAlphabet( peek( src , 0 ) ) = 1 : return getVar( src )
return double( src )
#deffunc varList local data , local i // デバグ用変数リスト表示
data = ""
for i,0,vIndex
data += "" + i + ":[" + varName( i ) + "] = " + varValue( i ) + "\n"
next
dialog data
return
#deffunc varInit int _length // 初期化
sdim varName , 64 , _length
ddim varValue , _length
return
#global
varInit 100 // 変数最大100個で初期化
//----------------------------------------------------------------------------
#module "rpn"
#defcfunc rpnConv str _src // 数式を逆ポーランド表記に変換する
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + _src + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "+" : case "-" // 足し算、割り算
case "<" : case ">" // より大きい、より小さい
case "<=" : case ">=" // 以上、以下
case "==" : case "!=" // 同じ、同じじゃない
case "&" : case "|" // and 、or
case "^" : case "=" // xor 、代入
if flg = 1 & ( value = "+" | value = "-" ) { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "=" { // =の後に+-が入ったなら、
rpn += "0 " // 手前に0を入れて調整
push value // 引き算として記号はプッシュ
swbreak
} // =の後に+-が入ったなら、手前に0を入れて調整
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 引き算として記号はプッシュ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 // ここに来た記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」 なら終了
if lastStack() = "<" : _break // 「<」 なら終了
if lastStack() = ">" : _break // 「>」 なら終了
if lastStack() = "<=" : _break // 「<=」なら終了
if lastStack() = ">=" : _break // 「>=」なら終了
if lastStack() = "==" : _break // 「==」なら終了
if lastStack() = "!=" : _break // 「!=」なら終了
if lastStack() = "&" : _break // 「&」 なら終了
if lastStack() = "|" : _break // 「|」 なら終了
if lastStack() = "^" : _break // 「^」 なら終了
if lastStack() = "!" : _break // 「!」 なら終了
if lastStack() = "=" : _break // 「=」 なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
swbreak
case "*" : case "/" // 掛け算、割り算
case "!" // not
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" or lastStack() = "!" { // 前に入れた記号が掛け算割り算Not記号なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「)」の次は、記号が入って当然なので0でいい
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return rpn
//----------------------------------------------------------------------------
#defcfunc rpncomp str _src // 逆ポーランド表記の計算式を計算する
rpn = _src
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "<"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 < work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 > work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "<="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 <= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 >= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "=="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 = work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "!="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 != work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "&"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 & work2 ) // 計算してスタックに戻す
swbreak
case "|"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 | work2 ) // 計算してスタックに戻す
swbreak
case "^"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 ^ work2 ) // 計算してスタックに戻す
swbreak
case "!"
work2 = int( varConv( pop() ) ) // Notは1件分にのみ適用
push "" + ( ( 0xffffffff ^ work2 ) ) // 計算してスタックに戻す
swbreak
case "="
work2 = varConv( pop() ) // 代入する値取得
setVar pop() , work2 // 変数名取得して代入
push "1" // 計算では無いが、処理完了的意味で1を返す
swbreak
case "+"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
return varConv( pop() ) // 最後にスタックに残った値が、答え
//----------------------------------------------------------------------------
#defcfunc compute str _src // 計算式を逆ポーランド表記に変換して、それを計算する
return int( rpncomp( rpnconv( _src ) ) )
#global
//----------------------------------------------------------------------------
// sample
mes compute( "a = 10" )
mes compute( "b = 20" )
mes compute( "c = a + b" )
mes compute( "c" )
mes compute( "a > b" )
mes compute( "a < b" )
mes compute( "a+10 == b" )
mes compute( "1 & 3" )
mes compute( "1 | 2" )
mes compute( "1 ^ 1" )
mes compute( "!0" )
stop
| |
|
2011/11/13(Sun) 09:29:14|NO.42986
こういうことか・・・。
5 + 2 * 5 + 5
5 2 5 * + 5 +
( ( 5, ( 2, 5 )* )+, 5 )+
|
|
2011/11/13(Sun) 16:51:58|NO.42996
次は「すみませんプログラムをIF文みたいに分岐して実行するにはどうしたらいいんですか><」とか、
「すみませんHSPのラベルとgoto、gosubみたいなことがしたいです><」とか、
「すみませんこのプログラムをコンパイルしt(ry ><」
とか言うやつが出てこなければいいのだが。
いや、出てくるだろう。(反語)
そういえば途中から逆ポーランド法の計算の説明になってきて
当初の命令と引数を分けて取得し、命令の文字列によって
処理を分岐させるという目的からそれているような……。
個人的には変数への値の代入と、逆ポーランド法の計算のほうが役に立ったけど。
|
|
2011/11/14(Mon) 08:15:13|NO.43003
>「ファーストイン・ラストアウト(先に入ったモンが最後に出る)」
>略して「FILO」と呼ぶらしい。
このFILOとかFIFOとか、呼び方がいろいろあってややこしいんだよね。
ほんとうは2種類しかないのに呼び方は4種類もある
FIFO(first in first out)
FILO(first in last out)
LIFO(last in first out)
LILO(last in last out)
2種類しかないのに呼び方が4種類もあるとわかりにくいから
呼び方も2種類にしたい。
なぜ呼び方が4種類あるかというと要素が4種類あって、
その4種類から2つを選んで並べているからだ。
その4種類の要素は
first in
first out
last in
last out
この中でいちばん重要なのは何だろう。
さらに分解してみると以下の4つになる
first
last
in
out
firstとlastはどっちが重要か。
「そんなの場合によって違う」なんて言わないであえて結論を
出してみると、やっぱり最後より最初が重要だろう。
ではinとoutは?
入力と出力。どっちも大事に決まっている。
でも、あえてどっちかを選ぶとしたらやっぱり出力だろう。
何かを選ぶとき、自分のほしいものが出力されるかどうかで選ぶ。
入力なんか後で考えればいい。
というわけで、最初に何が出てくるかが重要だ。
つまり、「first out」
おお、「first out」がふくまれるものは2つしかない。
first in first out
last in first out
の2つだ。
だから、よい子はFILOなんて呼び方をしちゃいけません。
最後に出てくるものなんか、どうでもいいんだよ。
え? 呼び方なんかどうでもいいって?
こりゃまた失礼しました。
|
|
2011/11/14(Mon) 08:43:34|NO.43005
以前、演算機能を実装しようとしたとき、何とか気泡に変換して
その後で演算という方法も考えてみたが、二度手間な気がしてやる気がしなかった。
|
|
2011/11/14(Mon) 08:52:00|NO.43006
常識のように言っていますが、あくまで持論です。
|
|
2011/11/14(Mon) 09:55:57|NO.43007
色々な要因が重なりハイな脳で作ったが為に、
書こうとした内容と出だしの方向に大きくずれがあったのは認める。
一応、「この位自力で書けるだろ」とは振ったものの、
折角なのでアフターケアしてみる。
LIFO?
・・・。
・・・・・・。
∧__∧
(`・ω・´) まてぇい
.ノ^ yヽ、
ヽ,,ノ==l ノ
/ l |
"""~""""""~"""~"""~"
/|
| |
_|_|_
(ミ゙∧,,∧
∧,,∧ (^(ω・´ )
(-ω-´) `[_ノ )
━0ニフx と) ノノ l、ヽ、
(ノノハ_l_l) 〈_ノノヽヽ>
では、お題の文字列をこのようにする。
"ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
当初考えたとおり、「後ろから命令を探す」事にして、手順を考える。
まず、strlen等で、文字列の長さを決める。
あとは、その長さが0になるまで繰り返し、1文字づつ調べてゆく。
上のお題では、命令は「命令名(引数)」の形であり、
算数的なカッコの使い方と違う点は、「「(」より前には関数名が有る事」だ。
つまり、まず「(」を見つけたら、その手前にある文字によって、
「数学的な計算の式」か、「命令の始まり」かを区別できる。
ついでに、命令の名前には「英字か数字」と言うルールもつけよう。
そこで、こうだ。
1:後ろから、1文字づつ調べる。
2:その文字が「(」だったら、命令の引数開始の(の可能性があるので、フラグをたてる。
4:フラグが立っていたら・・・
4−1:空白だったら、「rnd ( ...」の様な場合とか 「12 * ( 15 ...」の可能性があるので無視
4−2:英字や数字でない文字に当たったら、それは命令では有りませんでした。フラグを下げる。
4−3:逆に英字か数字に当たったら、その位置こそが引数始まりの「(」でしょう。終了
5:1から繰り返す
そして、肝心なことだが、このルールで「(」が見つけられなかったら、その文字列には命令は存在しない。
命令探しの旅はここで終了である。
所で、先ほどから「命令」「命令」と連呼しているが・・・
「rnd( 640 )」・・・
以下にも、何か0から639までの乱数を返して来そうな命令である。
というか、その意図で書いた。
こういう「値を返してくる命令」は「関数」と呼ぶ。
自分で作る分には南都でお勝手に名づければ良いと思うが、一応、今後は関数でウッドボール。
コーディングしてみる。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#global
//----------------------------------------------------------------------------
source = "ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
fOpen = getIndexOpen( source )
mes "[ 1 2 3 4"
mes "[01234567890123456789012345678901234567890123456789"
mes "[" + source + "]"
mes "--------------------------------------------------"
mes "引数開始「(」位置 : " + fOpen
∧,,∧ ∧,,∧
( `・ω) ( ´;ω)
∠(っ{ニl━っ /´ 7っフっ
rノノ ヾヽ _ノ l、ヽ<
`ー' 7l_ノ ヽノノ゙ヽ>フ
まー、俺周辺だと普通にfiloなのは事実でなー・・・
でも検索するとlifoばっかりだなorz
スチールテープがスチロンと略されるので
俺周辺では「ポリエステルテープ」が「エスロン」と略されてたとか
笑われる事例は色々有るのう・・・orz
| |
|
2011/11/14(Mon) 09:56:32|NO.43008
関数のカッコ位置が決まった。
では更に進めて、関数名の位置を取得する。
まず、お題の元文字列と、カッコの位置を与える。
そこから「英字か数字」を調べ、「英字か数字」で無い場所に辿り着いたら、そこが名前の位置だ。
ここで注意しておくのは「スタート位置」だ。
先ほどの処理で確認した位置はズバリ「(」の位置なので、ここから開始すると「英字でも数字でもない」とかされかねない。
だから、開始位置は-1して渡す。渡された側が-1しても良い。
他に「空白」の例がある。
「rnd ( ...」のように、空白が入っている可能性が有る。
「関数名と(の間に空白を入れてはいけない」と言うルールをつければ、「エラー」と言えるが、一応対処する。
具体的にはまず「空白じゃなくなったら、関数名の上に居る」と言うフラグを作る。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#defcfunc getIndexName str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数名の上に居るかどうか
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
index-- // 参照位置を進める(先に進めるので、必要があれば後で戻す)
if fFunc = 1 { // 関数名と思われる文字の上に居る時・・・
if isNumeric ( value ) = 1 : _continue // 数字なら、まだ。
if isAlphabet( value ) = 1 : _continue // 英字なら、まだ。
return index + 2 // ここまで来たら、この1つ前までが関数名の始まり(参照位置を先に進めた分も戻す)
} else {
if value != ' ' : fFunc = 1 // 空白じゃない部分が登場したら、関数名上に辿り着いた。
}
wend
return 0
#global
//----------------------------------------------------------------------------
source = "ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
fOpen = getIndexOpen( source )
fName = getIndexName( source , fOpen - 1 )
mes "[ 1 2 3 4"
mes "[01234567890123456789012345678901234567890123456789"
mes "[" + source + "]"
mes "--------------------------------------------------"
mes "関数名開始 : " + fName
mes "引数開始「(」位置 : " + fOpen
| |
|
2011/11/14(Mon) 09:56:48|NO.43009
次に、関数の終了位置を探す。
これは簡単だ。
引数にカッコが使われているかもしれないので
「(」が開けられた回数と「)」で閉じられた回数をカウントして、対消滅したら終了だ。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#defcfunc getIndexName str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数名の上に居るかどうか
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
index-- // 参照位置を進める(先に進めるので、必要があれば後で戻す)
if fFunc = 1 { // 関数名と思われる文字の上に居る時・・・
if isNumeric ( value ) = 1 : _continue // 数字なら、まだ。
if isAlphabet( value ) = 1 : _continue // 英字なら、まだ。
return index + 2 // ここまで来たら、この1つ前までが関数名の始まり(参照位置を先に進めた分も戻す)
} else {
if value != ' ' : fFunc = 1 // 空白じゃない部分が登場したら、関数名上に辿り着いた。
}
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#defcfunc getIndexClose str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
cNest = 0 // カッコの左右のつじつまカウンタ
while ( peek( source , index ) != 0 ) // 文字列の終わりは必ず0なので、そこまで進む
value = peek( source , index ) // 1キャラクタ取得
if value = '(' : cNest++ // ( なら、一段レベルアップ
if value = ')' : cNest-- // ) なら、一段レベルダウン
if cNest = 0 : return index // ±0になったら、そこが終了位置
index++ // 文字参照位置を進める
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#global
//----------------------------------------------------------------------------
source = "ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
fOpen = getIndexOpen ( source )
fName = getIndexName ( source , fOpen - 1 )
fClose = getIndexClose( source , fOpen )
mes "[ 1 2 3 4"
mes "[01234567890123456789012345678901234567890123456789"
mes "[" + source + "]"
mes "--------------------------------------------------"
mes "関数名開始 : " + fName
mes "引数開始「(」位置 : " + fOpen
mes "引数終了「)」位置 : " + fClose
・・・。
こうして関数の位置が特定できた。
あとはstrmid等駆使すれば
関数名を取り出し、名称別の処理に引数を渡す事ができるだろう。
| |
|
2011/11/14(Mon) 10:12:48|NO.43010
なお、とことんまで「コピペするだけで終わり」を許さぬ姿勢で臨む。
これら例は上の逆ポーランド表記変換から変数管理、そして今回に至るまで
「文字列や日本語が含まれている」事は一切関知していない!
自力で付け加える気の無い
只のクレクレファッキンチキン野郎は数値だけで満足するんだな!!
|
|
2011/11/15(Tue) 09:21:25|NO.43019
現実逃避はまだ続く。
前のモジュールを使って、関数を抜き出して、抜き出した情報を元に処理をして、
その結果を元文字列に戻してゆく処理をしてみる。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#defcfunc getIndexName str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数名の上に居るかどうか
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
index-- // 参照位置を進める(先に進めるので、必要があれば後で戻す)
if fFunc = 1 { // 関数名と思われる文字の上に居る時・・・
if isNumeric ( value ) = 1 : _continue // 数字なら、まだ。
if isAlphabet( value ) = 1 : _continue // 英字なら、まだ。
return index + 2 // ここまで来たら、この1つ前までが関数名の始まり(参照位置を先に進めた分も戻す)
} else {
if value != ' ' : fFunc = 1 // 空白じゃない部分が登場したら、関数名上に辿り着いた。
}
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#defcfunc getIndexClose str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
cNest = 0 // カッコの左右のつじつまカウンタ
while ( peek( source , index ) != 0 ) // 文字列の終わりは必ず0なので、そこまで進む
value = peek( source , index ) // 1キャラクタ取得
if value = '(' : cNest++ // ( なら、一段レベルアップ
if value = ')' : cNest-- // ) なら、一段レベルダウン
if cNest = 0 : return index // ±0になったら、そこが終了位置
index++ // 文字参照位置を進める
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#deffunc devideFunc str _source , var str1 , var func , var arg , var str2
source = _source // 元文字列
srcLen = strlen( source ) // 文字列サイズ
fOpen = getIndexOpen ( source ) // 関数の引数開始「(」取得
if fOpen <= 0 { // 無ければ、関数は無い。
str1 = source
func = ""
arg = ""
str2 = ""
} else {
fName = getIndexName ( source , fOpen - 1 ) // 関数名位地取得
fClose = getIndexClose( source , fOpen ) // 関数の引数終了「)」位地取得
str1 = strmid( source , 0 , fName ) // 関数より手前の文字列取得
func = strmid( source , fName , fOpen - fName ) // 関数名文字列取得
arg = strmid( source , fOpen + 1 , fClose - fOpen - 1 ) // 関数引数取得(カッコヌキ)
str2 = strmid( source , fClose + 1 , srcLen - fClose ) // 関数より後文字列取得
}
mes "--------------------------------------------------"
mes "元文字列 : [" + source + "]"
mes "手前文字列 : [" + str1 + "]"
mes "関数名 : [" + func + "]"
mes "引数全体 : [" + arg + "]"
mes "後文字列 : [" + str2 + "]"
return
#global
//----------------------------------------------------------------------------
mes "[ 1 2 3 4"
mes "[01234567890123456789012345678901234567890123456789"
source = "ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
while( 1 )
devideFunc source , str1 , func , arg , str2 // 関数、引数と、それ以前、その後を分割
if func = "" : _break // 関数名が無ければ、関数は無かった
gosub *execute // 関数名別処理呼び出し
source = str1 + result + str2 // 結果を組み立てる
wend
stop
*execute
if inStr(func,0,"rnd" ) >= 0 { gosub *frnd : return } // rnd関数
if inStr(func,0,"pset") >= 0 { gosub *fpset : return } // pset関数
return
*frnd
result = 0 // 引数取り出しが出来ないので取り合えず0にしておく
return
*fpset
result = 0 // 複数の引数取り出しが出来ないので取り合えず0にしておく
return
| |
|
2011/11/15(Tue) 09:24:16|NO.43020
関数部分が処理され、「とりあえず0」にされてる用紙が確認できるだろう。
関数の個別取得ができたので、いよいよ、最初に作った計算部分と合体する。
とりあえず、引数が一つだけのrnd関数を実装してみる。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用スタック内容リスト表示
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化
sdim stack , 64 , _length
return
#global
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value // 変数登録 未登録なら自動登録
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name // 変数内容取得
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#defcfunc varConv str _src , local src // 与えられた文字列を判別して変数内容か、数値化した値を返す
src = _src
if isAlphabet( peek( src , 0 ) ) = 1 : return getVar( src )
return double( src )
#deffunc varList local data , local i // デバグ用変数リスト表示
data = ""
for i,0,vIndex
data += "" + i + ":[" + varName( i ) + "] = " + varValue( i ) + "\n"
next
dialog data
return
#deffunc varInit int _length // 初期化
sdim varName , 64 , _length
ddim varValue , _length
return
#global
//----------------------------------------------------------------------------
#module "rpn"
#defcfunc rpnConv str _src // 数式を逆ポーランド表記に変換する
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + _src + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "+" : case "-" // 足し算、割り算
case "<" : case ">" // より大きい、より小さい
case "<=" : case ">=" // 以上、以下
case "==" : case "!=" // 同じ、同じじゃない
case "&" : case "|" // and 、or
case "^" : case "=" // xor 、代入
if flg = 1 & ( value = "+" | value = "-" ) { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "=" { // =の後に+-が入ったなら、
rpn += "0 " // 手前に0を入れて調整
push value // 引き算として記号はプッシュ
swbreak
} // =の後に+-が入ったなら、手前に0を入れて調整
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 引き算として記号はプッシュ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 // ここに来た記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」 なら終了
if lastStack() = "<" : _break // 「<」 なら終了
if lastStack() = ">" : _break // 「>」 なら終了
if lastStack() = "<=" : _break // 「<=」なら終了
if lastStack() = ">=" : _break // 「>=」なら終了
if lastStack() = "==" : _break // 「==」なら終了
if lastStack() = "!=" : _break // 「!=」なら終了
if lastStack() = "&" : _break // 「&」 なら終了
if lastStack() = "|" : _break // 「|」 なら終了
if lastStack() = "^" : _break // 「^」 なら終了
if lastStack() = "!" : _break // 「!」 なら終了
if lastStack() = "=" : _break // 「=」 なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
swbreak
case "*" : case "/" // 掛け算、割り算
case "!" // not
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" or lastStack() = "!" { // 前に入れた記号が掛け算割り算Not記号なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「)」の次は、記号が入って当然なので0でいい
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return rpn
//----------------------------------------------------------------------------
#defcfunc rpncomp str _src // 逆ポーランド表記の計算式を計算する
rpn = _src
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "<"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 < work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 > work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "<="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 <= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 >= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "=="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 = work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "!="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 != work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "&"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 & work2 ) // 計算してスタックに戻す
swbreak
case "|"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 | work2 ) // 計算してスタックに戻す
swbreak
case "^"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 ^ work2 ) // 計算してスタックに戻す
swbreak
case "!"
work2 = int( varConv( pop() ) ) // Notは1件分にのみ適用
push "" + ( ( 0xffffffff ^ work2 ) ) // 計算してスタックに戻す
swbreak
case "="
work2 = varConv( pop() ) // 代入する値取得
setVar pop() , work2 // 変数名取得して代入
push "1" // 計算では無いが、処理完了的意味で1を返す
swbreak
case "+"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
return varConv( pop() ) // 最後にスタックに残った値が、答え
//----------------------------------------------------------------------------
#defcfunc compute str _src // 計算式を逆ポーランド表記に変換して、それを計算する
return int( rpncomp( rpnconv( _src ) ) )
#global
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#defcfunc getIndexName str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数名の上に居るかどうか
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
index-- // 参照位置を進める(先に進めるので、必要があれば後で戻す)
if fFunc = 1 { // 関数名と思われる文字の上に居る時・・・
if isNumeric ( value ) = 1 : _continue // 数字なら、まだ。
if isAlphabet( value ) = 1 : _continue // 英字なら、まだ。
return index + 2 // ここまで来たら、この1つ前までが関数名の始まり(参照位置を先に進めた分も戻す)
} else {
if value != ' ' : fFunc = 1 // 空白じゃない部分が登場したら、関数名上に辿り着いた。
}
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#defcfunc getIndexClose str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
cNest = 0 // カッコの左右のつじつまカウンタ
while ( peek( source , index ) != 0 ) // 文字列の終わりは必ず0なので、そこまで進む
value = peek( source , index ) // 1キャラクタ取得
if value = '(' : cNest++ // ( なら、一段レベルアップ
if value = ')' : cNest-- // ) なら、一段レベルダウン
if cNest = 0 : return index // ±0になったら、そこが終了位置
index++ // 文字参照位置を進める
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#deffunc devideFunc str _source , var str1 , var func , var arg , var str2
source = _source // 元文字列
srcLen = strlen( source ) // 文字列サイズ
fOpen = getIndexOpen ( source ) // 関数の引数開始「(」取得
if fOpen <= 0 { // 無ければ、関数は無い。
str1 = source
func = ""
arg = ""
str2 = ""
} else {
fName = getIndexName ( source , fOpen - 1 ) // 関数名位地取得
fClose = getIndexClose( source , fOpen ) // 関数の引数終了「)」位地取得
str1 = strmid( source , 0 , fName ) // 関数より手前の文字列取得
func = strmid( source , fName , fOpen - fName ) // 関数名文字列取得
arg = strmid( source , fOpen + 1 , fClose - fOpen - 1 ) // 関数引数取得(カッコヌキ)
str2 = strmid( source , fClose + 1 , srcLen - fClose ) // 関数より後文字列取得
}
mes "-----------------------------------------------------------------------"
mes "元文字列 :[" + source + "]"
mes "手前文字列:[" + str1 + "] 関数名:[" + func + "] 引数全体:[" + arg + "] 後文字列:[" + str2 + "]"
return
#defcfunc rTrim str _source
source = _source
index = 0
value = 0
while( peek( source , index ) != 0 )
value = peek( source , index )
if value = ' ' or value = '\t' : poke source , index , 0
index++
wend
return source
#global
//----------------------------------------------------------------------------
// モジュール初期化
stackInit 100 // スタック最大100件で初期化
varInit 100 // 変数最大100個で初期化
//----------------------------------------------------------------------------
randomize // 乱数を使うので、とりあえずつけとく
source = "ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
while( 1 )
devideFunc source , str1 , func , arg , str2 // 関数、引数と、それ以前、その後を分割
if func = "" : _break // 関数名が無ければ、関数は無かった
gosub *execute // 関数名別処理呼び出し
source = str1 + result + str2 // 結果を組み立てる
wend
result = compute( source ) // 最後に残った式を処理する
mes compute("ret") // 変数retの中身を見る
stop
*execute
func = rTrim( func ) // 名前の最後に空白があるなら消す
if func = "rnd" { gosub *frnd : return } // rnd関数
if func = "pset" { gosub *fpset : return } // pset関数
return
*frnd
result = rnd( compute( arg ) ) // 引数を計算し、実際にrnd関数に当てはめて結果を取得する
return
*fpset
result = 0 // 複数の引数取り出しが出来ないので取り合えず0にしておく
return
何度も実行すれば、rndで値が変化している事が分るだろう。
| |
|
2011/11/15(Tue) 09:26:42|NO.43021
複数の引数が有るpset関数を処理する。
その為には、引数文字列を、分割しなければならない。
とは言え、これは簡単だ。
「,」を探して、それより前と後ろを分割すればいい。
split命令を使うと簡単だと思うが、将来的に文字列を扱う等するなら、
文字列内に有る「,」にも反応してしまうので、ここは分割処理を作ってしまう。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用スタック内容リスト表示
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化
sdim stack , 64 , _length
return
#global
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value // 変数登録 未登録なら自動登録
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name // 変数内容取得
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#defcfunc varConv str _src , local src // 与えられた文字列を判別して変数内容か、数値化した値を返す
src = _src
if isAlphabet( peek( src , 0 ) ) = 1 : return getVar( src )
return double( src )
#deffunc varList local data , local i // デバグ用変数リスト表示
data = ""
for i,0,vIndex
data += "" + i + ":[" + varName( i ) + "] = " + varValue( i ) + "\n"
next
dialog data
return
#deffunc varInit int _length // 初期化
sdim varName , 64 , _length
ddim varValue , _length
return
#global
//----------------------------------------------------------------------------
#module "rpn"
#defcfunc rpnConv str _src // 数式を逆ポーランド表記に変換する
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + _src + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "+" : case "-" // 足し算、割り算
case "<" : case ">" // より大きい、より小さい
case "<=" : case ">=" // 以上、以下
case "==" : case "!=" // 同じ、同じじゃない
case "&" : case "|" // and 、or
case "^" : case "=" // xor 、代入
if flg = 1 & ( value = "+" | value = "-" ) { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "=" { // =の後に+-が入ったなら、
rpn += "0 " // 手前に0を入れて調整
push value // 引き算として記号はプッシュ
swbreak
} // =の後に+-が入ったなら、手前に0を入れて調整
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 引き算として記号はプッシュ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 // ここに来た記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」 なら終了
if lastStack() = "<" : _break // 「<」 なら終了
if lastStack() = ">" : _break // 「>」 なら終了
if lastStack() = "<=" : _break // 「<=」なら終了
if lastStack() = ">=" : _break // 「>=」なら終了
if lastStack() = "==" : _break // 「==」なら終了
if lastStack() = "!=" : _break // 「!=」なら終了
if lastStack() = "&" : _break // 「&」 なら終了
if lastStack() = "|" : _break // 「|」 なら終了
if lastStack() = "^" : _break // 「^」 なら終了
if lastStack() = "!" : _break // 「!」 なら終了
if lastStack() = "=" : _break // 「=」 なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
swbreak
case "*" : case "/" // 掛け算、割り算
case "!" // not
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" or lastStack() = "!" { // 前に入れた記号が掛け算割り算Not記号なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「)」の次は、記号が入って当然なので0でいい
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return rpn
//----------------------------------------------------------------------------
#defcfunc rpncomp str _src // 逆ポーランド表記の計算式を計算する
rpn = _src
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "<"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 < work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 > work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "<="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 <= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 >= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "=="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 = work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "!="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 != work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "&"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 & work2 ) // 計算してスタックに戻す
swbreak
case "|"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 | work2 ) // 計算してスタックに戻す
swbreak
case "^"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 ^ work2 ) // 計算してスタックに戻す
swbreak
case "!"
work2 = int( varConv( pop() ) ) // Notは1件分にのみ適用
push "" + ( ( 0xffffffff ^ work2 ) ) // 計算してスタックに戻す
swbreak
case "="
work2 = varConv( pop() ) // 代入する値取得
setVar pop() , work2 // 変数名取得して代入
push "1" // 計算では無いが、処理完了的意味で1を返す
swbreak
case "+"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
return varConv( pop() ) // 最後にスタックに残った値が、答え
//----------------------------------------------------------------------------
#defcfunc compute str _src // 計算式を逆ポーランド表記に変換して、それを計算する
return int( rpncomp( rpnconv( _src ) ) )
#global
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#defcfunc getIndexName str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数名の上に居るかどうか
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
index-- // 参照位置を進める(先に進めるので、必要があれば後で戻す)
if fFunc = 1 { // 関数名と思われる文字の上に居る時・・・
if isNumeric ( value ) = 1 : _continue // 数字なら、まだ。
if isAlphabet( value ) = 1 : _continue // 英字なら、まだ。
return index + 2 // ここまで来たら、この1つ前までが関数名の始まり(参照位置を先に進めた分も戻す)
} else {
if value != ' ' : fFunc = 1 // 空白じゃない部分が登場したら、関数名上に辿り着いた。
}
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#defcfunc getIndexClose str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
cNest = 0 // カッコの左右のつじつまカウンタ
while ( peek( source , index ) != 0 ) // 文字列の終わりは必ず0なので、そこまで進む
value = peek( source , index ) // 1キャラクタ取得
if value = '(' : cNest++ // ( なら、一段レベルアップ
if value = ')' : cNest-- // ) なら、一段レベルダウン
if cNest = 0 : return index // ±0になったら、そこが終了位置
index++ // 文字参照位置を進める
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#deffunc devideFunc str _source , var str1 , var func , var arg , var str2
source = _source // 元文字列
srcLen = strlen( source ) // 文字列サイズ
fOpen = getIndexOpen ( source ) // 関数の引数開始「(」取得
if fOpen <= 0 { // 無ければ、関数は無い。
str1 = source
func = ""
arg = ""
str2 = ""
} else {
fName = getIndexName ( source , fOpen - 1 ) // 関数名位地取得
fClose = getIndexClose( source , fOpen ) // 関数の引数終了「)」位地取得
str1 = strmid( source , 0 , fName ) // 関数より手前の文字列取得
func = strmid( source , fName , fOpen - fName ) // 関数名文字列取得
arg = strmid( source , fOpen + 1 , fClose - fOpen - 1 ) // 関数引数取得(カッコヌキ)
str2 = strmid( source , fClose + 1 , srcLen - fClose ) // 関数より後文字列取得
}
mes "-----------------------------------------------------------------------"
mes "元文字列 :[" + source + "]"
mes "手前文字列:[" + str1 + "] 関数名:[" + func + "] 引数全体:[" + arg + "] 後文字列:[" + str2 + "]"
return
#defcfunc rTrim str _source
source = _source
index = 0
value = 0
while( peek( source , index ) != 0 )
value = peek( source , index )
if value = ' ' or value = '\t' : poke source , index , 0
index++
wend
return source
#deffunc divideArg str _source , var arg , var str1
source = _source
index = 0
while( peek( source , index ) != 0 )
if peek( source , index ) = ',' {
arg = strmid( source , 0 , index )
str1 = strmid( source , index + 1 , strlen( source ) - index )
return
}
index++
wend
arg = source
str1 = ""
return
#global
//----------------------------------------------------------------------------
// モジュール初期化
stackInit 100 // スタック最大100件で初期化
varInit 100 // 変数最大100個で初期化
//----------------------------------------------------------------------------
randomize // 乱数を使うので、とりあえずつけとく
source = "ret = pset( rnd( 640 ) , rnd (( 40 + 8 ) * 10 ))"
while( 1 )
devideFunc source , str1 , func , arg , str2 // 関数、引数と、それ以前、その後を分割
if func = "" : _break // 関数名が無ければ、関数は無かった
gosub *execute // 関数名別処理呼び出し
source = str1 + result + str2 // 結果を組み立てる
wend
result = compute( source ) // 最後に残った式を処理する
mes compute("ret") // 変数retの中身を見る
stop
*execute
func = rTrim( func ) // 名前の最後に空白があるなら消す
if func = "rnd" { gosub *frnd : return } // rnd関数
if func = "pset" { gosub *fpset : return } // pset関数
return
*frnd
result = rnd( compute( arg ) ) // 引数を計算し、実際にrnd関数に当てはめて結果を取得する
return
*fpset
divideArg arg , arg1 , arg // 最初の引数を取り出す
divideArg arg , arg2 , arg // 次の引数を取り出す
color 255 , 0 , 0
pset compute( arg1 ) , compute( arg2 ) // 取得した引数を元にpset実行
color 0,0,0
result = 1 // 成功という事で1でも返しておく
return
| |
|
2011/11/15(Tue) 09:28:18|NO.43022
無事にpsetでドットが画面に書き込まれたのが確認できたであろうか?
何?表示が邪魔で分らん?
実は、俺もやってみて機が付いた(ヲ
そこで、boxf関数とか追加する。
これなら分るだろう。
邪魔な表示も消す。
//----------------------------------------------------------------------------
#define global ctype isAlphabet( %1 ) ( ( ( 0x40 < %1 ) & ( %1 < 0x5b ) ) or ( ( 0x60 < %1 ) & ( %1 < 0x7b ) ) )
#define global ctype isNumeric( %1 ) ( 0x30 <= %1 & %1 <= 0x39 )
//----------------------------------------------------------------------------
#module "stack"
#deffunc push str _data // スタックに登録する
stack( sindex ) = _data
sIndex++
return
#defcfunc pop // スタックから取り出す
sIndex--
return stack( sIndex )
#defcfunc isEmpty // スタックが空か確認する
if sIndex = 0 : return 1
return 0
#defcfunc lastStack // 最後に入れたスタックの内容参照
return stack( sIndex - 1 )
#defcfunc stackLevel // 現在のスタック使用サイズを取得
return sIndex
#deffunc stackList local data , local i // デバグ用スタック内容リスト表示
data = ""
for i,0,sIndex
data += "" + i + ":[" + stack( i ) + "]\n"
next
dialog data
return
#deffunc stackInit int _length // 初期化
sdim stack , 64 , _length
return
#global
//----------------------------------------------------------------------------
#module "var"
#deffunc setVar str _name , double _value // 変数登録 未登録なら自動登録
vIndex = -1
foreach varName
if varName( cnt ) = _name {
varName( cnt ) = _name
varValue( cnt ) = _value
return
}
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = _value
return
#defcfunc getVar str _name // 変数内容取得
vIndex = -1
foreach varName
if varName( cnt ) = _name : return varValue( cnt )
if vIndex = -1 & varName( cnt ) = "" : vIndex = cnt
loop
varName( vIndex ) = _name
varValue( vIndex ) = 0.0
return 0.0
#defcfunc varConv str _src , local src // 与えられた文字列を判別して変数内容か、数値化した値を返す
src = _src
if isAlphabet( peek( src , 0 ) ) = 1 : return getVar( src )
return double( src )
#deffunc varList local data , local i // デバグ用変数リスト表示
data = ""
for i,0,vIndex
data += "" + i + ":[" + varName( i ) + "] = " + varValue( i ) + "\n"
next
dialog data
return
#deffunc varInit int _length // 初期化
sdim varName , 64 , _length
ddim varValue , _length
return
#global
//----------------------------------------------------------------------------
#module "rpn"
#defcfunc rpnConv str _src // 数式を逆ポーランド表記に変換する
index = 0 // 文字の参照位置のインデックス
rpn = "" // 変換結果を記録する文字列変数
buf = "" // 一時的に数字を覚えておく文字列変数
f = "(" + _src + ")" // ループ内で処理完了する為の処置
flg = 0 // 直前処理文字が「計算用の記号」と確定出来る時、1になる
while( peek( f , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( f , index , 1 ) // インデックス位置の1文字取得
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( f , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "+" : case "-" // 足し算、割り算
case "<" : case ">" // より大きい、より小さい
case "<=" : case ">=" // 以上、以下
case "==" : case "!=" // 同じ、同じじゃない
case "&" : case "|" // and 、or
case "^" : case "=" // xor 、代入
if flg = 1 & ( value = "+" | value = "-" ) { // 直前の記号が計算記号なのに、足し算引き算記号が来た(プラスマイナス表現かもしれない)
if lastStack() = "=" { // =の後に+-が入ったなら、
rpn += "0 " // 手前に0を入れて調整
push value // 引き算として記号はプッシュ
swbreak
} // =の後に+-が入ったなら、手前に0を入れて調整
if lastStack() = "(" and stackLevel() <= 1 { // 直前の記号が「(」で、スタックレベルが1以下だったら・・・
rpn += "0 " // それは「-(n+...」の表現なので、「0-(n+...」の様に改変する
push value // 引き算として記号はプッシュ
swbreak
} else { // そうでなければ
buf += value // 通常の「±表現の記号」と思われるので、保持
flg = 0 // 数値扱いなので、フラグは0に。
swbreak
}
}
flg = 1 // ここに来た記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "(" : _break // 「(」 なら終了
if lastStack() = "<" : _break // 「<」 なら終了
if lastStack() = ">" : _break // 「>」 なら終了
if lastStack() = "<=" : _break // 「<=」なら終了
if lastStack() = ">=" : _break // 「>=」なら終了
if lastStack() = "==" : _break // 「==」なら終了
if lastStack() = "!=" : _break // 「!=」なら終了
if lastStack() = "&" : _break // 「&」 なら終了
if lastStack() = "|" : _break // 「|」 なら終了
if lastStack() = "^" : _break // 「^」 なら終了
if lastStack() = "!" : _break // 「!」 なら終了
if lastStack() = "=" : _break // 「=」 なら終了
rpn += pop() + " " // そうでないならぶちまける
wend
push value // 記号をスタックに積む
swbreak
case "*" : case "/" // 掛け算、割り算
case "!" // not
flg = 1 // 掛け算割り算の記号は、確実に計算用なので、1
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
if lastStack() = "*" or lastStack() = "/" or lastStack() = "!" { // 前に入れた記号が掛け算割り算Not記号なら・・・
rpn += pop() + " " // ぶちまける
} else {
_break // そうでなければ終了
}
wend
push value // 記号をスタックに積む
swbreak
case "(" // カッコ開く
flg = 1 //「(」記号は、確実に計算用なので、1
if buf != "" : buf += " " // 文字が溜まっていたら、区切りの空白を追加しておく
push value
swbreak
case ")"
flg = 0 //「)」の次は、記号が入って当然なので0でいい
if buf != "" { rpn += buf + " " : buf = "" } // 文字が溜まっていたら、結果に出力、クリア
while( isEmpty() = 0 ) // スタックが空になるまで
data = pop() // スタックからデータを取り
if data = "(" : _break // 「(」なら終了
rpn += data + " " // そうでないならぶちまける
wend
swbreak
case " " // 空白は無視->結果に余分な空白が付かない
swbreak
default
buf += value // 数字が1桁じゃない事も有るので、とりあえず数字を覚えていく
flg = 0 // ここに来たという事は、少なくとも計算用記号ではないので、0
swbreak
swend
index++
wend
return rpn
//----------------------------------------------------------------------------
#defcfunc rpncomp str _src // 逆ポーランド表記の計算式を計算する
rpn = _src
index = 0 // 文字の参照位置のインデックス
ans = 0.0 // 答えを保持する変数。割り算等が有るので、実数で。
buf = "" // 一時的に数字を覚えておく文字列変数
work1 = 0.0 // 計算用変数。実数。
work2 = 0.0 // 計算用変数。実数。
while( peek( rpn , index ) != 0 ) // 文字列の最後は必ず0なので、0まで続ける
value = strmid( rpn , index , 1 ) // インデックス位置の1文字取得
if value = "+" | value = "-" { // +,-の時
if strmid( rpn , index + 1 , 1 ) != " " { // その次が空白で無いなら±表現
buf += value // 数値として記録して
index++ // 次へ
_continue
}
}
if value = "<" | value = ">" { // < か > だった時は、<= か、>=の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // < か > の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
if value = "=" | value = "!" { // = か ! だった時は、== か != の可能性がある
if strmid( rpn , index + 1 , 1 ) = "=" { // = か ! の次の文字が=だったなら
value += "=" // 判定文字列に「=」をつけて
index++ // 文字参照位置を進めておく
}
}
switch value
case "<"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 < work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 > work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "<="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 <= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case ">="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 >= work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "=="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 = work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "!="
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work1 != work2 { push "1" } // 判定が正
else { push "0" } // 判定が否
swbreak
case "&"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 & work2 ) // 計算してスタックに戻す
swbreak
case "|"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 | work2 ) // 計算してスタックに戻す
swbreak
case "^"
work2 = int( varConv( pop() ) ) // 手前の
work1 = int( varConv( pop() ) ) // 2件のスタックを取り出し
push "" + ( work1 ^ work2 ) // 計算してスタックに戻す
swbreak
case "!"
work2 = int( varConv( pop() ) ) // Notは1件分にのみ適用
push "" + ( ( 0xffffffff ^ work2 ) ) // 計算してスタックに戻す
swbreak
case "="
work2 = varConv( pop() ) // 代入する値取得
setVar pop() , work2 // 変数名取得して代入
push "1" // 計算では無いが、処理完了的意味で1を返す
swbreak
case "+"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 + work2 ) // 計算してスタックに戻す
swbreak
case "-"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 - work2 ) // 計算してスタックに戻す
swbreak
case "*"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
push "" + ( work1 * work2 ) // 計算してスタックに戻す
swbreak
case "/"
work2 = varConv( pop() ) // 手前の
work1 = varConv( pop() ) // 2件のスタックを取り出し
if work2 = 0.0 { // 割る側が0なら計算できないのでエラー
dialog "Error : Divide by zero." , 1
return 0.0
}
push "" + ( work1 / work2 ) // 計算してスタックに戻す
swbreak
case " " // 空白は区切り記号として扱う
if buf != "" { push buf : buf = "" }
swbreak
default
buf += value
swbreak
swend
index++
wend
return varConv( pop() ) // 最後にスタックに残った値が、答え
//----------------------------------------------------------------------------
#defcfunc compute str _src // 計算式を逆ポーランド表記に変換して、それを計算する
return int( rpncomp( rpnconv( _src ) ) )
#global
//----------------------------------------------------------------------------
#module "function"
#defcfunc getIndexOpen str _source
source = _source // 元文字列
index = strLen( source ) // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数の始まりの可能性の有る「(」を見つけた際に1
oIndex = 0 // 関数の始まり位置
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
if fFunc = 1 { // 関数可能性フラグが立っていて
if value = ' ' { index-- : _continue } // 空白なら無視
if isAlphabet( value ) = 0 & isNumeric( value ) = 0 { // 英文字でなく、数字でも無いなら、関数可能性が消えました。
fFunc = 0 // 可能性フラグを下げ
oIndex = 0 // 可能性位置をクリア
_continue // この記号こそが関数の引数開始かも知れないのでやりなおし
}
if isAlphabet( value ) = 1 | isNumeric( value ) = 1 : _break // 英文字か数字なら、そこが関数の引数開始位置でしょう。
} else {
if value = '(' { oIndex = index : fFunc = 1 } // 「(」だったら、関数の引数開始かもしれない
}
index-- // 参照位置を進める
wend
return oIndex
#defcfunc getIndexName str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
fFunc = 0 // 関数名の上に居るかどうか
while ( index > 0 ) // 文字参照位置が0より大きい間繰り返す
value = peek( source , index ) // 1キャラクタ取得
index-- // 参照位置を進める(先に進めるので、必要があれば後で戻す)
if fFunc = 1 { // 関数名と思われる文字の上に居る時・・・
if isNumeric ( value ) = 1 : _continue // 数字なら、まだ。
if isAlphabet( value ) = 1 : _continue // 英字なら、まだ。
return index + 2 // ここまで来たら、この1つ前までが関数名の始まり(参照位置を先に進めた分も戻す)
} else {
if value != ' ' : fFunc = 1 // 空白じゃない部分が登場したら、関数名上に辿り着いた。
}
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#defcfunc getIndexClose str _source , int _index
source = _source // 元文字列
index = _index // 文字参照位置
value = 0 // 参照中の文字
cNest = 0 // カッコの左右のつじつまカウンタ
while ( peek( source , index ) != 0 ) // 文字列の終わりは必ず0なので、そこまで進む
value = peek( source , index ) // 1キャラクタ取得
if value = '(' : cNest++ // ( なら、一段レベルアップ
if value = ')' : cNest-- // ) なら、一段レベルダウン
if cNest = 0 : return index // ±0になったら、そこが終了位置
index++ // 文字参照位置を進める
wend
return 0 // 終了位置が見つけられなかったら、とりあえず0でも返す
#deffunc devideFunc str _source , var str1 , var func , var arg , var str2
source = _source // 元文字列
srcLen = strlen( source ) // 文字列サイズ
fOpen = getIndexOpen ( source ) // 関数の引数開始「(」取得
if fOpen <= 0 { // 無ければ、関数は無い。
str1 = source
func = ""
arg = ""
str2 = ""
} else {
fName = getIndexName ( source , fOpen - 1 ) // 関数名位地取得
fClose = getIndexClose( source , fOpen ) // 関数の引数終了「)」位地取得
str1 = strmid( source , 0 , fName ) // 関数より手前の文字列取得
func = strmid( source , fName , fOpen - fName ) // 関数名文字列取得
arg = strmid( source , fOpen + 1 , fClose - fOpen - 1 ) // 関数引数取得(カッコヌキ)
str2 = strmid( source , fClose + 1 , srcLen - fClose ) // 関数より後文字列取得
}
return
#defcfunc rTrim str _source
source = _source
index = 0
value = 0
while( peek( source , index ) != 0 )
value = peek( source , index )
if value = ' ' or value = '\t' : poke source , index , 0
index++
wend
return source
#deffunc divideArg str _source , var arg , var str1
source = _source
index = 0
while( peek( source , index ) != 0 )
if peek( source , index ) = ',' {
arg = strmid( source , 0 , index )
str1 = strmid( source , index + 1 , strlen( source ) - index )
return
}
index++
wend
arg = source
str1 = ""
return
#global
//----------------------------------------------------------------------------
// モジュール初期化
stackInit 100 // スタック最大100件で初期化
varInit 100 // 変数最大100個で初期化
//----------------------------------------------------------------------------
randomize // 乱数を使うので、とりあえずつけとく
source = "ret = boxf( 100 , 100 , 100 + rnd( 440 ) , 100 + rnd (( 20 + 8 ) * 10 ) )"
while( 1 )
devideFunc source , str1 , func , arg , str2 // 関数、引数と、それ以前、その後を分割
if func = "" : _break // 関数名が無ければ、関数は無かった
gosub *execute // 関数名別処理呼び出し
source = str1 + result + str2 // 結果を組み立てる
wend
result = compute( source ) // 最後に残った式を処理する
mes compute("ret") // 変数retの中身を見る
stop
*execute
func = rTrim( func ) // 名前の最後に空白があるなら消す
if func = "rnd" { gosub *frnd : return } // rnd関数
if func = "pset" { gosub *fpset : return } // pset関数
if func = "boxf" { gosub *fboxf : return } // boxf関数
return
*frnd
result = rnd( compute( arg ) ) // 引数を計算し、実際にrnd関数に当てはめて結果を取得する
return
*fpset
divideArg arg , arg1 , arg // 最初の引数を取り出す
divideArg arg , arg2 , arg // 次の引数を取り出す
color 255 , 0 , 0
pset compute( arg1 ) , compute( arg2 ) // 取得した引数を元にpset実行
color 0,0,0
result = 1 // 成功という事で1でも返しておく
return
*fboxf
divideArg arg , arg1 , arg // 最初の引数を取り出す
divideArg arg , arg2 , arg // 次の引数を取り出す
divideArg arg , arg3 , arg // 次の引数を取り出す
divideArg arg , arg4 , arg // 次の引数を取り出す
color 255 , 0 , 0
boxf compute( arg1 ) , compute( arg2 ) , compute( arg3 ) , compute( arg4 ) // 取得した引数を元にpset実行
color 0,0,0
result = 1 // 成功という事で1でも返しておく
return
| |
|
2011/11/15(Tue) 09:30:29|NO.43023
ここまで出来ればもう十分だろう。
後は、使いたい奴が勝手にデバグして改造して自分のプログラムに組み込めばいい。
基本「捨て場に捨てた」ものなので、好きにするがいいさ。
|
|
2011/11/15(Tue) 18:11:56|NO.43025
さすがですなぁ
HSPでもやればここまでできるんだな。
簡単なノベルスゲームにでも使うスクリプトなら処理できそうだ。
ここまで来るともうC++とかを使いたくなってくるけど。
boostのspiritうめえええええええええええ。
これはこれで使い方を覚えるのが面倒だけど。
有志がこいつにいろいろと機能を追加して、
HSPでスクリプトを解析するためのモジュールでも用意してくれると便利なんだけどな。
|
|