プログラミングの原点である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

コメント