作者: Hiroshi Shinohara
日時: 2003/10/14(21:59)
  昨日は、低気圧の通過とかで、嵐が吹き荒れていました。  心懸けが悪いせいか、
ナントカ心と秋の空という文句が、ツイ浮かんできます。

  さて、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 & 杉本喜代志