Wish Tcl/Tk 入門4. 条件分岐 if, elsif, 配列 |
#!/bin/sh # the next line restarts using wish \ exec wish "$0" "$@" # sentaku.tcl 4.1 # 初期化 ボタンです。 # # 問題文です。message box を使います。 # 小題は 配列 q(i) にいれます。 # set txt " 問題: 次の問で正しいもののボタンを押せ\n" set q(1) " (1) 幼稚園バスのそばを通過するときは停止する必要がある。" set q(2) " (2) 方向指示機が壊れた車を運転してはならない。" set q(3) " (3) 停車を禁止されている箇所は 7 箇所である。" set q(4) " (4) 普通免許で運転できるトラックの最大重量は5噸である。" set q(5) " (5) 電動機付き自転車は道路交通法では軽車両である。" # # 答えの配列をセットします。{ ... } の中は偶数個要素があり、 # 例えば $kotae(1) は n になります。 # array set kotae { 1 n 2 n 3 y 4 y 5 y } # message .mg1 -text $txt -bg white -width 300 -padx 10 -pady 5 # # 問題を checkbutton で作ります。答えは 配列 a(i) にいれます。 # 繰り返し文 for で 5 個の checkbutton を定義します。 # for { set i 1 } { $i <= 5 } { incr i } { checkbutton .c($i) -text $q($i) -variable a($i) \ -onvalue "y" -offvalue "n" } # # 採点ボタンの定義 # button .bt1 -text 採点 -bg yellow -command { set tokuten 0 for { set i 1 } { $i <= 5 } { incr i } { if { $a($i) == $kotae($i) } then { set tokuten [ expr $tokuten + 1 ] } } if { $tokuten <= 2 } { set kouhyou "偶然でも取れる得点です。"} \ elseif { $tokuten <=4 } { set kouhyou "あと一歩の努力が必要。"} \ else { set kouhyou "大変良くできました。"} } # # 得点結果の表示 label # label .lb1 -text あなたの得点は -bg white label .lb2 -textvariable tokuten -width 5 -relief ridge -bg yellow label .lb3 -text 点です。 -bg white label .lb4 -textvariable kouhyou -bg white # # 頭を揃えるように pack するために、-anchor w (west = 左) を指定 # pack .mg1 -anchor c -fill x -expand yes pack .c(1) .c(2) .c(3) .c(4) .c(5) -anchor w -expand yes pack .bt1 .lb1 .lb2 .lb3 .lb4 -anchor e -side left -fill x
この問題を実行してみてください。問題と答えは、それぞれ配 列 q(i), a(i) にいれています。この代入の仕方で、q(i) は set を用いて、また a(i) の 方は array set を 用いています。tcl の配列の引数は、数字でも文字でも構いません。したがって array set では、面倒でも (引数、内容) を組にして、偶数個の list を set する必要があります。
配列の要素を引くときには、$a($i) であることが必要です。 また 変数として利用する -textvariable や -variable の 時には a($i) で a には $ が いりません。$i の変わりに $a(1) と実際 に書く場合には $a(1) で ok なわけです。
checkbutton は、ボタンの先頭に小さなボタンがついて、そこ が押されたかどうかで、variable で指定された変数に onvalue と offvalue が入ります。これによって onvalue に配点を問題別に設 定することも可能です。
for は 繰り返し文です。c 言語や fortran, basic, csh を 少しは知っている人であれば、何をしているか理解できると思いま す。for { set i 1 } { $i <= 5 } { incr i } { command } は i = 1 から 5 まで i を 1 つずつ増やしながら command を実行す る意味です。
得点の大きさによって if 文の分岐が行われます。注意しなけ れば行けないのは、if 文は elseif や else 文が無くても成立し ます。したがって elseif や else 文が続く場合には、文章を継続 する \ がないとそこで if 文が終ったと判断し、その後の文章が 理解できないと エラーを出します。中括弧が開く { があれば、文 章は継続されると判断しますので、これをうまく使えば 継続の \ を使わないでも 処理できます。例えば以下の様に表現出来ます。
if { $a == "1" } { puts "$a は 1 だ。" } elseif { $a != "2" } { puts "$a は 2 ではない。" } else { puts "$a は 2 だ。"}また 最初は気づかないのですが、中括弧 の後には ス ペースが必要です。
誤 if { a >= 1 }{ command } 正 if { a >= 1 } { command } @ ここに スペースが必要です。エラーの意味が分からなくて困りますので、あらかじめ注意します。-anchor は 右寄せ、左寄せ、中央化等に使えます。n e w s (北東西南) の指定で (上右左下) を指示します。c が 中央です。 pack だけでなく、多くの 道具に共通して使える option です。
4.2 住所録 (puts)
(実行例 4.2) 配列の処理を勉強するために住所を表示することを 考えます。ここではじめて標準出力に puts コマンドで 出力しま す。このプログラムを実用にするにはもう少し処理が必要です。 例として見てください。
#!/bin/sh # the next line restarts using wish \ exec wish "$0" "$@" # juushoroku.tcl 4.2 # # 住所録データを入力し、一つの配列変数として標準出力します。 # # 入力ラベル label と 入力 entry # label .lb1 -text 名前 -bg white entry .en1 -textvariable namae -width 20 -relief ridge -bg yellow label .lb2 -text 〒 -bg white entry .en2 -textvariable yuubin -width 7 label .lb3 -text 住所 -bg white entry .en3 -textvariable juusho -width 60 # # 登録ボタンで登録します。touroku の配列を作ります。 # puts は 標準出力に それぞれの 要素をかきます。 # parray は 配列の全ての値を表示させる。 # button .bt1 -text 登録 -command { array set touroku " 名前 $namae 〒 $yuubin 住所 $juusho " puts $touroku(名前) puts $touroku(〒) puts $touroku(住所) puts [ parray touroku ] } # # pack で 表示させます。 # pack .lb1 .en1 -anchor w pack .lb2 .en2 -anchor w pack .lb3 .en3 -anchor w pack .bt1 -anchor c
実行してみましょう。名前と〒住所を入力して、登録ボタンを 押すと Unix であれば juushoroku.tcl を実行した kterm 上に puts の結果が出力されます。puts は敢えていままで 使いません でしたが、標準出力にも結果を出すことができるもっとも標準的な command です。デバッグ等にも良く使います。
ここで注目したいのは、array set で { ... } では なく、 " ... " を使っていることです。こうしないと、変数が正しく受け 渡されません。いずれも文字のならび である list を受け渡すも のですが、結果が異なることを確認してみてください。
文字変数 リストの処理では、perl のコマンドのような split や concat また、string command によっていろいろな文字 変数の処理をすることができます。man でいろいろ調べてください。 文字変数処理は perl の方が便利なので、ここではこれ以上詳しく 説明しません。
4.3 変数リストの処理
tcl/tk を コマンドとして利用するには、コマンドと一緒に option や file 名の引数を渡すことが必要です。この処理は、 csh スクリプトの時のように argc や argv 変数を用います。 ここでは、引数をただ受け取って表示するだけの プログラムを 作ってみましょう。(例題に懲りすぎて少し疲れ気味です。)
#!/bin/sh # the next line restarts using wish \ exec wish "$0" "$@" # arg.tcl 4.3 # # コマンドの引数の数だけただ label に表示します。 # argc = 引数の数です。 # 引数が無い場合には、用法を示します。 if { $argc == 0 } { puts "Usage: arg.tcl -i input.file -o out.file" exit } # # default の 値をいれます。 set inputfile input.dat set outputfile output.dat # # # 引数の数だけ中身を読み判断します。 # lindex list n list の n 番目の要素をだします。 # ここでは -i option と -o option の時だけ処理がなされます。 # for { set i 0 } { $i <= $argc } { incr i } { set param [ lindex $argv $i ] # if { $param == "-i" } then { incr i set inputfile [ lindex $argv $i ] } # if { $param == "-o" } then { incr i set outputfile [ lindex $argv $i ] } } # # 結果を entry の 入力窓にいれます。 # label .lb1 -text 入力ファイル -bg white -fg black entry .e1 -textvariable inputfile -bg white -fg blue label .lb2 -text 出力ファイル -bg white -fg black entry .e2 -textvariable outputfile -bg white -fg blue button .bt1 -text 終了 -bg white -fg black -command exit # # pack ボタン .b1 を詰めて表示する。これがないと表示しない。 # pack .lb1 .e1 .lb2 .e2 -fill both -expand yes pack .bt1
% arg.tcl だけ 入力すると用法がでてきます。
% arg.tcl a b c としても、default の 入力しか設定されま せん。
% arg.tcl -i a.dat -o b.dat で 正しく入力、出力 file に a.dat, b.dat がはいります。% arg.tcl -o b.dat -i a.dat とし ても ok です。
このほか重要な変数として、一般の配列としてリストの数を返 すのは、llength list があります。また lindex の他 list に 加 えたり lappend list value, list の first から last の中身を 入れ換えたり lreplace first last value1 value2 ... などいろ いろあります。ソート lsort list などもあります。言語としての 基本操作のコマンドはいろいろありますから、こんな使い方はでき ないかという目的別にコマンドを探してみると良いです。この 文 書は、コマンド reference になるほど 網羅はできません。l (エ ル) で始まるコマンドですから、参考書の索引の L を見てくださ い。
また list 操作の他に 文字操作 コマンド string があります。 string コマンドでは、文字列の長さや比較などができます。man n string を見てください。例が無いのは申し訳ないですが、私はあ まり使いません。
rsaito@ee.uec.ac.jp