Tclコマンド編

 Tclの目玉「リスト」
 前節の説明のように、文字列の操作に関して強力なコマンドがありますが、Tcl では一歩進んだ処理を実現するために、「リスト」という考えを取り入れています。
 といっても、これも前述したようにTcl は型を持ちませんから、リストも別に特別な型ではありません。普通の文字列を「空白またはタブで要素に区切られた」ものとして扱うためのコマンドが実装されているにすぎません。
 それでは、以下の3つのプロシジャを順に実行してみてください。
 
proc list_test1 {} {
  global wk_list;
  catch {unset wk_list};
  for {set cnt 1} {$cnt <= 100000} {incr cnt} {
    lappend wk_list [format %05.5d $cnt];
  }
}
proc list_test2 {} {
  global wk_list;
  for {set cnt 0} {$cnt < 100000} {incr cnt 6} {
    set wk [string range $wk_list $cnt [expr $cnt+4]];
  }
}
proc list_test3 {} {
  global wk_list;
  foreach i $wk_list {
    set wk $i;
  }
}

list_test1;
time list_test2;
time list_test3;
 プロシジャ list_test1 で、10000要素のリストを作成します。list_test2 と list_test3 のプロシジャは、その文字列から順に要素を取り出してローカル変数に設定しています。低スペックのPCであれば、要素数をすこし減らしたほうが良いかもしれません。
 2つのプロシジャの実行スピードの違いがわかってもらえたと思います。ちなみにテスト環境では、3〜4.5倍程度の違いがありました。
 リストと連想配列を組み合わせることによって、インタプリタとは思えないような、高速かつ高度なデータ処理を行うことができます。
 では、リスト処理を体感するため、以下のスクリプトを実行してみてください。画面が表示されたら、入力域に文字列を入れてPushボタンをクリックしてみてください。
 
proc list_test4 {} {
  catch {destroy .top1};
  toplevel .top1;
  label .top1.l1 -text "適当な文字列:";
  label .top1.l2 -text "区切り文字(1バイト):";
  label .top1.l3 -text "リスト化された文字列:split ";
  label .top1.l4 -text "要素数:llength ";
  label .top1.l5 -text "2個目の要素:lindex ";
  label .top1.l6 -text "検索する要素のパターン文字列:";
  label .top1.l7 -text "要素番号をパターン検索:lsearch ";
  label .top1.l8 -text "2番目に要素「abc XYZ」を挿入:linsert ";
  label .top1.l9 -text "2番目の要素を「abc」に入れ替え:lreplace ";
  label .top1.la -text "要素をソーティング:lsort ";
  label .top1.lb -text "2〜4番目の要素を取り出す:lrange ";
  label .top1.lc -text "リストを文字列「@=@」で結合:join ";
  entry .top1.e1 -width 40;
  entry .top1.e2 -width 2;
  label .top1.l31 -relief ridge -an w -background yellow;
  label .top1.l41 -relief ridge -an w -background yellow;
  label .top1.l51 -relief ridge -an w -background yellow;
  entry .top1.e6 -width 5;
  label .top1.l71 -relief ridge -an w -background yellow;
  label .top1.l81 -relief ridge -an w -background yellow;
  label .top1.l91 -relief ridge -an w -background yellow;
  label .top1.la1 -relief ridge -an w -background yellow;
  label .top1.lb1 -relief ridge -an w -background yellow;
  label .top1.lc1 -relief ridge -an w -background yellow;
  button .top1.b1 -text Push -command {list_test5};
  grid .top1.l1 -column 1 -row 1 -sticky e;
  grid .top1.l2 -column 1 -row 2 -sticky e;
  grid .top1.l3 -column 1 -row 3 -sticky e;
  grid .top1.l4 -column 1 -row 4 -sticky e;
  grid .top1.l5 -column 1 -row 5 -sticky e;
  grid .top1.l6 -column 1 -row 6 -sticky e;
  grid .top1.l7 -column 1 -row 7 -sticky e;
  grid .top1.l8 -column 1 -row 8 -sticky e;
  grid .top1.l9 -column 1 -row 9 -sticky e;
  grid .top1.la -column 1 -row 10 -sticky e;
  grid .top1.lb -column 1 -row 11 -sticky e;
  grid .top1.lc -column 1 -row 12 -sticky e;
  grid .top1.e1 -column 2 -row 1 -sticky w;
  grid .top1.e2 -column 2 -row 2 -sticky w;
  grid .top1.l31 -column 2 -row 3 -sticky we;
  grid .top1.l41 -column 2 -row 4 -sticky we;
  grid .top1.l51 -column 2 -row 5 -sticky we;
  grid .top1.e6 -column 2 -row 6 -sticky w;
  grid .top1.l71 -column 2 -row 7 -sticky we;
  grid .top1.l81 -column 2 -row 8 -sticky we;
  grid .top1.l91 -column 2 -row 9 -sticky we;
  grid .top1.la1 -column 2 -row 10 -sticky we;
  grid .top1.lb1 -column 2 -row 11 -sticky we;
  grid .top1.lc1 -column 2 -row 12 -sticky we;
  grid .top1.b1 -column 1 -columnspan 2 -row 13 -sticky we;
}
proc list_test5 {} {
  set org [.top1.e1 get];
  if { [string length [set sep [.top1.e2 get]]] == 0 } {
    set sep " ";
  }
  .top1.l31 conf -text [split [.top1.e1 get] $sep];
  .top1.l41 conf -text [llength [split [.top1.e1 get] $sep]];
  .top1.l51 conf -text [lindex [split [.top1.e1 get] $sep] 2];
  .top1.l71 conf -text [lsearch [split [.top1.e1 get] $sep] [.top1.e6 get]];
  .top1.l81 conf -text [linsert [split [.top1.e1 get] $sep] 2 "abc XYZ"];
  catch {.top1.l91 conf -text [lreplace [split [.top1.e1 get] $sep] 2 2 abc];}
  .top1.la1 conf -text [lsort [split [.top1.e1 get] $sep]];
  .top1.lb1 conf -text [lrange [split [.top1.e1 get] $sep] 2 4];
  .top1.lc1 conf -text [join [split [.top1.e1 get] $sep] @=@];
}
list_test4;

 

  list リストを作成する。
list ?arg arg ...?
?arg arg ...? 要素 arg からなるリストを返す。arg 自体がリストでも可。その場合、個々のリストは保持される。
例) >set elm "1 2"
>list a b $elm c
a b {1 2} c
「1 2」という文字列であり、1と2からなるリストでもある。
2番目の要素はリストの入れ子になっている
  llength リストの要素数を返す。
llength list
list 要素数を求めるリスト。
例) >llength {1 2 {3 4} 5}
4
2番目の要素は「3 4」で1つの要素。
  lindex リストから指定要素を取り出す。
lindex list index
list 対象リスト。
index 0から始まる正の整数。ただし、最後は「end」でも可。
また、負の整数や範囲外の場合は、空文字を返す。
例) >lindex [list 0 1 {a b} 3] 2
a b
 
  lappend リスト要素のアペンド
lappend varName ?value value value ...?
varName 要素を追加する変数名。存在しなくても可。
?value value value ...? 追加する要素。
例) >set lst
can't read "lst": no such variable
>lappend lst a b
a b
>lappend lst {c d} e
a b {c d} e
この時点では「lst」は存在しない


存在の有無に関わらずアペンドできる。
また、set lst "$lst {c d} e"とするより高速。
  lsearch リストから要素をパターン検索して要素番号を返す。
lsearch ?mode? list pattern
?mode? 検索モード。以下の3通り。
-exact  :完全一致
-glob   :ワイルドカード検索。デフォルト
-regexp  :正規表現検索
list 検索対象リスト。
pattern パターン。
例) >set lst {a abc ab abab}
>lsearch $lst a*
0
>lsearch -exact $lst ab
2
>lsearch -regexp $lst (ab)..
3

「a」で始まる要素

「ab」に完全一致する要素

「ab」2回以上の繰り返し。
  linsert リストへの要素追加。
linsert list index element ?element element ...?
list 追加対象のリスト。
index 追加位置番号。最後に追加する場合は「end」も可。
また、範囲外の場合最初または最後に追加される。
element 追加する要素。
例) >set lst {0 1 2 3}
>linsert $lst 2 abc
0 1 abc 2 3
>linsert $lst end {a b} z
0 1 2 3 {a b} z
リストを作成。
2番目に挿入。

最後に挿入。ただし、最後に挿入する場合は、lappendの方が高速
  lreplace リストの部分集合を交換する。
lreplace list first last ?element element ...?
list 対象リスト。
first 部分集合の最初の要素番号。「end」も可。
last 部分集合の最後の要素番号。同じでも可。
?element element ...? 交換する部分集合。指定しない場合、上記の部分集合が削除される。
例) >set lst {0 1 2 3}
>lreplace $lst 1 2 a b {c d}
0 a b {c d} 3
>lreplace $lst 2 2
0 1 3
リストを作成。
要素番号1〜2の代わりに3要素からなるリストで置換する。
要素番号2の要素を削除する。
  lsort リストをソーティングする。
lsort ?options? list
?options? ソーティングオプション。下記等。
-ascii:アスキー昇順にソート。デフォルト。
-integer:整数として昇順にソート。
-command command:ソート順をcommandで記述する。
list ソーティング対象のリスト。
例) >set lst {21 11 8 3}
>lsort $lst
11 21 3 8
>lsort -decreasing $lst
8 3 21 11
>lsort -integer $lst
3 8 11 21
>proc cmp {a b} {
set aa 0
foreach i [split $a {}] {
catch {incr aa $i}
}
set bb 0
foreach i [split $b {}] {
catch {incr bb $i}
}
return [string compare $aa $bb]
}
>lsort -command cmp $lst
11 21 3 8
リストを作成。
普通にソートする。

降順にソートする。

整数値としてソートする。

比較処理を作成する。
2つの整数の桁ごとの合計値を
比較している。
その場合、11は、1+1=2となり、
一桁の3より小さくなる。






上記の方法でソートする。
  lrange リストの部分集合を取り出す。
lrange list first last
list 対象リスト。
first 部分集合の最初の要素番号。「end」も可。
last 部分集合の最後の要素番号。同じでも可。
例) >set lst {0 1 2 3}
>lrange $lst 1 2
1 2
リストを作成。
要素番号1から2の部分リストを取り出す。
  split 指定文字で文字列をTCLリストに分割する。
split string ?splitChars?
string リスト化する文字列。
?splitChars? 分割文字。基本的に1文字であるが、2文字でも分割は可能。
その場合それぞれの文字で分割される。
例) >split "comp.unix.misc" .
comp unix misc
>split "Hello World" {}
H e l l o {} W o r l d
>split "リスト化文字列" 化
リスト 文字列
>split "comp.unix.misc" .u
comp {} nix misc
ピリオド(.)で分割する。

空文字で分割する。
1文字づつに分割される。
日本語も可。

「.」と「u」それぞれで分割される。
1個目の要素が空文字であることに注意。
  join Tclのリストを指定文字列で結合した文字列を返す。
join list ?joinString?
list 対象リスト。
?joinString? 要素を結合する文字列。
例) >set lst {0 1 2 3}
>join $lst +
0+1+2+3
>puts "\"[join $lst \",\"]\""
"0","1","2","3"
リストを作成。
「+」で結合。

「","」で結合した外側を「"」で囲む。
CSV形式の文字列になる。