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

コメント