昨日は、低気圧の通過とかで、嵐が吹き荒れていました。 心懸けが悪いせいか、
ナントカ心と秋の空という文句が、ツイ浮かんできます。
さて、Iconミニ講座で、クロスワードパズル支援のプログラムを作りましたが、
見返してみますと、辞書参照のあたりが、非常に分かりにくくなっています。
全体を組み合わせる前の個々の処理は簡単だったのですが、組合せると何故か面倒に
なっています。
ワイルドカード文字生成、分割パターン生成、そのデータから更に分配組合せを生成
するため、generatorが3つあり、everyの3段ループ処理にしています。
個々のループは単純ですが、3段にしたのが面倒になっている原因と思います。
処理を簡単化して構成だけ取り出すと、こんな感じになっています。
-----^ GEN01.ICN ( date:03-10-14 time:21:05 ) --------------<cut here
####################
# generatorの使い方
####################
# gen01.icn Rev.1.1 2003/10/14 風つかい
# Iconミニ講座(おまけ)の decrefp6.icnの辞書参照の every..every..every..の
# 構成を単純にしたプログラム
# This file is in the public domain.
procedure main()
every s1 := test01() do { # test01から順次取り出し "a","b","c"
every s2 := test02() do { # test02から順次取り出し "A","B","C"
every s3 := test03(s1,s2) do { # ↑を引数にして test03から順次取り出し
writes(s3," ") # スペースを夾んで書き出し
}
}
}
write() # カーサ戻し
end
# "a","b","c"を順次生成する generator
procedure test01()
suspend !"abc"
end
# "A","B","C"を順次生成する generator
procedure test02()
suspend !"ABC"
end
# s1とs2の連結 と s2とs1の連結を生成する generator
# 例: test03("x","y") -> "xy","yx"を生成
procedure test03(s1,s2)
suspend (s1 || s2) | (s2 || s1)
end
-----$ GEN01.ICN ( lines:35 words:100 ) --------------------<cut here
簡単化した例を動かすとこうなります。
-----^ GEN01.1 ( date:03-10-14 time:21:05 ) ----------------<cut here
aA Aa aB Ba aC Ca bA Ab bB Bb bC Cb cA Ac cB Bc cC Cc
-----$ GEN01.1 ( lines:1 words:18 ) ------------------------<cut here
Icon教典(The Icon Programming Language)を読み返しながら、色々試してみますと
every..every..every..と3段ループにする必要はなく、1つの everyで、全パターン
を取り出せることが分かりました。(汗)
前の例は、こんな風にできます。だいぶ分かりやすくなった気がします。
3段ループの各々で、一体何についてループさせたんだっけ? と悩まないで済む分、
楽になりました。
-----^ GEN02.ICN ( date:03-10-14 time:20:59 ) --------------<cut here
####################
# generatorの使い方
####################
# gen02.icn Rev.1.1 2003/10/14 風つかい
# gen01.icnの every..every..every をまとめたもの
# This file is in the public domain.
procedure main()
# ↓"a","b","c"を生成
every s3 := test03(test01(),test02()) do writes(s3," ") # スペースを夾んで
# ↑ ↑"A","B","C"を生成 # 書き出し
# ↑引数の連結と逆連結を生成
write()
end
# "a","b","c"を順次生成する generator
procedure test01()
suspend !"abc"
end
# "A","B","C"を順次生成する generator
procedure test02()
suspend !"ABC"
end
# s1とs2の連結 と s2とs1の連結を生成する generator
# 例: test03("x","y") -> "xy","yx"を生成
procedure test03(s1,s2)
suspend (s1 || s2) | (s2 || s1)
end
-----$ GEN02.ICN ( lines:30 words:80 ) ---------------------<cut here
尚、サンプル例ですと、test01(),test02()を設けず、いきなり !"abc"、!"ABC"と
test03()の引数に書くこともできます。
-----^ GEN03.ICN ( date:03-10-14 time:20:58 ) --------------<cut here
####################
# generatorの使い方
####################
# gen03.icn Rev.1.1 2003/10/14 風つかい
# test03の引数を直接記載したもの
# This file is in the public domain.
procedure main()
# ↓"a","b","c"を生成
every s3 := test03(!"abc" ,!"ABC" ) do writes(s3," ") # スペースを夾んで
# ↑ ↑"A","B","C"を生成 # 書き出し
# ↑引数の連結と逆連結を生成
write()
end
# s1とs2の連結 と s2とs1の連結を生成する generator
# 例: test03("x","y") -> "xy","yx"を生成
procedure test03(s1,s2)
suspend (s1 || s2) | (s2 || s1)
end
-----$ GEN03.ICN ( lines:20 words:64 ) ---------------------<cut here
という訳で、Iconミニ講座(おまけ)のプログラムを書き換えると、次のように
なります。 ちょっと長いですが、折角ですので、掲載しておきます。
-----^ DICREF2.ICN ( date:03-10-14 time:21:11 ) ------------<cut here
####################
# クロスワードパズル支援
####################
# dicref2.icn Rev.1.2 2003/10/14 風つかい
####################
# クロスワードパズル支援 dicrefp6.icnを見直し。
# This file is in the public domain.
link file_e, # f_name: 実行ファイル名取得
string_e, # mscomb: 英文字列重複組合せ expcomb: 英文字列分配組合せ
# lright: 右寄せ桁揃え(長い場合はそのまま)
number_e, # ndivdf: 正整数の部分和(降順 分割数・最小制限)
struct_e, # tbl_ref: list要素で table参照
strings # (Icon基本ライブラリ BIPLに含まれる)
# deletec: 英文字列から文字を削除 csort: 英文字列のソート
procedure main(Larg)
####################
# コマンドライン引数チェック。無ければ Usage表示
####################
Usage := " 英単語(.はワイルドカード) 最大分割数 最短文字長"
if *Larg < 1 then stop(f_name(),Usage)
####################
# コマンドライン引数から、分割パターンを生成
####################
c_word := Larg[1] # コマンドラインの英文字列
# ↓左辺の式が失敗すれば、右辺の式を選ぶ
ndiv := \Larg[2] | 1 # 分割数指定無しは、1(分割せず)
nmin := \Larg[3] | *c_word # 最小文字長指定無しは、引数文字列長
L_lpat := [] # 文字列分割パターン格納 list
every put(L_lpat,n_divdf(*c_word,ndiv,nmin)) # 分割パターン格納
# L_lpat例:[[6],[3,3]]
####################
# 辞書読込
####################
# 必要な文字数の生成( setに入れて、ダブリを削除)
S_pat := set() # 文字数格納 set(指定文字数ため)
every insert(S_pat,!!L_lpat) # 文字数を全て 格納 (Rev.1.2)
# ↑2重リストから全ての要素を取り出す
# S_pat例: (6,3)
T_dic := table() # 辞書格納 table生成
every n_pat := !sort(S_pat) do { # 単語長 setを sortして、順に取り出し
# 辞書ファイル名生成 ↑ setは sortすると listになる
# ↓rightは、右寄せ桁合わせ関数
fname := "e" || right(n_pat,2,"0") # 辞書ファイル名 例:"e06","e03"
read_dic(fname,T_dic) # 辞書読込 (Rev.1.1 別 procedureに)
} # end of every n_pat ..
####################
# 辞書参照
####################
n_comb := 0 # 組合せ数カウンタ
n_find := 0 # 辞書にある件数カウンタ
s_word := map(deletec(c_word,'.')) # コマンドライン文字列から '.'を削除し
# 小文字化
n_dot := *c_word -*s_word # '.'の数 = ワイルドカードの文字数
write("\n",c_word," を、最大分割数 ",ndiv,"、最小文字長 ",nmin,
" にて分割してチェック。開始:",&clock)
# 文字列分割結果を 順次取り出して 辞書参照
# ↓組合せ分配
every Lword := expcomb((s_wild := mscomb(&lcase,n_dot)) || s_word,
!L_lpat) do { # ↑ワイルドカード作成 例: a-z,aa-zz
# ↑例: [6]とか [3,3]とか # Lword例: ["sunday"]とか,["day","sun"]とか
# every L_word := .. で、mscombの全ての組合せ、L_lpatの全ての要素、expcomb
# の全ての組合せについて、Lwordの値が生成される。 各々 every every everyと
# 重ねる必要はない。 (Rev.1.1)
n_comb +:= 1 # チェック回数カウンタ+1
if n_comb % 1000 = 1 then writes(&errout,"*") # チェック状況表示
# 辞書参照して、分配結果が全て辞書にあれば、L_loutにセット
# ↓いずれかが辞書になければ、&nullでクリア。
L_lout := tbl_ref(T_dic,Lword) | &null
# L_lout例: [["Day","day"],["Sun","sun","nus"]]
# 分配文字列が全て辞書にあれば、書き出し
if \L_lout then { # L_outに値が &nullでなければ
n_find +:= 1 # 合致カウンタ+1
writes(&errout,"!") # 合致表示
# "."を除く元の文字列書き出し
writes(s_word)
if *s_wild >= 1 then writes(" + ",s_wild) # ワイルドカード文字
writes(" ->")
# 辞書検索結果(2重 list)の書き出し
every Lout := L_lout[i := 1 to *L_lout] do { # 参照結果を取り出して
if i > 1 then writes(" +") # 先頭でなければ、区切りマーク
every writes(" ",!Lout) # 参照結果を書き出し
} # end of Lout ..
write()
} # end of if \L_out ..
} # end of every Lword ..
write(&errout)
write(n_comb," 通りの組合せのうち、",n_find," 通りが辞書にありました。",
" 終了:",&clock)
end
####################
# 英単語スペルチェック辞書読込
####################
# 英単語スペルチェック辞書を読み込んで、インデックスを付けて、テーブルに格納
# arg [1]: string 読込ファイル名
# [2]: table テーブル
# value :(none)
# read_dic Rev.1.1 2003/10/13 風つかい mainから分離。
procedure read_dic(fname,T_dic)
# 辞書ファイルオープン
dir_in := open(fname) | stop(fname," が見つかりません。")
writes(fname," を読込中です。開始:",&clock)
# 辞書読込
# 単語を1つずつ読み込んで、keyを生成し、T_dicにセット
n_line := 0 # 辞書行数カウンタ
while word := read(dir_in) do {
n_line +:= 1 # 辞書行数+1
if n_line % 1000 = 1 then writes(&errout,"*") # 読み込み状況表示
# 単語を小文字変換しソートしたものを keyにして格納。
# 同一文字を含む単語は同じ keyに対応する valueに listの形式で格納。
# tableの key生成
# ↓文字列ソート
s_word := csort(map(word)) # 小文字へ変換しソート
# ↑小文字変換 例: "ACb" -> "abc"
# 辞書 tableへ格納
put(\T_dic[s_word],word) | (T_dic[s_word] := [word])
# 格納例: key:"abc" -> value: ["ACb","Bca","cab"]
} # end of while word ..
close(dir_in) # 辞書ファイルクローズ
write(&errout)
write(" 終了:",&clock," ",lright(n_line,5),"語ありました。")
return # Iconでは構造体はメモリー節約のため call元と
# 同じものを使う。return T_dicとしなくてよい。
end
# 履歴
# dicref2.icn Rev.1.2 2003/10/14 風つかい 文字数ふるいの everyを1つに
# dicref2.icn Rev.1.1 2003/10/13 風つかい 構成変更のため名前変更
# 多段ループがあって、プログラムの見通しが悪いので、修正。
# 辞書読込の別 procedure化。 辞書参照の generatorの everyを1つに
# dicref.icn Rev.1.3.1 2003/10/07 風つかい
-----$ DICREF2.ICN ( lines:149 words:528 ) -----------------<cut here
Iconは、色々と面白い機能がある言語ですので、お試し頂けるとうれしいです。
風つかい(hshinoh@...)
IconのWWWは、 http://www.cs.arizona.edu/icon/
UniconのWWWは、http://unicon.sourceforge.net/index.html
BGM: Amazing Grace / KANKAWA & 杉本喜代志