Wish Tcl/Tk 入門

Wish Tcl/Tk 入門

4. 条件分岐 if, elsif, 配列

条件分岐にどの言語にもあるのが、if 文です。また変数の集まり として配列があります。tcl/tk での表現を具体例でみましょう。

4.1 選択問題の作成 (if, array, for, checkbutton)

(実行例 4.1) 自動車免許の ○×問題を作ってみました。問題の答 えが間違っているかも知れません。それは勘弁ください。
#!/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