プログラミングの原点であるBASICを触ってみます。
Contents
BASIC入門
まずFreeBASICをこちらからダウンロードします。
BASIC インストール
解凍します。
root@ubu16:~# ls FreeBASIC-1.05.0-linux-x86_64.tar.gz root@ubu16:~# tar xzf FreeBASIC-1.05.0-linux-x86_64.tar.gz root@ubu16:~# ls FreeBASIC-1.05.0-linux-x86_64 FreeBASIC-1.05.0-linux-x86_64.tar.gz root@ubu16:~# root@ubu16:~# cd FreeBASIC-1.05.0-linux-x86_64/ root@ubu16:~/FreeBASIC-1.05.0-linux-x86_64# ls bin changelog.txt doc examples include install.sh lib readme.txt root@ubu16:~/FreeBASIC-1.05.0-linux-x86_64#
インストールします。
root@ubu16:~/FreeBASIC-1.05.0-linux-x86_64# ./install.sh -i
適当にプログラムを書いてコンパイルして実行してみます。
takk@ubu16:~$ mkdir basictest takk@ubu16:~$ cd !$ takk@ubu16:~/basictest$ vi test.bas takk@ubu16:~/basictest$ cat test.bas print "HELLO" takk@ubu16:~/basictest$ takk@ubu16:~/basictest$ fbc test.bas takk@ubu16:~/basictest$ ls test test.bas takk@ubu16:~/basictest$ ./test HELLO takk@ubu16:~/basictest$
fbcでFreeBASICのコンパイルができます。
ん、でもおかしいです。私の知っているBASICは、行番号がありました。
行番号をつけてコンパイルしてみます。
takk@ubu16:~/basictest$ cat test.bas 10 print "HELLO" takk@ubu16:~/basictest$ fbc test.bas test.bas(1) error 145: Only valid in -lang deprecated or fblite or qb in '10 print "HELLO"' takk@ubu16:~/basictest$
エラーになりました。メッセージから推測すると-langオプションでqb(quick basic)を指定すれば通るということでしょうか。
takk@ubu16:~/basictest$ fbc -lang qb test.bas takk@ubu16:~/basictest$
通りました。
ソースコードにマジックコメントを使って直接埋め込むこともできます。
takk@ubu16:~/basictest$ cat test.bas '$lang: "qb" 10 print "HELLO" takk@ubu16:~/basictest$ fbc -lang qb test.bas takk@ubu16:~/basictest$
BASIC RNDとINKEY$
RNDとINKEY$を使ってみます。
偶数と奇数、どちらが半だったか丁だったかすぐ忘れてしまうのですが、丁度2で割れるから偶数と覚えてしまいましょう。
test.bas
takk@ubu16:~/basictest$ cat test.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 money = 1 40 while 1 50 die1 = INT(RND * 6) + 1 60 die2 = INT(RND * 6) + 1 70 dice = die1 + die2 80 PRINT "" 90 PRINT "半か丁か (半=y 丁=n) 所持金は" & money & "円です" 100 PRINT "" 110 do 120 let k$ = INKEY$ 130 loop until k$ <> "" 140 PRINT "賽の目の合計は、" & dice & "でした" 150 hancho = dice mod 2 160 if k$ = "y" and hancho = 0 then 170 PRINT "あたりです" 180 money = money * 2 190 elseif k$ = "n" and hancho = 1 then 200 PRINT "あたりです" 210 money = money * 2 220 else 230 PRINT "はずれです" 240 money = money * 0 250 end if 260 PRINT "" 270 wend 280 takk@ubu16:~/basictest$
実行するには、fbcでビルドして、testファイルを実行すればよいです。
takk@ubu16:~/basictest$ fbc test.bas takk@ubu16:~/basictest$ ./test
RANDOMIZEしてないので、記憶ゲームになっています。このゲームで100万円ぐらい稼げるでしょうか。
BASIC MID$
子供の頃に夢中になったBASICの感覚を取り戻すため、ダンジョンゲームを作っていこうかと思います。
まずはダンジョンの表示。MID$を使って文字列を抽出してマップを表示します。
takk@ubu16:~/basictest$ cat test.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 40 dim map$(30) 50 map$(1) = "###################################" 60 map$(2) = "###################################" 70 map$(3) = "###################################" 80 map$(4) = "#######S# # ## ############" 90 map$(5) = "####### ### #### ## ## # #######" 100 map$(6) = "####### # # ## #G## #######" 110 map$(7) = "####### # #### ### ### #### #######" 120 map$(8) = "####### # # #######" 130 map$(9) = "###################################" 140 map$(10) = "###################################" 150 map$(11) = "###################################" 160 170 x = 8 180 y = 4 190 200 210 220 money = 1 230 while 1 240 cls 250 for yy = y-3 to y+3 260 print mid$(map$(yy),x-7,15) 270 next 280 290 locate 4,8 300 print "*" 310 320 do 330 let k$ = INKEY$ 340 loop until k$ <> "" 350 360 select case k$ 370 case "j" 380 y = y + 1 390 case "k" 400 y = y - 1 410 case "h" 420 x = x - 1 430 case "l" 440 x = x + 1 450 end select 460 wend 470 takk@ubu16:~/basictest$
実行するとこのような画面が表示されます。*がプレイヤーです。#は壁です。判定してないので、今は壁を通り抜けることができます。Vimmerなので移動はjkhlです。
BASIC IF
壁の判定を行う前に、マップ内の移動区間を定めたいと思います。
select case k$ case "j" ck = y + 1 if ck <= 8 then y = ck case "k" ck = y - 1 if ck >= 4 then y = ck case "h" ck = x - 1 if ck >= 8 then x = ck case "l" ck = x + 1 if ck <= 28 then x = ck end select
次は、壁(#)の判定です。
次に移動する場所に#があったら、移動しないようにする修正にしました。
select case k$ case "j" ck = y + 1 if mid$(map$(ck),x,1) <> "#" and ck <= 8 then y = ck case "k" ck = y - 1 if mid$(map$(ck),x,1) <> "#" and ck >= 4 then y = ck case "h" ck = x - 1 if mid$(map$(y),ck,1) <> "#" and ck >= 8 then x = ck case "l" ck = x + 1 if mid$(map$(y),ck,1) <> "#" and ck <= 28 then x = ck end select
BASIC COLOR
コンソールに表示する文字色と文字色を変えてみます。
color命令で数字を指定すれば、数字に割り当てられている色が設定できます。
書式) COLOR 文字色,背景色
takk@ubu16:~/basictest$ cat color.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 cls 40 50 for i = 0 to 15 60 y = int(i / 8) + 1 70 x = (i mod 8) * 2 + 1 80 locate y,x 90 color i,8 100 print "" & i 110 next takk@ubu16:~/basictest$
では作成中のゲームのマップに色を付けてみましょう。
takk@ubu16:~/basictest$ cat test.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 40 dim map$(30) 50 ' 1 2 3 60 ' 12345678901234567890123456789012345 70 map$(1) = "###################################" 80 map$(2) = "###################################" 90 map$(3) = "###################################" 100 map$(4) = "#######S# # ## ############" 110 map$(5) = "####### ### #### ## ## # #######" 120 map$(6) = "####### # # ## #G## #######" 130 map$(7) = "####### # #### ### ### #### #######" 140 map$(8) = "####### # # #######" 150 map$(9) = "###################################" 160 map$(10) = "###################################" 170 map$(11) = "###################################" 180 190 x = 8 200 y = 4 210 220 while 1 230 cls 240 250 color 2,1 'green on blue 260 for yy = y-3 to y+3 270 print mid$(map$(yy),x-7,15) 280 next 290 300 color 5 'red 310 locate 4,8 320 print "*" 330 340 color ,0 'bgcolor=black 350 360 do 370 let k$ = INKEY$ 380 loop until k$ <> "" 390 400 select case k$ 410 case "j" 420 ck = y + 1 430 if mid$(map$(ck),x,1) <> "#" and ck <= 8 then y = ck 440 case "k" 450 ck = y - 1 460 if mid$(map$(ck),x,1) <> "#" and ck >= 4 then y = ck 470 case "h" 480 ck = x - 1 490 if mid$(map$(y),ck,1) <> "#" and ck >= 8 then x = ck 500 case "l" 510 ck = x + 1 520 if mid$(map$(y),ck,1) <> "#" and ck <= 28 then x = ck 530 end select 540 wend 550 takk@ubu16:~/basictest$
BASIC GOSUB
GOSUB命令を使って、ダンジョンにモンスターを出現させます。
書式) GOSUB 行番号またはラベル名
行番号は修正する度に変わるので、ラベル名にします。monsterというラベルにしました。
monster: monster_exists = INT(RND * 10) if monster_exists = 1 then locate 9,1 print "モンスターが現れた" end if return
移動してると時々モンスターが現れるようになりました。
モンスターが現れたという表示だけではつまらないので、ヒットポイントを作って、モンスターからダメージを受けるように修正します。
ヒットポイントの変数名はhpにしました。
hp = 100
サブルーチンの方はヒットポイントを10引く処理を追加します。
monster: monster_exists = INT(RND * 10) if monster_exists = 1 then locate 9,1 print "モンスターが現れた" hp = hp - 10 end if return
あと、ヒットポイントが表示されていないと、ダメージを受けていることが分からないので、もう一つサブルーチンを追加します。
status_disp: locate 2,20 print "HP: " & hp return
全ソースです。
takk@ubu16:~/basictest$ cat test.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 hp = 100 40 50 dim map$(30) 60 ' 1 2 3 70 ' 12345678901234567890123456789012345 80 map$(1) = "###################################" 90 map$(2) = "###################################" 100 map$(3) = "###################################" 110 map$(4) = "#######S# # ## ############" 120 map$(5) = "####### ### #### ## ## # #######" 130 map$(6) = "####### # # ## #G## #######" 140 map$(7) = "####### # #### ### ### #### #######" 150 map$(8) = "####### # # #######" 160 map$(9) = "###################################" 170 map$(10) = "###################################" 180 map$(11) = "###################################" 190 200 x = 8 210 y = 4 220 230 while 1 240 cls 250 260 color 2,1 'green on blue 270 for yy = y-3 to y+3 280 print mid$(map$(yy),x-7,15) 290 next 300 310 color 5 'red 320 locate 4,8 330 print "*" 340 350 color 7,0 'white on black 360 370 gosub monster 380 gosub status_disp 390 400 do 410 let k$ = INKEY$ 420 loop until k$ <> "" 430 440 select case k$ 450 case "j" 460 ck = y + 1 470 if mid$(map$(ck),x,1) <> "#" and ck <= 8 then y = ck 480 case "k" 490 ck = y - 1 500 if mid$(map$(ck),x,1) <> "#" and ck >= 4 then y = ck 510 case "h" 520 ck = x - 1 530 if mid$(map$(y),ck,1) <> "#" and ck >= 8 then x = ck 540 case "l" 550 ck = x + 1 560 if mid$(map$(y),ck,1) <> "#" and ck <= 28 then x = ck 570 end select 580 590 wend 600 610 status_disp: 620 locate 2,20 630 print "HP: " & hp 640 return 650 660 monster: 670 monster_exists = INT(RND * 10) 680 690 if monster_exists = 1 then 700 locate 9,1 710 print "モンスターが現れた" 720 hp = hp - 10 730 end if 740 750 return 760 takk@ubu16:~/basictest$
モンスターに遭遇すると、ヒットポイントが減ります。
BASIC 変数の入れ替え
流行りの言語、例えばPythonでは、変数の入れ替えは、一文でできてしまいます。
a,b = b,a
Pythonさん、なんてエレガントに書けてしまうのでしょう。C言語を軸に育ってしまった私としては、驚きの構文です。
使ってみましょう。
takk@deb9:~/tmp$ cat -n t.py 1 a = 10 2 b = 20 3 4 print "a =",a, ",b =",b 5 6 a,b = b,a 7 8 print "a =",a, ",b =",b 9 takk@deb9:~/tmp$
結果です。
takk@deb9:~/tmp$ python t.py a = 10 ,b = 20 a = 20 ,b = 10 takk@deb9:~/tmp$
素敵ですね。Python空とんでます。
ついでにPerl。私はPerl派なのでこれが一番すっきりしているように思います。Pythonも捨てがたいですが。
takk@deb9:~/tmp$ cat -n t.pl 1 $a = 10; 2 $b = 20; 3 4 print "a = $a , b = $b\n"; 5 6 ($a,$b) = ($b,$a); 7 8 print "a = $a , b = $b\n"; 9 takk@deb9:~/tmp$
結果はPythonと同じです。もちろんRubyでも同じことができますね。省略します。
やはり軽く処理を書きたいなら、Perl/Python/Ruby世代の言語が良いのでしょうか。
酒を飲みながら趣味でプログラミングするベテランの友人は、頑としてBASICしか使わないそうです。まあ、わからない気もないです。この頑固友人のような人たちは軽くすっきり書くプログラミングの面白さを味わうことはできないのでしょうか。
いえいえ、BASICでも変数の入れ替えが1命令でできてしまいます。その名もSWAP命令。
SWAP 変数A, 変数B
このように使います。
takk@deb9:~/tmp$ cat swap.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 a = 10 40 b = 20 50 60 print "A=";A;", B =";B 70 80 swap a,b 90 100 print "A=";A;", B =";B takk@deb9:~/tmp$
はい、すっきりしてますね。
結果です。
takk@deb9:~/tmp$ fbc swap.bas takk@deb9:~/tmp$ ./swap A= 10, B = 20 A= 20, B = 10 takk@deb9:~/tmp$
BASIC OPTIONBASE
OPTION BASEとは、配列の添字の既定の下限を宣言する命令です。
本物のQuick Basicでは0か1しか指定できませんでしたが、FreeBASIC(-lang qb)では、01以外も指定できます。
では使ってみます。OPTION BASEを1にして、わざと0番目に入れてみます。きっとコンパイルエラーになるのでしょうね。
takk@deb9:~/tmp$ cat optionbase.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 option base 1 40 dim array(3) 50 60 array(0) = 10 70 array(1) = 100 80 90 for i = 0 to 3 100 print "array(";i;")=";array(i) 110 next 120 takk@deb9:~/tmp$ fbc optionbase.bas
エラーでません。なぜに! 実行はどうでしょうか。
takk@deb9:~/tmp$ ./optionbase array( 0)= 10 array( 1)= 100 array( 2)= 0 array( 3)= 0 takk@deb9:~/tmp$
代入されています。何故でしょう。
もしかしたら、確保した領域を0で初期化するだけなのかもしれません。
takk@deb9:~/tmp$ cat optionbase.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 option base 3 40 dim array(3) 50 60 70 for i = 0 to 3 80 print "array(";i;")=";array(i) 90 next 100 takk@deb9:~/tmp$ fbc optionbase.bas takk@deb9:~/tmp$ ./optionbase array( 0)= 0 array( 1)= 5.901103e-39 array( 2)= 0 array( 3)= 0 takk@deb9:~/tmp$
なるほど、0以外の値が見えます。もっとたくさんの領域で確認してみます。
OPTION BASE 50 と0のプログラムを作ります。
takk@deb9:~/tmp$ cat optionbase50.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 option base 50 40 dim array(50) 50 60 for i = 0 to 50 70 print "array(";i;")=";array(i) 80 next 90 takk@deb9:~/tmp$ fbc optionbase50.bas takk@deb9:~/tmp$ ./optionbase50 > op50
takk@deb9:~/tmp$ cat optionbase0.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 option base 0 40 dim array(50) 50 60 for i = 0 to 50 70 print "array(";i;")=";array(i) 80 next 90 takk@deb9:~/tmp$ fbc optionbase0.bas takk@deb9:~/tmp$ ./optionbase0 > op0
両者を比べてみましょう。
takk@deb9:~/tmp$ diff -y -W40 op0 op50 array( 0)= 0 array( 0)= 0 array( 1)= 0 array( 1)= 0 array( 2)= 0 | array( 2)= 5.889 array( 3)= 0 array( 3)= 0 array( 4)= 0 array( 4)= 0 array( 5)= 0 array( 5)= 0 array( 6)= 0 | array( 6)= 5.898 array( 7)= 0 array( 7)= 0 array( 8)= 0 | array( 8)= 8.845 array( 9)= 0 array( 9)= 0 array( 10)= 0 | array( 10)= 5.89 array( 11)= 0 array( 11)= 0 array( 12)= 0 | array( 12)= 2.24 array( 13)= 0 array( 13)= 0 array( 14)= 0 | array( 14)= 8.84 array( 15)= 0 array( 15)= 0 array( 16)= 0 | array( 16)=-1.33 array( 17)= 0 | array( 17)= 4.59 array( 18)= 0 | array( 18)= 5.88 array( 19)= 0 array( 19)= 0 array( 20)= 0 array( 20)= 0 array( 21)= 0 array( 21)= 0 array( 22)= 0 | array( 22)= 5.88 array( 23)= 0 array( 23)= 0 array( 24)= 0 | array( 24)=-1.33 array( 25)= 0 | array( 25)= 4.59 array( 26)= 0 | array( 26)= 26 array( 27)= 0 | array( 27)= 1.40 array( 28)= 0 array( 28)= 0 array( 29)= 0 array( 29)= 0 array( 30)= 0 array( 30)= 0 array( 31)= 0 | array( 31)= 31 array( 32)= 0 | array( 32)=-1.33 array( 33)= 0 | array( 33)= 4.59 array( 34)= 0 | array( 34)=-1.33 array( 35)= 0 | array( 35)= 4.59 array( 36)= 0 | array( 36)= 5.60 array( 37)= 0 array( 37)= 0 array( 38)= 0 | array( 38)= 5.60 array( 39)= 0 array( 39)= 0 array( 40)= 0 | array( 40)= 1.40 array( 41)= 0 array( 41)= 0 array( 42)= 0 | array( 42)= 1.40 array( 43)= 0 array( 43)= 0 array( 44)= 0 | array( 44)= 7.00 array( 45)= 0 array( 45)= 0 array( 46)= 0 | array( 46)= 7.00 array( 47)= 0 array( 47)= 0 array( 48)= 0 | array( 48)= 5.90 array( 49)= 0 array( 49)= 0 array( 50)= 0 array( 50)= 0 takk@deb9:~/tmp$
やはり思った通りです。OPTION BASEはおそらく領域確保して、初期化する位置だけの指定でしょう。代入できてしまうので、注意が必要です。
BASIC SPC
スクリプト言語では、同じ文字が連続する文字列をこのように簡単に生成できます。
takk@deb9:~/tmp$ perl -e '$a="A"x5; print "$a\n"' AAAAA takk@deb9:~/tmp$
今回は、空白の生成をします。
空白を10文字生成したい場合、Perl/Python/Rubyでは、こんな感じでしょうか。
takk@deb9:~/tmp$ head space.* ==> space.pl <== print "AAA" . " "x10 . "AAA\n"; ==> space.py <== print "AAA" , " "*10 , "AAA" ==> space.rb <== print "AAA" , " "*10 , "AAA\n" takk@deb9:~/tmp$
これをBASICでやるには、SPC命令です。
takk@deb9:~/tmp$ cat space.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 print "AAA";SPC(10);"AAA" 40 takk@deb9:~/tmp$
実行結果です。
takk@deb9:~/tmp$ fbc space.bas takk@deb9:~/tmp$ ./space AAA AAA takk@deb9:~/tmp$
SPACE$(数字)と命令も同じように使えます。BASICは子供の頃に夢中になって、ベーマガの打ち込みしたり、自分で考えてプログラミングしたつもりですが、こんな命令があったことも忘れてました。
BASIC DATE$/TIME$/VAL/STR$
時間計算です。 DATEとTIMEを使ってみましょう。
takk@deb9:~/tmp$ cat date.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 print date$ 40 print time$ takk@deb9:~/tmp$
実行結果はこうです。
takk@deb9:~/tmp$ ./date 09-05-2017 23:15:39 takk@deb9:~/tmp$
時分秒を個別で取得したい場合は、TIMEで返る文字列の一部をMID$を使って抽出します。
takk@deb9:~/tmp$ cat time.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 time_str$ = time$ 40 50 print time_str$ 60 70 hour$ = mid$(time_str$,1,2) 80 min$ = mid$(time_str$,4,2) 90 sec$ = mid$(time_str$,7,2) 100 110 print hour$ 120 print min$ 130 print sec$ 140 takk@deb9:~/tmp$
実行結果です。
takk@deb9:~/tmp$ ./time 23:32:39 23 32 39 takk@deb9:~/tmp$
ただこれでは文字列を取得してるだけですので計算ができません。
文字列を数字にするには、VAL()を使います。数字を文字列に戻すには、STR$()を使います。
takk@deb9:~/tmp$ cat val.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 sec$ = mid$(time$,7,2) 40 50 print sec$ 60 70 sec_val = val(sec$) 80 90 sec_val = sec_val + 100 100 110 sec$ = str$(sec_val) 120 130 print sec$
実行結果です。
takk@deb9:~/tmp$ fbc val.bas takk@deb9:~/tmp$ ./val 29 129 takk@deb9:~/tmp$
29に100が加算されて129が表示されました。
129の前にスペースがありますが、負の数の時、-が入ります。
takk@deb9:~/tmp$ cat minus.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 print str$(-200) 40 print str$(200) takk@deb9:~/tmp$ fbc minus.bas takk@deb9:~/tmp$ ./minus -200 200 takk@deb9:~/tmp$
では、時間を数値に直して、計算してみます。
こんなプログラムになりました。
takk@deb9:~/tmp$ cat time-calc.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 time_str$ = time$ 40 50 print time_str$ 60 70 hour$ = mid$(time_str$,1,2) 80 min$ = mid$(time_str$,4,2) 90 sec$ = mid$(time_str$,7,2) 100 110 timeval = val(hour$)*3600 + val(min$)*60 + val(sec$) 120 130 timeval = timeval + 60*5 140 150 hour$ = str$(int(timeval / 3600)) 160 min$ = str$(int((timeval mod 3600) / 60)) 170 sec$ = str$(timeval mod 3600 mod 60) 180 190 print hour$;":";min$;":";sec$ takk@deb9:~/tmp$
結果です。
takk@deb9:~/tmp$ ./time-calc 19:12:17 19: 17: 17 takk@deb9:~/tmp$
BASIC READ/DATA
DATAを使うと楽に定数定義できます。
takk@deb9:~/tmp$ cat read-data1.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 data 500 40 50 read a 60 70 print a takk@deb9:~/tmp$
,(カンマ)で区切って一行に複数の値を定義できます。
takk@deb9:~/tmp$ cat read-data2.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 for i = 1 to 10 40 read a 50 print a 60 next 70 80 data 10,20,30,40,50 90 data 60,70,80,90,100 takk@deb9:~/tmp$
READで複数の変数に格納するには、変数を,(カンマ)で区切っ指定します。
takk@deb9:~/tmp$ cat read-data3.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 for i = 1 to 5 40 read a,b 50 print a,b 60 next 70 80 data 10,20,30,40,50 90 data 60,70,80,90,100 takk@deb9:~/tmp$
結果です。
takk@deb9:~/tmp$ ./read-data3 10 20 30 40 50 60 70 80 90 100 takk@deb9:~/tmp$
BASIC RESTORE
RESTOREを使います。DATAで定義された値は、順番に読み込みされますが、定義がない場合はどうなるでしょうか。
takk@deb9:~/tmp$ cat restore1.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 data 500,600,700 40 50 read a 60 print a 70 80 read a 90 print a 100 110 read a 120 print a 130 140 read a 150 print a takk@deb9:~/tmp$
このように定義がない場合、0が読み込まれます。
takk@deb9:~/tmp$ ./restore1 500 600 700 0 takk@deb9:~/tmp$
読み込みをDATAの先頭に戻すには、RESTOREを使います。
takk@deb9:~/tmp$ cat restore2.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 data 500,600,700 40 50 read a 60 print a 70 80 read a 90 print a 100 110 read a 120 print a 130 140 restore 150 160 read a 170 print a takk@deb9:~/tmp$
DATAの先頭に戻って500が読み込めました。
takk@deb9:~/tmp$ fbc restore2.bas takk@deb9:~/tmp$ ./restore2 500 600 700 500 takk@deb9:~/tmp$
BASIC OPEN/INPUT/CLOSE
ファイル読み込みをします。
ファイルを開き、読み込み、閉じる。ファイルを扱うのはこの手順ですね。
サンプルソースです。
takk@deb9:~/tmp$ cat open-input-close.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 open "sample.txt" for input access read as #1 40 50 do until eof(1) 60 line input #1, a$ 70 print a$ 80 loop 90 close #1 takk@deb9:~/tmp$
openで#番号に、ファイルを割り付けて、input #番号でファイルを読み込みます。
EOFを見つけるまで繰り返し読むため、do~loopを使っています。
読み込むデータを作成。
takk@deb9:~/tmp$ seq 100 | pr -t5J | tee sample.txt 1 21 41 61 81 2 22 42 62 82 3 23 43 63 83 4 24 44 64 84 5 25 45 65 85 6 26 46 66 86 7 27 47 67 87 8 28 48 68 88 9 29 49 69 89 10 30 50 70 90 11 31 51 71 91 12 32 52 72 92 13 33 53 73 93 14 34 54 74 94 15 35 55 75 95 16 36 56 76 96 17 37 57 77 97 18 38 58 78 98 19 39 59 79 99 20 40 60 80 100 takk@deb9:~/tmp$
では、ビルドして、ファイルを読み込んでみましょう。
処理は、読み込んでそのまま表示するだけです。
takk@deb9:~/tmp$ fbc open-input-close.bas takk@deb9:~/tmp$ ./open-input-close 1 21 41 61 81 2 22 42 62 82 3 23 43 63 83 4 24 44 64 84 5 25 45 65 85 6 26 46 66 86 7 27 47 67 87 8 28 48 68 88 9 29 49 69 89 10 30 50 70 90 11 31 51 71 91 12 32 52 72 92 13 33 53 73 93 14 34 54 74 94 15 35 55 75 95 16 36 56 76 96 17 37 57 77 97 18 38 58 78 98 19 39 59 79 99 20 40 60 80 100 takk@deb9:~/tmp$
ファイルと結果を比較してみます。
takk@deb9:~/tmp$ !! | diff - sample.txt ./open-input-close | diff - sample.txt takk@deb9:~/tmp$
何も表示されてませんので、一致しましたね。
BASIC OPEN/WRITE/CLOSE
読み込みの次は書き込みです。
サンプルはファイルコピーをするプログラムです。sample.txtをout.txtへコピーします。
cat open-write-close.bas | nl -ba -i10 -v10 takk@deb9:~/tmp$ cat open-write-close.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 open "sample.txt" for input as #1 40 open "out.txt" for output as #2 50 60 do until eof(1) 70 line input #1, a$ 80 write #2,a$ 90 loop 100 110 close #1 120 close #2 130 takk@deb9:~/tmp$
結果を見てみましょう。
takk@deb9:~/tmp$ fbc open-write-close.bas takk@deb9:~/tmp$ ./open-write-close takk@deb9:~/tmp$ ls open-write-close open-write-close.bas out.txt sample.txt takk@deb9:~/tmp$ ls -l 合計 64 -rwxr-xr-x 1 takk takk 52624 9月 18 20:47 open-write-close -rw-r--r-- 1 takk takk 156 9月 18 20:36 open-write-close.bas -rw-r--r-- 1 takk takk 332 9月 18 20:47 out.txt -rw-r--r-- 1 takk takk 292 9月 18 20:05 sample.txt takk@deb9:~/tmp$
あれ、sample.txtとout.txtのサイズが違います。
diffします。
takk@deb9:~/tmp$ diff *.txt 1,20c1,20 < "1 21 41 61 81" < "2 22 42 62 82" < "3 23 43 63 83" < "4 24 44 64 84" < "5 25 45 65 85" < "6 26 46 66 86" < "7 27 47 67 87" < "8 28 48 68 88" < "9 29 49 69 89" < "10 30 50 70 90" < "11 31 51 71 91" < "12 32 52 72 92" < "13 33 53 73 93" < "14 34 54 74 94" < "15 35 55 75 95" < "16 36 56 76 96" < "17 37 57 77 97" < "18 38 58 78 98" < "19 39 59 79 99" < "20 40 60 80 100" --- > 1 21 41 61 81 > 2 22 42 62 82 > 3 23 43 63 83 > 4 24 44 64 84 > 5 25 45 65 85 > 6 26 46 66 86 > 7 27 47 67 87 > 8 28 48 68 88 > 9 29 49 69 89 > 10 30 50 70 90 > 11 31 51 71 91 > 12 32 52 72 92 > 13 33 53 73 93 > 14 34 54 74 94 > 15 35 55 75 95 > 16 36 56 76 96 > 17 37 57 77 97 > 18 38 58 78 98 > 19 39 59 79 99 > 20 40 60 80 100 takk@deb9:~/tmp$
書き込み時に勝手に””が付与されていますね。実はwriteを使うと、区切り文字とクォートが自動で付与されるようになっています。変数を用意して試してみましょう。
cat write.bas | nl -ba -i10 -v10 takk@deb9:~/tmp$ cat write.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 a$ = "HELLO1" 40 b$ = "HELLO1" 50 c$ = "HELLO3" 60 70 open "out0.txt" for output as #1 80 90 write #1,a$,b$,c$ 100 110 close #1 120 takk@deb9:~/tmp$
以下が結果です。なんとCSV形式ですね。
takk@deb9:~/tmp$ fbc write.bas takk@deb9:~/tmp$ ./write takk@deb9:~/tmp$ cat out0.txt "HELLO1","HELLO1","HELLO3" takk@deb9:~/tmp$
ではwriteをprintに変えてもう一度、ファイルコピーをやってみましょう。
cat open-print-close.bas | nl -ba -i10 -v10 takk@deb9:~/tmp$ cat open-print-close.bas | nl -ba -i10 -v10 10 '$lang: "qb" 20 30 open "sample.txt" for input as #1 40 open "out2.txt" for output as #2 50 60 do until eof(1) 70 line input #1, a$ 80 print #2,a$ 90 loop 100 110 close #1 120 close #2 130 takk@deb9:~/tmp$
結果です。out2とsample.txtが同じサイズです。今度は一致しました。
takk@deb9:~/tmp$ fbc open-print-close.bas takk@deb9:~/tmp$ ./open-print-close takk@deb9:~/tmp$ ls -l *.txt -rw-r--r-- 1 takk takk 332 9月 18 20:47 out.txt -rw-r--r-- 1 takk takk 292 9月 18 20:51 out2.txt -rw-r--r-- 1 takk takk 292 9月 18 20:05 sample.txt takk@deb9:~/tmp$ diff out2.txt sample.txt takk@deb9:~/tmp$
BASIC 配列の要素数
近代の言語では、配列の要素は配列の変数さえ分かればすぐに取得できますね。
takk@deb9:~$ perl -e '@arr=1..10;print scalar(@arr) ."\n"' 10 takk@deb9:~$ perl -e '$arr[31]=10;print scalar(@arr) ."\n"' 32 takk@deb9:~$
BASICではどうしていたのでしょう。
$lang: "qb" dim a(50) print lbound(a) print ubound(a) size = ubound(a) - lbound(a) + 1 print "elements = ";size
このようにbound(境界)を求める関数を使ってました。lboundはlower boundで配列添え字の最小値が取得でき、uboundは、upper boundで最大値が取得できます。
実行結果です。
0 50 elements = 51
要素数は、最大50-最小0+1の、51になります。
OPTION BASEを変えてみましょう。
$lang: "qb" option base 10 dim b(20) print lbound(b) print ubound(b) size = ubound(b) - lbound(b) + 1 print "elements = ";size
最小が10に底上げされたので、要素数は11です。
10 20 elements = 11
二次元配列の場合は残念なことに、次元数をbound関数に指定せねばなりません。
$lang: "qb" option base 0 dim c(7,9) print lbound(c,1) print ubound(c,1) size = ubound(c,1) - lbound(c,1) + 1 print "elements = ";size print lbound(c,2) print ubound(c,2) size = ubound(c,2) - lbound(c,2) + 1 print "elements = ";size
0 7 elements = 8 0 9 elements = 10
三次元になっても指定が面倒なのは同じです。
$lang: "qb" option base 3 dim d(3,3,3) print lbound(d,3) print ubound(d,3) size = ubound(d,3) - lbound(d,3) + 1 print "elements = ";size
3 3 elements = 1
コメント