Icon雑記帳


                                                   Rev.02 1997/05/03 風つかい
                                                   Rev.01 1997/05/03 風つかい
■  Icon > Icon雑記帳  はじめに                      風つかい       目次

  Iconは AWKや Perlの仲間のテキスト処理言語です。
  Iconは データ構造が豊富で、テキスト解析に 威力を発揮する 強力な制御構造
を持っています。

              もっと^2 使われてよい言語と思います。

  しかし、未だ 日本語の入門書が ありませんので、 AWKについて、ある程度の
知識を お持ちになっている方を 対象として、入門講座を書きました。

   ・テキスト解析言語Icon入門講座 ---AWKerのための Icon入門---
   ・Icon日記 ---テキスト解析言語Icon入門講座2---

  この Icon雑記帳は その続編として、その後 作ったプログラムや 前の 講座で
書き落とした 部分の 解説を、まとめたものです。

  前の 2編の講座と いっしょに ご覧になって Iconを使って頂けると うれしい
です。

  Iconのプログラムおよびライブラリーは、次のところから入手できます。
            http://www.cs.arizona.edu/icon/index.html

  Iconは PDSですので、この入門講座も同じ扱いとします。(転載・編集自由)
               (This textbook is in the public domain.)

目次
         はじめに
  (1) jsplit.icn(1)
  (2) jsplit.icn(2)
  (3) jsplit.icn(3)
  (4) generatorの使い方(1)
  (5) generatorの使い方(2)
  (6) generatorの作り方(1)
  (7) generatorの作り方(2)
  (8) generatorのまとめ
  (9) IYAX.ICN
(10) 順列
(11) 組み合わせ
(12) pipe風のプログラム
(13) ビデオ問題は、道遠し
(14) ビデオ問題/組み合わせ
(15) ビデオ問題/簡易解法
(16) ビデオ問題/難しい
(17) 構造体 tree表示(1)
(18) jimage.icn
(19) 構造体 tree表示(2)
(20) dir2tree(1)
(21) dir2tree(2)
(22) dir2tree(3)
         むすび

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: La Fiesta / Chick Corea
(icon_224.txt 1997/05/03 PCVAN PIG)

■  Icon > Icon雑記帳(1) jsplit.icn(1)         風つかい        目次

  Icon日記 に引き続き、Icon雑記帳 というタイトルで、Icon関連の お話しをし
ていきたい と思います。 Iconの話題が多いか 雑記帳の方が多いかは、ちと心配
なところも ありますが。

  ところで、HB彗星ですが3月下旬になると、夕方の6時〜7時頃に北西の空
に見えるということで、期待していたのですが、余り見えません。
  6時頃は未だ太陽が沈みきっていないのと、7時頃は見易いはずなのですが、
結構 雲が出てたり、天気が良くなかったりして 見えないことが多いです。結局
下旬になって まだ1度しか見ていません。

  さて今日はシフトJIS関連の procedureの続きで、split.icnのシフトJIS
対応をやってみたいと思います。 split.icnは文字列をある文字(セット)で、
区切って listに格納するものです。  space区切りや tab区切りのデータ処理に
使います。  で、まず、当初の split.icnの復習 を。

  文字列から 単語を切り出して 書き出す プログラムです。 切り出した 単語を
listにして 上位プログラムに渡す やり方と generatorで 上位に渡す やり方 の
2種類のやり方 が 入っていますが、結果は 同じです。
-----^ SPLIT4.ICN ( date:97-03-29 time:17:04 ) -------------<cut here
# 文字列からのデータの切り出しのテスト
# This file is in the public domain.
# Rev.01 1997/03/29 windy
# list を使う。
procedure split(line,c)
# stringを c(cset)で分割し、listへ変換する procedure
# Icon入門講座(7)Iconの特徴(5) スキャン 参照
  /c := ' \t'              # 引数に cが省略されていたらスペースかタブとみなす
                           # / は nullかどうかのチェック。nullならば...。
  /line := &subject        # lineが省略されていれば、 &subject
  list := []               # 戻り値用リストを生成
  line ? {                 # line を走査対象とする。
    while tab(upto(~c)) do     # c以外の文字が見つかったら走査位置をそこ迄移動
      put(list,tab(many(~c)))  # c以外の文字以外(すなわち c)が見つかる位置迄
  }                            # 移動し、移動した位置の間の文字列をlistへ追加
  return list                  # 結果(list)を返す。
end

# generator を使う。
# stringを c(cset)で分割し出力する generator
# Icon日記(9)suspend/generator 参照
procedure getword(c,s)
# function : split a string with c
  /s := &subject                   # 指定が無ければ &subject
  /c := ' \t'                      # 指定が無ければスペースかタブとみなす。
  s ? {
    while tab(upto(~c)) do {
      word := tab(many(~c))
      suspend word                 # generatorの場合は suspendで結果を返す。
    }
  }
end

# test data
procedure main()
  s1 := " The Icon Programming Language"
  s2 := "あたり が 前 なら 、 近所 は 隣 。"   # 単語の間にスペースを
                                               # はさんであります。
  write("文字列からの単語の取り出しテスト")
  write("split.icnでやると")
  write("data:\"",s1,"\"")
  n := 0
  s1 ? {                           # s1を検索対象文字列とする。
    every s := !split() do {       # sの単語を listに格納したものから
                                   # 1つずつ取り出す。
      write(n +:= 1,":",s)         # 番号をつけて単語を出力。
    }
  }
  write("\ndata:\"",s2,"\"")
  n := 0
  s2 ? every s := !split() do write(n +:= 1,":",s)  # s1の所もこう書けます。
  # ↓↑番号(n +:=1のところ)をつけないなら、下の表現でOKです。
  #s2 ? every write(!split())

#  write("\ngetword.icnでやると")                # split.icnと結果は同じ。
#  write("data:\"",s1,"\"")
#  n := 0
#  s1 ? every s := getword() do write(n +:= 1,":",s)
#  write("\ndata:\"",s2,"\"")
#  n := 0
#  s2 ? every s := getword() do write(n +:= 1,":",s)
end
-----$ SPLIT4.ICN ( lines:62 words:240 ) -------------------<cut here

  動かした結果はこんな風になります。
-----^ SPLIT4.TXT ( date:97-03-29 time:17:04 ) -------------<cut here
文字列からの単語の取り出しテスト
split.icnでやると
data:" The Icon Programming Language"
1:The
2:Icon
3:Programming
4:Language

data:"あたり が 前 なら 、 近所 は 隣 。"
1:あたり
2:が
3:前
4:なら
5:、
6:近所
7:は
8:隣
9:。
-----$ SPLIT4.TXT ( lines:18 words:29 ) --------------------<cut here

  ここで、英文ならば 単語の間が スペースで 区切られています ので、単語の
切り出し が 容易なのです。  日本語では、単語の間に スペースを入れる 習慣
はありません ので 切り出しがやっかいです。
  サンプルの 日本文には、単語の間に スペースを入れましたので、切り出せる
のですが、実際の 日本文では 難しいですね。

  英文では、単語の区切りが " "で、更に ","が使えますが、  日本文では "、"
だけで 区切らないと いけません。  Icon日記でも、文章を書いていて、 "、"を
使うと 文章が 細切れになりすぎる ところでは、つい 区切りのために スペース
を 入れたく なりました。

  で、英文と 同じように 単語の切り出し に 使うのは 無理ですが、特定の文字
で、文字列を 切り分ける プログラムを、作ってみよう と思います。

  また1バイト文字なら 全文字数は 256ですが、日本語は2バイトで 表わし
ます。 256x256通りで、65536文字。  実際は 1バイト文字が 混在
しますので、それよりは 少なくなります が、それにしても 256文字の感覚で
は 処理は できない と思います。

  で〜。  文字数が2桁は多い つもりで 使わないと、いくらCPU能力が あっ
ても 実用的 ではないかもしれません。  まあ、漢字チェックに 全漢字のセット
と照合したり しなければ、大丈夫のような気もします。

  前の split4.icnで、~c(cset c以外)という表現の所は、csetは1バイト文字
全体の集合 ですので 高々256個で、使い物 に なりますが、2バイト文字 で
65536個 を 相手では、まずいだろうと思います。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: Europe/Carlos Santana
(icon_201.txt 1997/03/30 PCVAN PIG)

■  Icon > Icon雑記帳(2) jsplit.icn(2)         風つかい         目次

  今日は、暖かい晴れの天気で、桜も見頃となっています。月に群雲 花に風
の 例えの通り、強風が 吹いて来ました。 春の嵐 ですね〜。

  さて、今回は、jsplit.icnの続きです。 split.icnは、upto()と many()で
構成されています。 これを シフトJIS対応に した jupto()と jmany()に
すればいいのですが、もう一つ問題があります。

  ~cという 表現を 使って います。  しかし 前回 お話し しました ように
2バイト文字で 補集合(指定文字セット以外 の文字)を 使うと 膨大な 文
字数 の 照合 が 必要となります ので、jupto_not()と jmany_not() という
補集合を 使わない procedureを 作ろう と 思います。

  という訳で、次に 補集合を 使わないため procedureです。
-----^ SJIS5.ICN ( date:97-03-30 time:18:11 ) --------------<cut here
# SJIS関係 procedure その5
# jsplit.icn用 procedure
# Icon日記(20)〜(22)シフトJIS処理 参照
# Rev.01 1997/03/30 windy
# This file is in the public domain.

procedure jupto_not(S,s,i1,i2)
# 文字列 sの中の  集合 Sに含まれない文字の位置を生成する generator
# value: 文字の先頭位置
# ☆の部分を、juptoと 判定条件を 逆に してあります。
# Rev.01 1997/03/30 windy
  /i1 := 1                               # default
  if /s then {                           # default
    s  := &subject
    i1 := &pos
  }
  /i2 := *s +1                           # default

  every L := jpos(s,i1,i2) do {          # 文字を 順次 取り出す。
    if not member(S,s[L[1]:L[2]]) then { # ☆文字が Sに含まれないテスト
      suspend L[1]
    }
  }
end

# 文字列 sの中 &pos以降で、集合 Sに含まれる文字の位置を生成する
# value: 文字の先頭位置
# ☆の部分を、jmanyと 判定条件を 逆に してあります。
# Rev.01 1997/03/30 windy
procedure jmany_not(S,s,i1,i2)
  /i1 := 1                               # default
  if /s then {                           # default
    s  := &subject
    i1 := &pos
  }
  /i2 := *s +1                           # default

  if i1 = i2 then return i2              # 行末手当
  every L := jpos(s,i1,i2) do {          # 順次取り出す。
    if member(S,s[L[1]:L[2]])            # ☆文字が Sに含まれるテスト
    then {
      return L[1]
    }
    else {
      if L[2] = i2 then {                # 行末なら含まれない文字とみなす。
        return L[2]
      }
    }
  }
  fail
end


# SJISコードの文字列の文字の先頭位置を発生する generator
# value: list [1]:先頭位置,[2]:次の文字の先頭
# Rev.03 1997/03/02 windy  範囲末手当抜け対策
# Rev.02 1997/03/02 windy  範囲指定(i1,i2)追加
# Rev.01 1997/02/27 windy, Rev.03 1997/03/02 windy
procedure jpos(s,i1,i2)
#  write("jpos:",s,":",i1,":",i2)
  mode := 1                              # 1バイト目(/2バイト目)モード
  every i := i1 to (i2 -1) do {          #
    n := ord(s[i])
    case mode of {
            # SJISコードの1バイト目チェック(16rは16進数を示す。)
      1 :   if {16r80 < n < 16rA0} | {16rDF < n < 16rFD}
            then {                       # 2バイトコード
              if i = i2 -1 then {        # 範囲末手当
                suspend [i,i+2] 
              }
              else  mode := 2            # ←2バイト目モードへ
            }
            else suspend [i,i+1]         # 1バイトコード
      2 : { # 2バイトコードの2バイト目
            mode := 1                    # モードを1バイト目チェックへ
            suspend [i-1,i+1]            # 2バイトコード
          }
    }
  }
end


# test driver
#procedure main()
#  S := set(L := ["d","え","e","お","f","か"]) # 検索で捜す文字のセット
#  s := "abcdefあいうえおかabcあいう"          # 検索対象文字列
#  write("検索対象↓")
#  write(s)
#  writes("捜す文字")
#  n := 0
#  every ss := !L do writes(" ",n +:= 1,":",ss)
#  write()
#  write("every write(jupto_not(S\,s\,3\,22))")
#  every write(jupto_not(S,s,3,22))            # jupto_not(S,s,i1,i2)
#  write("write(jmany_not(S\,s\,9\,22))")
#  write(jmany_not(S,s,9,22))                  # jmany_not(S,s,i1,i2)
#  write("write(jmany_not(S\,s\,26))")
#  write(jmany_not(S,s,26))                    # jmany_notの行末手当
#
#  write("Sに含まれる文字以外の切り出し")
#  s ? {                                       # jupto_notとjmany_not
#    while tab(jupto_not(S)) do {
#      write(tab(jmany_not(S)))
#    }
#  }
#end
-----$ SJIS5.ICN ( lines:106 words:370 ) -------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: トライアングル・ブルー/アン・ルイス
(icon_202.txt 1997/03/30 PCVAN PIG)

■  Icon > Icon雑記帳(3) jsplit.icn(3)         風つかい             目次

  今回も jsplit.icnの続きです。
  シフトJIS対応の split.icnプログラムです。 linkしている sjis5は 前回
の プログラムです。
-----^ SPLIT5.ICN ( date:97-03-30 time:18:38 ) -------------<cut here
# 文字列からのデータの切り出しのテスト
# This file is in the public domain.
# Rev.01 1997/03/30 windy
link sjis5
# list を使う。
procedure jsplit(S,s)
# split.icnと引数の順を入れ替え
# stringを 文字列のセットS で 分割し、listへ変換する procedure
  /S := set([" ","\t"])    # 引数に Sが省略されていたら、spaceか tab
  /s := &subject           # sが 省略されていれば、 &subject
  list := []               # 戻り値用リストを生成
  s ? {                    # s を走査対象とする。
    while tab(jupto_not(S)) do     # S以外の文字が見つかったら走査位置を
                                   # そこ迄移動
      put(list,tab(jmany_not(S)))  # S以外の文字以外(すなわち S)が見つ
                                   # かる位置迄 移動し、移動した位置の間
  }                                # の文字列をlistへ追加
  return list              # 結果(list)を返す。
end

# generator を使う。
# stringを S(set)で分割し出力する generator
procedure jgetword(S,s)
# function : split a string with S
  /s := &subject                   # 指定が無ければ &subject
  /S := set([" ","\t"])            # 指定が無ければ spaceか tab
  s ? {
    while tab(jupto_not(S)) do {
      word := tab(jmany_not(S))
      suspend word                 # generatorの場合は suspendで結果を返す。
    }
  }
end

# test data
procedure main()
  s1 := " The Icon Programming Language"
  s2 := "あたりが、前なら、近所は、隣。"
  write("文字列からの単語の取り出しテスト")
  write("jsplit.icnでやると")

  write("data:\"",s1,"\"")
  n := 0
  s1 ? every s := !jsplit() do write(n +:= 1,":",s) 

  write("\ndata:\"",s2,"\"")
  n := 0
  s2 ? every s := !jsplit(set(["、","。"])) do write(n +:= 1,":",s) 

#  write("\njgetword.icnでやると")         # jsplit.icnと結果は同じ。
#  write("data:\"",s1,"\"")
#  n := 0
#  s1 ? every s := jgetword() do write(n +:= 1,":",s)
#  write("\ndata:\"",s2,"\"")
#  n := 0
#  s2 ? every s := jgetword(set(["、","。"])) do write(n +:= 1,":",s)
end
-----$ SPLIT5.ICN ( lines:57 words:210 ) -------------------<cut here

  split5 > split5.txtとした時の結果です。
-----^ SPLIT5.TXT ( date:97-03-30 time:18:38 ) -------------<cut here
文字列からの単語の取り出しテスト
jsplit.icnでやると
data:" The Icon Programming Language"
1:The
2:Icon
3:Programming
4:Language

data:"あたりが、前なら、近所は、隣。"
1:あたりが
2:前なら
3:近所は
4:隣
-----$ SPLIT5.TXT ( lines:13 words:16 ) --------------------<cut here

  今日の夕方は、HB彗星が良く見えました。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: Vivien/篠原美也子
(icon_203.txt 1997/03/30 PCVAN PIG)

■  Icon > Icon雑記帳(4) generatorの使い方(1)  風つかい           目次

  アリゾナ大の Icon-Webに アクセスしましたら、Icon Newsletterの No.52
が、出ていました。 urlは、次の通りです。
  http://www.cs.arizona.edu/icon/newsletter/inl52/inl52.html

  目次の部分を引用しておきます。
>No. 52 -- April 1, 1997 
>
>Contents
>
>      Mail-Order Program Material 
>      Teaching Icon 
>      Web Links 
>      Native Interface Components in Windows Icon 9.3 
>      Programming Language Handbook 
>      From Our Mail 
>      Knowledge Explorer 

  ご興味のある方は、ご覧になって下さい。

  さて、今日は、Iconの特徴 の1つである generatorの使い方 について、
お話し します。
  generatorと、その使い方を あげて みましょう。
  実例を 示した方が 早いと思いますので、早速サンプルを。
      every (generatorを含む式)     または、
      every (generatorを含む式)  do ...
の格好で 使います。
-----^ GEN01.ICN ( date:97-04-01 time:01:02 ) --------------<cut here
# generator 使い方1
# Rev.01 1997/04/01 windy
procedure main()
  write("generatorの使い方1")
  write("! : 要素を 順次 取り出す")
  
  every writes(" ",!"ABC")              ; write() # string
  every writes(" ",!["D","E","F"])      ; write() # list
  every writes(" ",!set(["G","H","I"])) ; write() # set(順番は無保証)
  write()

  write("! : 順次 取り出しの 組み合わせ")
  every writes(" ",!("MNO") || !("123"))
  write()
  write()

  write("! : 順次 取り出して、取り出す度に 何かの 付帯処理も行う")
  n := 0
  every s := !"PQR" do write(" ",n +:=1,":",s)
  write()

  write("alternation")
  every writes(" ","あ" | "い" | "う")     ; write() # alternation
  every writes(" ",(1 | 2 | 3) + (10 | 20 | 30))
  write()
  write()

  write("i to k by j : 数列の発生")
  every writes(" ",1 to 20 by 2)       ; write()
  s := "W1X2Y3Z4"
  write("! : 数列を発生して、その数を使って 何かの 処理を 行う")
  every writes(" ",s[1 to *s])         ; write()
  n := 0
  every i := 1 to *s by 2 do write(" ",n +:=1,":",s[i]) 
  write()

  write("seq()関数 : 数列の発生")
  every writes(" ",seq() \10)          ; write()  # seq() 10個限定
  write()

  write("文字列スキャン")
  s := "ABCDE12345ABCDE12345"
  write(s)
  write("upto(\'C1\')")
  s ? every writes(" ",upto('C1'))     ; write()
  write("find(\"AB\" | \"CD\")")
  s ? every writes(" ",find("AB"|"12"))  ; write()

end
-----$ GEN01.ICN ( lines:49 words:179 ) --------------------<cut here

  gen01 > gen01.txt とした時の結果です。
-----^ GEN01.TXT ( date:97-04-01 time:01:03 ) --------------<cut here
generatorの使い方1
! : 要素を 順次 取り出す
 A B C
 D E F
 H G I

! : 順次 取り出しの 組み合わせ
 M1 M2 M3 N1 N2 N3 O1 O2 O3

! : 順次 取り出して、取り出す度に 何かの 付帯処理も行う
 1:P
 2:Q
 3:R

alternation
 あ い う
 11 21 31 12 22 32 13 23 33

i to k by j : 数列の発生
 1 3 5 7 9 11 13 15 17 19
! : 数列を発生して、その数を使って 何かの 処理を 行う
 W 1 X 2 Y 3 Z 4
 1:W
 2:X
 3:Y
 4:Z

seq()関数 : 数列の発生
 1 2 3 4 5 6 7 8 9 10

文字列スキャン
ABCDE12345ABCDE12345
upto('C1')
 3 6 13 16
find("AB" | "CD")
 1 11 6 16
-----$ GEN01.TXT ( lines:36 words:113 ) --------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: Four On Six / Wes Montgomery
(icon_204.txt 1997/04/01 PCVAN PIG)

■  Icon > Icon雑記帳(5) generatorの使い方(2)  風つかい             目次

  今回は、generatorの続きです。
  データを 順次 発生するものには、前回あげたものの他にも あります。

  ・read(), reads() のような ファイル読みだし関数
  ・get(), pull()   のような データ取り出し関数
  ・upto(), find()  のような スキャン関数
と
  ・while ...
  ・until ...
  ・repeat ...
のような くり返し構文 の組み合わせでも、データを 順次 発生できます。

  早速サンプルを。
-----^ GEN02.ICN ( date:97-04-02 time:23:04 ) --------------<cut here
# generator 使い方2
# This file is in the public domain.
# Rev.01 1997/04/02 windy
procedure main()
  write("generatorの使い方2")
  write()

  write("while ... read()のサンプル  最初の5行のみ書き出し")
  dir := open(f_name := "gen02.icn") | stop("cannot open ",f_name)
                          #↑このソースプログラムをオープン
  n := 0
  while s := read(dir) do {
    n +:= 1
    write(5 >= n," :",s)  # 最初の5行のみ書き出す
 }
#  while s := read(dir) do write( 5 >= (n +:= 1), " :",s)  # これでも同じ。
#  while write(5 >= (n +:= 1) ," :",read(dir))             # これでも同じ。
  write()

  write("while ... get()のサンプル   先頭から取り出し")
  L := ["A","B","C"]
  n := 0
  while s := get(L) do writes(" ",n +:= 1,":",s) ; write()
  write()

  write("while ... pull()のサンプル  末尾から取り出し")
  L := ["A","B","C"]
  n := 0
  while writes(" ",n +:= 1,":",pull(L)) ; write() # get()もこう書けます。
  write()

  write("until ... do ... pop() のサンプル")
  L := ["A","B","C","D","E","F","G"]
  n := 0
  until n = 5 do writes(" ",n +:= 1,":",pop(L))   # pop()は get()と同じ。
  write() 
  write()

  write("repeat { if ... break  } のサンプル")
  L := ["A","B","C","D","E","F","G"]
  n := 0
  repeat {
    writes(" ",n +:= 1,":",get(L))
    if n >= 5 then break
  }
  write() 
  write()

  write("while ... upto() do {... many() ...} のサンプル")
  s := " The Icon Programming Language"
  n := 0
  s ? { while tab(upto(~' ')) do writes(" ",n +:= 1,":",tab(many(~' '))) }
  write()

end
-----$ GEN02.ICN ( lines:55 words:209 ) --------------------<cut here

  gen02 > gen02.txtとした時の結果です。
-----^ GEN02.TXT ( date:97-04-02 time:23:04 ) --------------<cut here
generatorの使い方2

while ... read()のサンプル  最初の5行のみ書き出し
1 :# generator 使い方2
2 :# This file is in the public domain.
3 :# Rev.01 1997/04/02 windy
4 :procedure main()
5 :  write("generatorの使い方2")

while ... get()のサンプル   先頭から取り出し
 1:A 2:B 3:C

while ... pull()のサンプル  末尾から取り出し
 1:C 2:B 3:A

until ... do ... pop() のサンプル
 1:A 2:B 3:C 4:D 5:E

repeat { if ... break  } のサンプル
 1:A 2:B 3:C 4:D 5:E

while ... upto() do {... many() ...} のサンプル
 1:The 2:Icon 3:Programming 4:Language
-----$ GEN02.TXT ( lines:23 words:78 ) ---------------------<cut here

  なんだか、くり返し構文の説明になってしまった。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: 熱くなれ/大黒麻季
(icon_205.txt 1997/04/02 PCVAN PIG)

■  Icon > Icon雑記帳(6) generatorの作り方(1)  風つかい            目次

  今回は、generatorの使い方に 変わって、作り方です。
といっても、前々回、前回の generatorの使い方で、write()や writes()で
データを書き出しているところを、 suspend データ という形に、変えれば
そのまま generatorができあがります。

  例えば、前々回の gen01.icnの
  every writes(" ",!"ABC")              ; write() # string
は、
procedure abc()
  suspend !"ABC"
end
と変形すれば、これは generatorとなります。

procedure main()
  every writes(" ",abc())
end
とすると、最初の、every writes(" ",!"ABC") と同じ動作となります。

  実際のプログラムでは、同じ動作では仕方が無いのですが、generator
を使って、generatorを作る一番簡単なサンプルとしてあげました。

  実際は、複雑な generatorを、比較的簡単な generatorを段階的に組み
合わせて作っていきます。

  gen01.icnを generatorに変換しますと、次のように なります。
-----^ GEN01A.ICN ( date:97-04-04 time:21:46 ) -------------<cut here
# generator 使い方1 の procedureを generatorへ変換
# This file is in the public domain.
# Rev.01 1997/04/04 windy
procedure main()
  write("generatorの使い方1")
  write("! : 要素を 順次 取り出す")
  every writes(" ",abc())   ; write() # string
  every writes(" ",def())   ; write() # list
  every writes(" ",ghi())   ; write() # set(順番は無保証)
  write()

  write("! : 順次 取り出しの 組み合わせ")
  every writes(" ",mno123())
  write()
  write()

  write("! : 順次 取り出して、取り出す度に 何かの 付帯処理も行う")
  n := 0
  every s := pqr() do write(" ",n +:=1,":",s)
  write()

  write("alternation")
  every writes(" ",AIU())   ; write() # alternation
  every writes(" ",number1())
  write()
  write()

  write("i to k by j : 数列の発生")
  every writes(" ",number2())       ; write()
  write("! : 数列を発生して、その数を使って 何かの 処理を 行う")
  n := 0
  every writes(" ",wxyz1())  ; write()
  every L := wxyz2() do write(" ",L[1],":",L[2]) 
  write()

  write("seq()関数 : 数列の発生")
  every writes(" ",seque() \10)     ; write()  # 10個限定
  write()                 #↑生成数限定

  write("文字列スキャン")
  s := "ABCDE12345ABCDE12345"
  write(s)
  write("upto(\'C1\')")
  s ? every writes(" ",n_upto(s))   ; write()
  write("find(\"AB\" | \"CD\")")
  s ? every writes(" ",n_find(s))   ; write()

end


# generator
procedure abc()
  suspend !"ABC"                # string
end
procedure def()
  suspend !["D","E","F"]        # list
end
procedure ghi()
  suspend !set(["G","H","I"])   # set(順番は無保証)
end
procedure mno123()
  suspend !("MNO") || !("123")
end
procedure pqr()
  suspend !"PQR"
end
procedure AIU()
  suspend "あ" | "い" | "う"
end
procedure number1()
  suspend (1 | 2 | 3) + (10 | 20 | 30)
end
procedure number2()
  suspend 1 to 20 by 2
end
procedure wxyz1()
  s := "W1X2Y3Z4"
  suspend s[1 to *s]
end
procedure wxyz2()
  n := 0
  s := "W1X2Y3Z4"
  every i := 1 to *s by 2 do suspend [n +:= 1,s[i]]
end
procedure seque()
  suspend seq()
end
procedure n_upto(s)
  s ? suspend upto('C1')
end
procedure n_find(s)
  s ? suspend find("AB"|"12")
end
-----$ GEN01A.ICN ( lines:93 words:279 ) -------------------<cut here

  gen02.icnを generatorに変換したものは、次回にします。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: MOSAIC/遊佐未森
(icon_206.txt 1997/04/04 PCVAN PIG)

■  Icon > Icon雑記帳(7) generatorの作り方(2)  風つかい           目次

  今回も generatorの作り方の続きです。gen02.icnの
  L := ["A","B","C"]
  n := 0
  while s := get(L) do writes(" ",n +:= 1,":",s) ; write()

の while ...の部分を
  while s := get(L) do suspend s
とすると、これも generatorとなります。 これが、くり返し構文を使って
generatorを作る 一番簡単なサンプルとなります。

  この場合に、nも返したければ、
  while s := get(L) do suspend [n +:= 1,s]
とすると、リストを発生する generatorということになります。

  generatorを 使って、generatorを 階層的に 作っていくやり方は、既に
jsplit.icnで 使っています。
 jsplit.icnの中の jgetword()は、次のような 階層構造に なっていました。

 generator --- jgetword() ---
  |  ...                     |
  |  while ... jupto_not()...| <----   文字列から単語を切り出す
  |  ...                     |      |  generator
   --------------------------       |
                     ---------------
                    |
 generator --- jupto_not() -- 
  |  ...                     |         文字列を、1文字ずつ なめて
  |  every ... jpos() do ... | <----   文字セットとの照合をして、
  |  ...                     |      |  単語の区切り位置を探す
   --------------------------       |  generator
                     ---------------
                    |
 generator --- jpos()--------          文字列を、1バイトずつ なめて
  |  ...                     |         シフトJISの文字の 先頭位置
  |  every ... i to j do ... |         を探す generator
  |  ...                     |         ( i to j という generatorを
   --------------------------             を使った generatorです。)

  さて、gen02.icnを generatorに変換したものを、次に。
-----^ GEN02A.ICN ( date:97-04-04 time:21:50 ) -------------<cut here
# generator 使い方2 の procedureを generatorへ変換
# This file is in the public domain.
# Rev.01 1997/04/04 windy
procedure main()
  write("generatorの使い方2")
  write()

  write("while ... read()のサンプル  最初の5行のみ書き出し")
  dir := open(f_name := "gen02a.icn") | stop("cannot open ",f_name)
                          #↑このソースプログラムをオープン
  # 最初の5行のみ書き出す
  every L := line(dir) \5 do write(L[1]," :",L[2])
  write()             #↑生成数限定(行数制限に使う)

  write("while ... get()のサンプル   先頭から取り出し")
  L := ["A","B","C"]
  every LL := get_l(L) do writes(" ",LL[1],":",LL[2])       ; write()
  write()

  write("while ... pull()のサンプル  末尾から取り出し")
  L := ["A","B","C"]
  every LL := pull_l(L) do writes(" ",LL[1],":",LL[2])      ; write()
  write()

  write("until ... do ... pop() のサンプル")
  L := ["A","B","C","D","E","F","G"]
  every LL := until_l(L) do writes(" ",LL[1],":",LL[2])     ; write() 
  write()

  write("repeat のサンプル")
  L := ["A","B","C","D","E","F","G"]
  every LL := repeat_l(L) \5 do writes(" ",LL[1],":",LL[2]) ; write() 
  write()                #↑生成数限定(行数制限に使う)

  write("while ... upto() do {... many() ...} のサンプル")
  s := " The Icon Programming Language"
  every LL := word(s) do writes(" ",LL[1],":",LL[2])        ; write()
end


# generator
procedure line(dir)
  n := 0
  while s := read(dir) do suspend [n +:= 1,s] # 行数制限は、上位へ移動
end
procedure get_l(L)
  n := 0
  while s := get(L) do suspend [n +:= 1,s]
end
procedure pull_l(L)
  n := 0
  while s := pull(L) do suspend [n +:= 1,s]
end
procedure until_l(L)
  n := 0
  until n = 5 do suspend [n +:= 1,pop(L)]     # pop()は get()と同じ。
end
procedure repeat_l(L)
  n := 0
  repeat suspend [n +:= 1,get(L)]             # 行数制限は、上位へ移動
end
procedure word(s)
  n := 0
  s ? {
    while tab(upto(~' ')) do {
      s := tab(many(~' '))     # ここは、suspendの外でやらないとうまく
                               # 動きません。
      suspend [n +:= 1,s]
    }
  }
end
-----$ GEN02A.ICN ( lines:71 words:251 ) -------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: MOSAIC/遊佐未森
(icon_207.txt 1997/04/04 PCVAN PIG)

■  Icon > Icon雑記帳(8) generatorのまとめ        風つかい              目次

  このところ、雨の日が多いです。  でも、桜の花は 結構 しぶとく残って
いますね。  さて、雨の日は、Iconでもやろうかな。

  今日は、generatorの考え方のまとめを、やってみようと思います。
  generatorを 使ったプログラム の構成は、

     - generator---------                -consumer----------
    |  データ発生部      |   データ     |  データ消費部     |
    |    suspend data ----------------->  every  generator  |
    |                    |              |                   |
     --------------------                -------------------
      データを順次生成                       データ加工
                                             データ選択
の ような 格好になります。
  データを 順次生成する プログラムがあって、そのデータを 消費するプログ
ラムが ある。  という構成になります。

  ここで、generatorが生成したデータに対して、consumerが 条件に 合わない
データを棄てて、次のデータを試してみる。 とした 場合が、Backtracking と
いうことに なります。


  前回、あげました jgetword()は、プログラム構成上は、

          -----------
  上位   | jgetword  | 単語 切り出し
   ↑     -----------
   |             |    ------------
   |         呼ぶ -->| jupto_not  | 単語 区切り位置 検索
   |         call     ------------
   |                           |    ---------------
   |                       呼ぶ -->| jpos          | シフトJIS文字
   ↓                       call    |    |          | 区切り位置 検索
  下位                              |呼ぶ -> i to j |
                                     ---------------

という階層に なっていますが、考え方としては、

                               drive     -----------
  上位                         駆動  -->| jgetword  | 単語 切り出し
   ↑                               |    -----------
   |           drive    ------------
   |           駆動 -->| jupto_not  | 単語 区切り位置 検索
   |               |    ------------
   |     ---------------
   |    |駆動 --> jpos  | シフトJIS文字
   ↓    |    |          | 区切り位置 検索
  下位   | i to j        |
          ---------------

  generatorが生成するデータで、順次 上位プログラムを 駆動していく構成と
考えた方が、分かりやすいと思います。  データ駆動型のプログラム構成 とで
も言えるでしょう。


  テキストデータで、
    ・1行に データが スペース区切りで、格納されている形式で、
      そのデータを使って、何らかの処理を やりたい場合
ですと、
  AWKのように、行データを 自動的に カラム分割して $1,$2,...と変数にセット
してくれた方が、プログラムは 組みやすく なります。
  Iconでは、この場合は、カラム分割 procedureを 作らないと いけません。

  一方、
    ・テキストデータが、通常の文章 とか プログラムソースで、
      文字や単語の出現順に、何かの処理を やりたい場合
ですと、
  Iconのように、文字や単語を、先頭から 順次 切り出して いく機能が 必要に
なります。  このような処理で generatorが 非常に 役立ちます。
  generatorは、文章のような テキストデータを、文字の集まり 単語の集まり と
みなした方が 自然なケース のプログラムに、向いています。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: そのままの君でいて/岡本真夜
(icon_208.txt 1997/04/05 PCVAN PIG)

■  Icon > Icon雑記帳(9) IYAX.ICN                 風つかい            目次

  Icon入門講座で、YAX形式のバインダー YAB.ICNを説明しましたが、実はテキ
ストファイルから、ファイルを取り出す YAX.ICNも いっしょに説明する予定で
した。

  しかし、コメント付けがメンドイので はしょって しまいました。
  次のシリーズでは、説明しようと 思っていた のですが、Icon日記を書いてる
頃には 、すっかり 忘れてしまって いました。
  というのは、YABは使うのですが、YAXは余り使わないもので。
  どなたかの 書き込みの中の スクリプトを、ログから抜き出して、ちょっと
動かしてみるのは、エディターで切り出しても、大差は無いですからね。

  Icon入門講座や、Icon日記から、まとめて プログラムを 切り出す のなら
あった方が、良いと思うのですが。

  でも、YABと YAXはやはり ペアで揃えて 置きたい。ということで、だいぶ前
に作ったプログラムに、コメントを追加して、整えてみました。

-----^ IYAX.ICN ( date:97-04-05 time:22:22 ) ---------------<cut here
###################
# YAX.LZH awkスクリプト掲載標準形式布教パッケージ Version 1.00 July/4/1990
# by takun@日経MIX のYAX.BATをIconに置換したもの。細かい動作は多少異なる。
###################
# This file is in the public domain.
# iyax.icn 1.8 1997/04/05 windy コメント追加
# iyax.icn 1.1 1995/07/01 - Yet Another eXtractor
global T, S
#link split
procedure main(args)
  Usage := "Usage : iyax input_file"

  # ファイルオープン
  if *args = 1 then in_dir := open(args[1]) | stop("cannot open ",args[1])
  else stop(Usage)
  write(&errout,args[1])  # ファイル名表示
  T := table()            # ファイル属性格納テーブル生成
  S := table()            # ファイル属性格納テーブル(チェック用)生成
  files := 0              # ファイル数

  # ファイル読み込み
  while line := read(in_dir) do {    # 1行づつ読み込む
    line ? {
      # 先頭が "-" で、その後に "-"が複数あったら読み飛ばす
      if ( tab(match("-")) & tab(many('-')) ) then {
        case s := move(2) of {       # その後の2文字をチェック
          "^ " :  {                  # ファイル開始記号
                    # ファイル出力中に、またヘッダーが来た時の始末
                    if \T["f_name"] then {  # 出力中ファイル名が存在すれば
                      f_close(o_dir) # 出力ファイル クローズ処理
                      files  +:= 1
                      write(&errout,"no trailer error") # トレーラー無し
                    }
                    # 通常処理
                    head_anl(line)                    # ヘッダー解析
                    o_dir := open(T["f_name"],"a") |  # 出力ファイルオープン
                    stop("open error ",f_name)
                    S_inz()                           # チェック用テーブル
                                                      # イニシャライズ
                  }
          "$ " :  {                  # ファイル終了記号
                    # ファイル出力中で無いのに、トレーラーが来た時の始末
                    if /T["f_name"] then {  # 出力中ファイル名が無ければ
                      write(&errout,"no header error")
                    }
                    # 通常処理
                    else {
                      tail_anl(line)                  # トレイラー解析
                      f_close(o_dir) # 出力ファイル クローズ処理
                      files  +:= 1
                    }
                  }
       default :  {                  # output body
                    # ファイル出力中なら、行データ出力
                    if \T["f_name"] then line_out(o_dir,line)
                  }
          } # end of case
        } # end of if...
        else {                       # output body
          if \T["f_name"] then line_out(o_dir,line)
        } # end of else
      } # end of line ?
    } # end of while

    close(in_dir)                    # ファイルクローズ
    write(&errout,"\n",files," file(s) extracted.") # 終了メッセージ

end

procedure f_close(o_dir)
# 出力ファイルクローズ
  close(o_dir)               # 出力ファイル クローズ
  set_time()                 # 出力ファイルのタイムスタンプ設定
  check()                    # ファイル属性のチェック
  every delete(T,key(T))     # テーブル内容クリア
  every delete(S,key(S))     # テーブル内容クリア
  return
end

procedure S_inz()
# チェック用データ格納テーブルをイニシャライズ
  S["lines"] := S["words"] := S["chars"] := 0
  return
end

procedure head_anl(line)
# ヘッダーを解析して、属性指定があれば、テーブルにセット
  write(&errout,line)
  L := split(line)
  T["f_name"] := L[2]   # 出力ファイル名セット
  set_atr(T,L)
  return
end

procedure tail_anl(line)
# トレイラーを解析して、属性指定があれば、テーブルにセット
  write(&errout,line)
  L := split(line)
  S["f_name"] := L[2]   # 出力ファイル名セット
  set_atr(T,L)
  return
end


procedure set_atr(T,L)
# テーブルにヘッダー、トレイラーの属性をセットする。
  every s := !L do {
    s ? if cp := upto(':') then T[ s[1 : cp]] := s[cp +1 : 0]
  }
  return
end

procedure line_out(o_dir,line)
  write(o_dir,line)
  # チェックテーブルの内容を更新
  S["lines"] +:= 1              # lines 行数
  S["words"] +:= *split(line)   # words 語数
  S["chars"] +:= *line          # chars 文字数
  return
end

procedure check()
# ファイルのヘッダー・トレーラーの属性部と、
# 実際のファイルの属性を、比較
  every x := key(T) do {
    if string(T[x]) ~== string(S[x]) then {
      write(&errout,T["f_name"]," ",x," error")
    }
  }
  return
end

procedure set_time()
# タイムスタンプの設定。 osawariは、YAX.LZHに含まれるものを使用。
# 無ければ、他の touchに合うように修正するか、コメントアウトして下さい。
  if (member(T,"date") & member(T,"time")) then {
    system("osawari " || T["date"] || " " || T["time"] || " " || T["f_name"])
  }
  return
end

# 文字列を指定文字(セット)で区切りリストに入れる。
procedure split(line,c)
# function : split a string  with c and store a list(exclude c)
  /c := ' \t'                      # 指定が無ければスペースかタブとみなす。
  list := []                       # return用リスト生成
  line ? while tab(upto(~c)) do put(list,tab(many(~c)))
  return list
end
-----$ IYAX.ICN ( lines:149 words:469 ) --------------------<cut here

  IYAXで、Icon入門講座や Icon日記から プログラムを 切り出しますと、エラー
が 出るファイルがあります。  そのファイルは、YABでバインドしたあとで、コメ
ントを追加したり、修正したために、行数や語数がずれているものです。(汗)
  また、大きなデータファイルで、中略しているものでも、エラーが出ます。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: 愛の言霊/サザン・オールスターズ
(icon_209.txt 1997/04/05 PCVAN PIG)

■  Icon > Icon雑記帳(10) 順列                   風つかい           目次

  HB彗星に関するTV番組を、結構やっていますね。
  で、彗星は 何処から来るかというので、太陽系から10兆kmあたりまでは、
観測できないが 氷や塵が漂っているそうです。
  その辺から太陽系に近づいて来て、太陽の引力に捉えられたものが彗星になって
太陽の周りを回りだすのではないか?  という説明をやっていました。

  冥王星のあたりが、60億kmですので、そのまた遥か遠くからやって来るので
すね。
  今日も雨で、HB彗星は見えません。  でも、飛行機で旅行される方は、雲の上
を飛んでらっしゃいますので、ご覧になれる方もいますよね。

  さて、以前、文字列の組み合わせを、やりました。  これは、確か高校の頃に
やった  順列・組み合わせ と同じものですよね。
  nPmとか nCmに、頭を悩ませたものです。  前のプログラムでは、nPnしかやって
いませんので、一般化して、nPmの場合のプログラムを作ってみましょう。

  stringの順列と listの順列のプログラムを作ってみました。
  考え方は、Icon日記(16) Recursive generator と同じです。
  生成文字数に 制限を つけないといけない のですが、そこは紙に実際に書き出
して  ふむふむ、ここで制限をつければいいんだな...と、やってます。

  3P2の場合ですと、
perm("DNA",2)
  |--> "D" || perm("NA",1)             # ここで1文字目
               |--> "N" || perm("A",0) # ここで2文字目(後は不要)
                            |---> ""   # 3文字目なので空文字で返す。
という風に動きます。

-----^ PERM11.ICN ( date:97-04-06 time:22:24 ) -------------<cut here
# nPm (n = *s または *L)  n個のものから、m個を選んで、並べる。
# This file is in the public domain.
# perm11.icn 1997/04/06 windy

# stringの順列
# nPm (n = *s)  n個のものから、m個を選んで、並べる。
procedure perm(s,m)
local i
  /m := *s                    # デフォルト nPn (n = m = *s)
  if m = 0 then return ""     # mを文字数カウンターに使う。
                              # 0なったら、そこで打ち止め。
  suspend s[i := 1 to *s] || perm(s[1:i] || s[i+1:0],m -1)
          #↑ 1文字選ぶ  ↑ ↑←前で選んだ文字以外  ↑指定文字数に
          #               |     の文字列に対し同じ    文字列を抑え
          #               |     処理を行う。          るためのカウ
          #           文字列の連結                     ンター
end

# listの要素の順列。
# nPm (n = *L)  n個のものから、m個を選んで、並べる。
procedure lperm(L,m)
local i
  /m := *L
  if m = 0 then return []
   suspend [L[i := 1 to *L]] ||| lperm(L[1:i] ||| L[i+1:0],m -1)
          #                  ↑listの連結
end


# test data
procedure main()
  # 英文字の順列サンプル
  s := "DNA"
  write(s,"の順列")
  write("3P0  3文字から0文字選ぶ")
  every write(perm(s,0))
  write("3P1  3文字から1文字選ぶ")
  every write(perm(s,1))
  write("3P2  3文字から2文字選ぶ")
  every write(perm(s,2))
  write("3P3  3文字から3文字選ぶ")
  every write(perm(s,3))
  write()

  # list要素(日本語文字)の順列サンプル
  L := ["ま","ゆ","み"]
  every writes(!L)           ; write("の順列")
  write("3P0  3文字から0文字選ぶ")
  every LL := lperm(L,0)  do {
    every writes(!LL)
    write()
  }
  write("3P1  3文字から1文字選ぶ")
  every LL := lperm(L,1)  do {
    every writes(!LL)
    write()
  }
  write("3P2  3文字から2文字選ぶ")
  every LL := lperm(L,2)  do {
    every writes(!LL)
    write()
  }
  write("3P3  3文字から3文字選ぶ")
  every LL := lperm(L,3)  do {
    every writes(!LL)
    write()
  }
end
-----$ PERM11.ICN ( lines:68 words:204 ) -------------------<cut here

  結果です。
-----^ PERM11.TXT ( date:97-04-06 time:22:24 ) -------------<cut here
DNAの順列
3P0  3文字から0文字選ぶ

3P1  3文字から1文字選ぶ
D
N
A
3P2  3文字から2文字選ぶ
DN
DA
ND
NA
AD
AN
3P3  3文字から3文字選ぶ
DNA
DAN
NDA
NAD
ADN
AND

まゆみの順列
3P0  3文字から0文字選ぶ

3P1  3文字から1文字選ぶ
ま
ゆ
み
3P2  3文字から2文字選ぶ
まゆ
まみ
ゆま
ゆみ
みま
みゆ
3P3  3文字から3文字選ぶ
まゆみ
まみゆ
ゆまみ
ゆみま
みまゆ
みゆま
-----$ PERM11.TXT ( lines:43 words:48 ) --------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: 哀愁のカサブランカ/郷ひろみ
(icon_210.txt 1997/04/06 PCVAN PIG)

■  Icon > Icon雑記帳(11) 組み合わせ             風つかい              目次

  前回、順列をやりましたので、今回は、組み合わせです。
  nCmに相当するプログラムです。順列のプログラムと、ほぼ同様のプログラム
です。
  順列と違って、文字の順番を考慮しませんので、
  残りの部分に、同じ処理を加えるために、渡す処理が少し違っています。

  3C2の場合ですと、
comb("CPU",2)
  |--> "C" || comb("PU",1)             # ここで1文字目
               |--> "P" || comb("U",0) # ここで2文字目(後は不要)
                            |---> ""   # 3文字目なので空文字で返す。
という風に動きます。

-----^ COMB11.ICN ( date:97-04-07 time:19:06 ) -------------<cut here
# nCm (n = *s または *L)  n個のものから、m個を選ぶ。
# This file is in the public domain.
# comb11.icn 1997/04/07 windy

# stringの組み合わせ
# nCm (n = *s)  n個のものから、m個を選ぶ。
procedure comb(s,m)
  /m := *s                    # デフォルト nCn (n = m = *s)
  if m = 0 then return ""     # mを文字数カウンターに使う。
                              # 0なったら、そこで打ち止め。
  suspend s[i := 1 to *s] || comb(s[i+1 : 0],        m -1)
          #↑ 1文字選ぶ  ↑ ↑←前で選んだ文字より  ↑指定文字数に
          #               |     後ろの文字列に同じ    文字列を抑え
          #               |     処理を行う。          るためのカウ
          #           文字列の連結                     ンター
end


# listの要素の組み合わせ。
# nCm (n = *L)  n個のものから、m個を選ぶ。
procedure lcomb(L,m)
local i
  /m := *L
  if m = 0 then return []
  suspend [L[i := 1 to *L]] ||| lcomb(L[i+1 : 0],m -1)
          #                  ↑listの連結
end


# test data
procedure main()
  # 英文字の組み合わせサンプル
  s := "CPU"
  write("\"",s,"\" の組み合わせ")
  every i := 1 to *s do {
    write("\n\"",*s,"C",i)
    j := 0
    every ss := comb(s,i) do {
      j +:= 1
      writes(j,":",ss)
      write()
    }
  }
  write()

  # list要素(日本語文字)の組み合わせサンプル
  L := ["さ","お","り"]
  every writes(!L)           ; write(" の組み合わせ")
  every i := 1 to *L do {
    write("\n\"",*L,"C",i)
    j := 0
    every LL := lcomb(L,i) do {
      j +:= 1
      writes(j,":")
      every writes(!LL)
      write()
    }
  }

end
-----$ COMB11.ICN ( lines:60 words:193 ) -------------------<cut here

  結果です。
-----^ COMB11.TXT ( date:97-04-07 time:19:23 ) -------------<cut here
"CPU" の組み合わせ

"3C1
1:C
2:P
3:U

"3C2
1:CP
2:CU
3:PU

"3C3
1:CPU

さおり の組み合わせ

"3C1
1:さ
2:お
3:り

"3C2
1:さお
2:さり
3:おり

"3C3
1:さおり
-----$ COMB11.TXT ( lines:29 words:24 ) --------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: Waltz For Debby / Bill Evans
(icon_211.txt 1997/04/07 PCVAN PIG)

■  Icon > Icon雑記帳(12) pipe風のプログラム     風つかい             目次

  パイプというのは、Unixや MS-DOSでいうパイプのことです。プログラム
の間のデータの渡し方で、

                   data                     data
   ------------   stream    ------------   stream     ------------
  |  program1  |  =====>   |  program2  |  =====>    |  program3  |
   ------------    pipe     ------------    pipe      ------------

               標準     標準            標準      標準
               出力     入力            出力      入力

と、つないでいける ものです。

  generatorの考え方の説明をしていて、こりゃ〜パイプだ〜と思いました。
  generatorのことですから、procedureの間のデータの受け渡し方法のこと
になるんですが。 よく似ていますよね。

  パイプの場合は、上の図のように、program1と program2は対等な位置
関係になりますが、procedureの場合は、データを渡すところで、対等と
いう訳にはいかず、どちらかのプログラムが 他方を呼ぶ/使うという関
係になりますので、

   ------------    data     ------------    data      ------------
  | procedure1 |  =====>   | procedure2 |  =====>    | procedure3 |
   ------------             ------------              ------------

は、

   - proc1 ----  呼ぶ/使う - proc2 ----  呼ぶ/使う  - proc3 ----
  |            |     ----    procedure2 |     ----     procedure3 |
  |            |    /      |            |    /       |            |
  |     proc2   <--        |             <--         |            |
   ------------ 下位   上位 ------------ 下位    上位 ------------

という構成になります。

  サンプルとして、ちょっと強引な例ですが、データを発生して、それを
フィルターにかけて、出力するものを。
-----^ PIPE01.ICN ( date:97-04-10 time:21:57 ) -------------<cut here
# pipe風 プログラム構成 サンプル
# samp212.icn 1997/04/10 windy
# This file is in the public domain.

procedure song_name()
# データ発生
  L_Vivien := ["Good Friend",
               "風の背中",
               "傷だらけの天使",
               "春の日",
               "welcome trouble",
               "夜明けのランナー",
               "Always",
               "午前3時の雨",
               "極楽駅から見える月",
               "暖かい沈黙",
               "前髪"]
  suspend !L_Vivien                       # listから要素を順に取り出し
end

#             ↓データ

procedure sel_word()
# フィルター(文字列に "の" が含まれるものを抜き出す)
  key := "の"
  every s := song_name() do {
    s ? if find(key) then suspend s
  }
end

#             ↓データ

procedure main()
# 書き出し
  every write(sel_word())
end
-----$ PIPE01.ICN ( lines:36 words:73 ) --------------------<cut here

  結果は次のようになります。
-----^ PIPE.TXT ( date:97-04-10 time:22:28 ) ---------------<cut here
風の背中
傷だらけの天使
春の日
夜明けのランナー
午前3時の雨
-----$ PIPE.TXT ( lines:5 words:5 ) ------------------------<cut here

  これは、次の3つのプログラムを、pipeで つないだものと、同じです。
  コマンドラインから、次のように打ち込みますと、同じ結果がでます。
  pipe02 | pipe03 | pipe04 > pipe.txt

  pipe03 の出力が標準出力ですので、pipe04 不要ですが、形を合わせるため
に入れてあります。(汗)

-----^ PIPE02.ICN ( date:97-04-10 time:22:21 ) -------------<cut here
# pipe サンプルプログラム1
# pipe02.icn 1997/04/10 windy
# This file is in the public domain.
procedure main()
# データ発生
  L_Vivien := ["Good Friend",
               "風の背中",
               "傷だらけの天使",
               "春の日",
               "welcome trouble",
               "夜明けのランナー",
               "Always",
               "午前3時の雨",
               "極楽駅から見える月",
               "暖かい沈黙",
               "前髪"]
  every write(!L_Vivien)         # listから要素を順に取り出し
end
-----$ PIPE02.ICN ( lines:18 words:37 ) --------------------<cut here

-----^ PIPE03.ICN ( date:97-04-10 time:22:20 ) -------------<cut here
# pipe サンプルプログラム2
# pipe03.icn 1997/04/10 windy
# This file is in the public domain.
procedure main()
# フィルター
# 文字列中に "が" 含まれるものを、抜き出す。
  while line := read() do {
    key := "の"
    line ? if find(key) then write(line)
  }
end
-----$ PIPE03.ICN ( lines:11 words:40 ) --------------------<cut here

-----^ PIPE04.ICN ( date:97-04-10 time:22:20 ) -------------<cut here
# pipe サンプルプログラム3
# pipe04.icn 1997/04/10 windy
# This file is in the public domain.
procedure main()
# 書き出し
  while write(read())
end
-----$ PIPE04.ICN ( lines:7 words:22 ) ---------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: Vivien/篠原美也子
(icon_212.txt 1997/04/10 PCVAN PIG)

■  Icon > Icon雑記帳(13) ビデオ問題は、道遠し   風つかい           目次

  う〜ん。難しい。  先日から、組み合わせのプログラムを色々試していますが
目標があります。
  以前の書きこみにあった、録画ビデオを効率よく、別のビデオテープへ詰め込
む問題が、なんとか解けないかとやっています。
  しかし、道は遠いな〜  という状態です。

(PCVANの PIGの テキストデータ会議室に、録画ビデオを 別のテープに
  効率良く 詰め込むアルゴリズムは無いか と質問された方が いらっしゃいまし
  た。 この辺は、それを講座のネタに 使わせて いただいています。)

  本屋で、組み合わせ理論の本とか、アルゴリズムの本とかをパラパラめくって
みたのですが、この問題は「ナップザック問題」というらしいですね。
  ナップザック問題は、ナップザックと荷物があって、一番たくさん詰めるやり
方はどれかというような問題です。
  具体的に、どうやって、組み合わせを洗いだすのか、よく分かりません。

  この問題を解くプログラムには、ある程度は「しらみつぶし」「総当たり」的
な処理が必要だろうということで、Iconが向いているのは確かだと思うのですが
なかなか、手強いです。  今回も、ビデオ問題の習作プログラムとなります。

  generatorからデータを受け取り、そのデータを順に組み合わせていく プログ
ラムをやってみます。  動作は、コメントを参照して下さい。

-----^ COMB12.ICN ( date:97-04-12 time:10:53 ) -------------<cut here
# generatorからのデータの組み合わせ
# comb12.icn 1997/04/12 windy
# This file is in the public domain.

# 文字列 sの文字を、1文字ずつ先頭から、順次 生成する generator
# s : ソースデータ
procedure gen_char(s)
  suspend !s               # 1文字ずつ取り出す
end

#          ↓データ(1文字)

# 文字列 sの文字を、1文字ずつ受け取り、組み合わせ文字列 を、
# 順次 生成する generator
# s : ソースデータ
procedure glcomb(s)
  L := []   # ワーク用リスト(生成済の文字列を、文字数別に格納。)
# "A","B","C"の順で3文字を受けた場合の動作は次の通りです。
# [["A"]]                                    # 1文字目の状態
#   ↑1文字リスト

# 1文字リストの文字 "A"と新たに来た "B"を連結した文字列を、
# 後ろに2文字リストを作成し格納し、
# 更に、1文字リストに "B"を追加すると、
# [["A","B"],     ["AB"]]                    # 2文字目の状態
#   ↑1文字リスト  ↑2文字リスト

# 2文字リストの文字 "AB"と新たに来た "C"を連結した文字列を、
# 後ろに3文字リストを作成し格納し、
# 更に、1文字リストの文字 "A","B"と "C"を各々連結した文字列を、
# 2文字リストに格納し、
# 更に、1文字リストに "C"追加格納すると、
# [["A","B","C"], ["AB","AC","DC"],["ABC"]]  # 3文字目の状態
#   ↑1文字リスト  ↑2文字リスト   ↑3文字リスト
#
  every new := gen_char(s) do {   # generatorから1文字取り出し
    put(L,[])                   # リスト L内に、空リスト追加
    every i := *L to 1 by -1 do { # リストを、末尾から処理
      if i > 1 then {             # リストが2要素以上だったら
        every j := 1 to *L[i -1] do {  # (i-1)文字リストの要素に
          put(L[i], comb := (L[i -1][j] || new) ) # 新たな文字を連結し
                                                  # i文字リストに格納
        suspend comb              # 追加した文字列を渡す
        }
      }
      else {
        put(L[1],new)             # 1文字リストに、新たな文字を追加
        suspend new               # 追加した文字を渡す
      }
    }
  }
end

#          ↓データ(文字(列))

# 文字列設定/結果書き出し処理
procedure main(args)
  # コマンドラインパラメータが無ければ、"ABCD"をソースデータとする。
  s := if *args = 0 then "ABCD" else args[1]
  # すべての組み合わせを書き出す。
  every write(glcomb(s))
end
-----$ COMB12.ICN ( lines:62 words:198 ) -------------------<cut here

  結果は、こうなります。
-----^ COMB12.TXT ( date:97-04-12 time:10:54 ) -------------<cut here
A
AB
B
ABC
AC
BC
C
ABCD
ABD
ACD
BCD
AD
BD
CD
D
-----$ COMB12.TXT ( lines:15 words:15 ) --------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: あゝ無情/アン・ルイス
(icon_213.txt 1997/04/12 PCVAN PIG)

■  Icon > Icon雑記帳(14) ビデオ問題/組み合わせ 風つかい             目次

  ビデオ問題の習作プログラムの続きです。 前回の generatorから 受け取った
文字の組み合わせプログラムを、リスト形式データでも 受け取れるように 拡張
したものです。

-----^ COMB13.ICN ( date:97-04-12 time:10:31 ) -------------<cut here
# generatorからのデータの組み合わせ
# comb12.icnを、文字列だけでなくリストにも適用できるよう拡張
# 動作は、comb12.icnのコメントを参照して下さい。
# comb13.icn 1997/04/12 windy
# This file is in the public domain.

link show_l # list書き出し procedure

# データ xの要素を、1つずつ先頭から、順次 生成する generator
# x : ソースデータ
procedure gen_elem(x)
  suspend !x               # 要素を1つずつ取り出す
end

#          ↓データ

# データを順次 受け取り、組み合わせてリストにしたデータを
# 生成する generator
# proc1 : generator名称、x : ソースデータ
procedure glcombf(proc1,x)
  L := []   # ワーク用リスト(生成済みのリスト、を要素数別に格納)
  every new := proc1(x) do {    # 新たに要素を受け取る。
    put(L,[])                   # リスト L内に、空リスト追加
    every i := *L to 1 by -1 do {       # 要素数毎リストについて末尾から
      if i > 1 then {                   # 既に、要素があったら、新たな要素
        every j := 1 to *L[i -1] do {   # と組み合わせて、
          put(L[i], comb := (L[i -1][j] ||| [new]) )  # 要素数毎リストに追加
          #    ↑i要素数リスト ↑(i-1)要素数リスト
        suspend comb                    # 組み合わせで生成した要素を返す
        }
      }
      else {
        put(L[1],[new])         # 1要素リストに、新たに来た要素を追加
        suspend [new]           # 新たに来た要素を返す
      }
    }
  }
end

#          ↓データ

# ソースデータ設定/結果書き出し処理
procedure main()

  # 文字列データ
  x := "ABCD"
  every show_sl(glcombf(gen_elem,x))
  #     ↑1重リスト書き出し
  write()

  # リストデータ
  x := [["A1","A2","A3"],["B1","B2","B3"],["C1","C2","C3"]]
  every show_wl(glcombf(gen_elem,x))
  #     ↑2重リスト書き出し

end
-----$ COMB13.ICN ( lines:56 words:161 ) -------------------<cut here

  結果はこうなります。
-----^ COMB13.TXT ( date:97-04-12 time:10:54 ) -------------<cut here
 A
 A B
 B
 A B C
 A C
 B C
 C
 A B C D
 A B D
 A C D
 B C D
 A D
 B D
 C D
 D

 A1 A2 A3

 A1 A2 A3
 B1 B2 B3

 B1 B2 B3

 A1 A2 A3
 B1 B2 B3
 C1 C2 C3

 A1 A2 A3
 C1 C2 C3

 B1 B2 B3
 C1 C2 C3

 C1 C2 C3

-----$ COMB13.TXT ( lines:35 words:68 ) --------------------<cut here

  リスト内容表示のために、linkしている procedureです。
-----^ SHOW_L.ICN ( date:96-11-04 time:11:19 ) -------------<cut here
procedure show_sl(list)
# show_sl.icn Ver.1.1 1996/11/04   windy
# リストの内容表示(テスト/表示用)
  every writes(" ",!list)
  write()
  return
end

procedure show_wl(wlist)
# show_wl.icn Ver.1.1 1996/11/03   windy
# 2重リストの内容表示(テスト/表示用)
  every list := !wlist do {
    every writes(" ",!list)
    write("")
  }
  write("")
  return
end
-----$ SHOW_L.ICN ( lines:18 words:38 ) --------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: トライアングル・ブルー/アン・ルイス
(icon_214.txt 1997/04/12 PCVAN PIG)

■  Icon > Icon雑記帳(15) ビデオ問題/簡易解法   風つかい            目次

  ビデオテープへの録画詰め込み問題は、テープやMDに好きな音楽を詰め込ん
だり、トラックに荷物を詰め込んだり、生活していて実際にいろんな場面で遭遇
します。
  でも、実際的には、全ての組み合わせを試すのではなく、
          ・大きいものから、順に詰めていく。
          ・空きがあれば、なるべく大きいものを詰めてみる。
          ・空きに、入るものが無くなるまで試す。

  というようなやり方もよくやります。
  これで、最適といえなくとも、実用的には、そこそこの結果が、得られている
のでないかと、思います。このやり方ですと、比較的簡単にプログラムできます。
  しかし、Iconでは 多重 listデータ構造がサポートされていますが、無い言語
では 結構 面倒なプログラムになるかも。

  録画ビデオのデータ例を引用しておきます。題名と録画時間が行単位に書き込
まれているファイルです。
-----^ SOURCE.DAT ( date:97-03-23 time:19:38 ) -------------<cut here
ジャスティス 118
暴力脱獄 127
セルピコ 130
あなただけ今晩は 137
天国からきたチャンピオン 101
スタンドバイミー 85
フランキー&ジョニー 118
スティング 129
グッドモーニングベトナム 121
-----$ SOURCE.DAT ( lines:9 words:18 ) ---------------------<cut here

  これを、長い順にテープに詰めていくプログラムです。
-----^ PACK01.ICN ( date:97-04-13 time:10:57 ) -------------<cut here
# ビデオ問題 簡易解法
# 長いビデオから順に、詰めていく。
# pack01.icn Rev.1.3 1997/04/13 windy 録画時間の分を追加
# pack01.icn Rev.1.2 1997/04/13 windy コメント訂正・追加、等
# pack01.icn Rev.1.1 1997/04/12 windy
# This file is in the public domain.
link split, show_l

procedure main(args)
# ダビングテープ時間
  t_tape := 120                        # テープ時間(分)
  t_max  := t_tape *3                  # 3倍

  # ファイルオープン
  f_name := "source.dat" 
  dir := open(f_name) | stop("cannot open ",f_name)

# 録画データ読み込み
  L_data := []
  n := 0
  while line := read(dir) do {         # 録画データファイルの順次読込
     L := split(line)
     put(L_data,[n +:= 1,L[1],numeric(L[2])])
    # 行を spaceで分割し、先頭に Noを付加して、listに格納。
    # [1]:No [2]:題名, [3]:録画時間(分)(Rev.1.2)
  }
  close(dir)

  write("録画データ")                  # 録画データ書出し
  show_wl(L_data)

  # 録画時間で listを sort
  L_data2 := sortf(L_data,3)           # 3番目の fieldで sort

  write("録画データソ\ート結果")       # 録画データソート結果書出し
  show_wl(L_data2)                     # "ソ"は SJISで、835c

  i     := 0        # 詰め込みテープ番号
  L_out := []       # 詰め込みデータ格納 list
                    # [i]: テープ番号
                    # [i][1]: 詰め込み時間 (初期値 : [0,"分"])(Rev.1.3)
                    # listにするのは、show_tlで確認しやすくするため ↑
                    # [i][2〜]: 詰め込みデータ(list)

  while *L_data2 >0 do {   # 録画データが無くなる迄 (Rev.1.2)
    i +:= 1                # テープ番号を進める。(Rev.1.2)
    put(L_out,[[0,"分"]])  # i番目テープ詰め込み時間初期値を追加(Rev.1.3)
    every 1 to *L_data2 do {     # 録画データ全部に対して
      LL := pull(L_data2)  # 録画データ取り出し
                           # list末尾から試す。
                           #(昇順なので末尾の方が録画時間が長い。)
      if (L_out[i][1][1] +LL[3]) <= t_max then {
        put(L_out[i],LL)
        L_out[i][1][1] +:= LL[3]          # 録画時間更新
      } else {
        push(L_data2,LL)                  # データを戻す
      }
    } # end of every
  } # end of while


  write("詰め込みデータ(先頭は詰め込み時間総和)")
  show_tl(L_out)

end
-----$ PACK01.ICN ( lines:65 words:204 ) -------------------<cut here
  (修正のため差し替え)

  結果は、次のようになります。
-----^ PACK01.TXT ( date:97-04-13 time:10:57 ) -------------<cut here
録画データ
 1 ジャスティス 118
 2 暴力脱獄 127
 3 セルピコ 130
 4 あなただけ今晩は 137
 5 天国からきたチャンピオン 101
 6 スタンドバイミー 85
 7 フランキー&ジョニー 118
 8 スティング 129
 9 グッドモーニングベトナム 121

録画データソート結果
 6 スタンドバイミー 85
 5 天国からきたチャンピオン 101
 1 ジャスティス 118
 7 フランキー&ジョニー 118
 9 グッドモーニングベトナム 121
 2 暴力脱獄 127
 8 スティング 129
 3 セルピコ 130
 4 あなただけ今晩は 137

詰め込みデータ(先頭は詰め込み時間総和)
 352 分
 4 あなただけ今晩は 137
 3 セルピコ 130
 6 スタンドバイミー 85

 357 分
 8 スティング 129
 2 暴力脱獄 127
 5 天国からきたチャンピオン 101

 357 分
 9 グッドモーニングベトナム 121
 7 フランキー&ジョニー 118
 1 ジャスティス 118

-----$ PACK01.TXT ( lines:38 words:90 ) --------------------<cut here
  (修正のため差し替え)
  たまたまかも しれませんが、結構 うまく 詰まっていますね。

  3重リストを使いましたので、show_lに、その表示用 procedureを追加
しました。
-----^ SHOW_L.ICN ( date:97-04-12 time:21:07 ) -------------<cut here
# show elements of list
# This file is in the public domain.
# show_l.icn Rev.1.3 1997/04/12 windy  add show_tl
# show_l.icn Rev.1.1 1996/11/03 windy 

procedure show_sl(list)
# リストの内容表示(テスト/表示用)
  every writes(" ",!list)
  write()
  return
end

procedure show_wl(wlist)
# 2重リストの内容表示(テスト/表示用)
  every list := !wlist do show_sl(list)
  write()
  return
end

procedure show_tl(tlist)
# 3重リストの内容表示(テスト/表示用)
  every list := !tlist do show_wl(list)
  return
end
-----$ SHOW_L.ICN ( lines:24 words:60 ) --------------------<cut here

  う〜ん。今日は、易きに走ってしまった。
  だって、「しらみつぶし」方式って、大変なんだもん。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: 美人薄命/アン・ルイス
(icon_215.txt 1997/04/12 PCVAN PIG)

■  Icon > Icon雑記帳(16) ビデオ問題/難しい     風つかい               目次

  ビデオテープへの録画詰め込み問題の習作です。今回は、録画テープのデータ
の組み合わせのうち、1本のテープに入る組み合わせを、全て書き出すところ迄
をやってみました。

  プログラムは、録画データ読み込み部 (pack02.icn)と、実際に組み合わせを生
成する部分 (pack04.icn)の2つに分けてあります。

  まず、録画データ読み込み部分です。 main()はこちらにあります。
-----^ PACK02.ICN ( date:97-04-14 time:20:16 ) -------------<cut here
# ビデオ問題  録画データ設定/書出し
# pack01.icn から分離したもの(ただし、データNoをアルファベットに変更)
# pack02.icn Rev.1.1 1997/04/13 windy
# This file is in the public domain.
link split

procedure main(args)
# ダビングテープ時間
  t_tape := 120                  # テープ時間(分)
  t_max  := t_tape *3            # 3倍速で録画するとすると

  # ファイルオープン
  f_name := if *args = 0 then "source.dat"         # 録画データファイル名
            else args[1]
  # source.dat は、番組名 時間(分)が、行毎に書き込まれている形式
  dir := open(f_name) | stop("cannot open ",f_name)

# 録画データ読み込み
  L_data := []                   # 録画データ格納 list
  n := 0                         # data No
  while line := read(dir) do {   # 録画データファイルの順次読込
     L := split(line)            # 行を spaceで分割し listに変換
     put(L_data,[&letters[n +:= 1],L[1],numeric(L[2])])
    # 行を spaceで分割し、先頭に Noを付加して、L_data(list)に格納。
    # [1]:No [2]:題名, [3]:録画時間(分)
    # No はアルファベットを、順に振っている。"A"〜"Z","a"〜"z"の順
    # 
    # L_data は、9個の録画データがあるとすると
    # [ #↓L_data[1][1] #↓L_data[1][2] #↓L_data[1][3]
    #   ["A",1番目録画題名,1番目録画時間(分)]  # ←L_data[1]
    #   ["B",2番目録画題名,2番目録画時間(分)]  # ←L_data[2]
    # (中略)
    #   ["I",9番目録画題名,9番目録画時間(分)]  # ←L_data[9]
    # ] #↑L_data[9][1] #↑L_data[9][2] #↑L_data[9][3]
    # の形式となる。

  }
  close(dir)

  # 詰め込み/結果書出し
  pack(L_data,t_max)             # 詰め込み

end
-----$ PACK02.ICN ( lines:43 words:141 ) -------------------<cut here

  実際に組み合わせを作る部分です。 pack02.icnを linkしています。
-----^ PACK04.ICN ( date:97-04-14 time:22:28 ) -------------<cut here
# 1本のテープに入る録画データの組み合わせ
# This file is in the public domain.
# pack04.icn 1997/04/13 windy
link pack02, show_l   # main()は、pack02に含まれている。

# データ xの要素を、1つずつ先頭から、順次 生成する generator
# x: ソースデータ
procedure gen_elem(x)
  suspend !x               # 要素を1つずつ取り出す
end

#          ↓データ

# データを順次 受け取り、組み合わせてリストにしたデータを
# 生成する generator
procedure glcombf(proc1,x,t_max)
# proc1: generator名
# x: generatorのソースデータ
# t_max:テープ時間


  L := []   # ワーク用リスト(生成済みのリスト、を要素数別に格納)
  every new := proc1(x) do {    # 新たに要素を受け取る。
    put(L,[])                   # リスト L末尾に、新データ挿入用
                                # 空リスト追加
    every i := *L to 1 by -1 do {       # 要素数毎リストについて、末尾から
      if i > 1 then {           # 2つ目以降のデータの場合
                                # 既に、要素があったら、新たな要素
        every j := 2 to *L[i -1] do {   # と その要素を組み合わせて、
          if (t := (L[i -1][j][1][1] + new[3])) <= t_max then {
            LL :=  [[t,"分"]] ||| L[i -1][j][2:0] ||| [new]
            put(L[i],LL)  # 要素数毎リストに追加
            #    ↑i要素数リスト ↑(i-1)要素数リスト
          suspend LL                    # 組み合わせで生成した要素を返す
          } # end of if
        } # end of every
      } # end of if
      else {                    # 最初のデータの場合。
        LL := [[new[3],"分"]] ||| [new]
        #       ↑録画時間    ↑新要素
        put(L[1],LL)  # 1要素リストに、新たに来た要素を追加
        suspend LL    # 新たに来た要素を返す
      } # end of else
    } # end of if
  } # end of every
end

#          ↓データ

# pack02.icn の main()で、この pack()を呼んでいる。
procedure pack(L_data,t_max)
# L_data      # 録画データ list
              # [j][1]:データNo, [j][2]:録画名称, [j][3]:録画時間
  i     := 0  # 詰め込みテープ番号
  L_out := [] # 詰め込みデータ格納 list
              # i : テープ番号
              # [i][1] : 詰め込み時間 (初期値 : [0,"分"])
              # 時間をlistにするのは、show_tlで確認しやすくするため 

  write("録画データ")            # 録画データ書出し
  show_wl(L_data)                # 2重 list内容表示

  # 組み合わせデータ書出し
  every show_wl( # ↓every でこの generatorからデータを取り出し。
                 glcombf(gen_elem,L_data,t_max)
               )

  return       # このリターンは無くとも動きます。
               #( main()に値を返す構成ではないため)

end
-----$ PACK04.ICN ( lines:70 words:249 ) -------------------<cut here

  結果は、長いので省略します。この結果を更に組み合わせて、要素がだぶって
いるものを削除すれば、全ての組み合わせが得られます。(理論的には)(汗)

  しかし〜、組み合わせ結果の数が、
          当初の            9データで、    68通り
    それを2つくっつけた  18データで、  526通り
          3つくっつけた  27データで、1765通り
          4つくっつけた  36データで、4181通り
          5つくっつけた  45データで、8175通り
  と、元のデータ数に比べて、非常に多くなります。  このまた組み合わせの数
は、とんでもない数になりそうです。

  他に良いアルゴリズムが無いかと、ちょっと考えて見ましたが、思い付きませ
ん。

  という訳で、ビデオテープ問題は、当面(永久かもしれない。)棚上げにします。
  (くやしいな〜)

  でも、今回のプログラムを作ってみて、linkを使ったプログラム構成に関して、
1つ、私の勘違いが見つかりました。  今回のプログラムの構成は、

                  上位                下位
  - pack02.icn -------      link      - pack04.icn --------
 | procedure main()   | <----------- |link pack02          |
 | ....               |      ------->|procedure pack()     |
 | ....               |     / 呼ぶ   |                     |
 | pack() -----------------          |                     |
  --------------------                ---------------------
       ↓ icont -c pack02.icn             |
    pack02.u1            ----- link ----->| icont pack04.icn
    pack02.u2                             ↓
                                       pack04.exe

  と、いうものです。  すなわち、下位の procedureが、入っているファイルが、
上位の procedureが入っているファイルを linkしています。

  先に、 icont -c pack02.icnとしておいて、次に icont pack04.icnとして linkし
ます。実行ファイル名は、pack04.exeとなります。

  逆の、上位プログラムが下位プログラムを linkするやり方は、今までも使ってい
たのですが、こういう下位プログラムが上位プログラムを linkするやり方ができる
とは、思っていませんでした。

  データ読み込み部分は同じで、組み合わせ部分を、色々のやり方のプログラムに
変えられるといいな。  ということで、試してみましたら、できました。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: WOMAN/アン・ルイス
(icon_216.txt 1997/04/17 PCVAN PIG)

■  Icon > Icon雑記帳(17) 構造体 tree表示        風つかい               目次

  桜は、横浜では、もうすっかり葉桜になっていますが、確か札幌あたりでは、
桜は5月でしたかね。  桜前線を追いかける旅なんてのも、やってみたいもんで
すね〜。

  さて、今日は、構造体 (list,set,table)の tree表示をやってみます。
  Iconでは、listや setや tableの要素に、再び listや setや tableを 含む
ことができて、いくらでも 深い階層の構造を作ることできます。 Iconの重要
な特徴の1つだと、思います。

  しかし、私の頭では、2重 list位までは、頭に浮かべられますが、3重の
listともなると、なかなかサットは思い浮かびません。
  プログラムを書いたその時は、分かっているのですが、しばらく後になると、
もう自分の書いたプログラムの データ構造が、理解できなくなっています。

  コメントで、シッカリとデータ構造を書いておけば良いのですが、そのために
はプログラムの行より、コメントの行の方が、多すぎるくらいになります。
  コメントの間にプログラムが、ぽつぽつと存在するような状態になって、これ
また 読解性が、落ちることになります。

  ということで、階層構造を持つ構造体を tree状に表示するプログラムを作っ
てみました。

  Recursive generatorを使って、再帰的に データ構造を書出して いきます。
treeの最初の列の書出しが、再帰構造に含め切れませんでしてので、少し 分か
りにくくなってしまいました。
  また stringの内容表示 のところにも、image()という 表示可能な文字 なら
  そのまま 表示し そうで無ければ 16進数表示 を行う 関数を、使いたかった
のですが、シフトJIS対応では無いので、使っていません。
  この関数をシフトJIS対応にした procedureを、作らないといけませんね。

-----^ X2TREE01.ICN ( date:97-04-23 time:21:05 ) -----------<cut here
# 構造体の構成を tree状に見せる generator
# Recursive generator
# x2tree01.icn 1997/04/23 windy
# This file is in the public domain.

procedure x2t(x)
# generator
# x2treeに含めたかったが、うまくいかないので外出し。
  suspend "<" || type(x) || ">"      # xのタイプを書出し
  suspend "  " || x2tree(x)          # tree化処理
  #       ↑tree書出し初期位置
end

procedure x2tree(x)
# generator
# show the tree structure of x
  n := *x                   # xのサイズ(末尾要素検出用)
  every xx := !x do {       # 構造体の要素を1つづつ取り出す
    n -:= 1                 # 要素カウンタ−1
    case t := type(xx) of { # 構造体のタイプにより、
      "string"                 : suspend "+ " || left(t,7) || " " || xx
                                               # jimageを作らなきゃ。↑
      "list" | "set" | "table" : { # この場合は、再度構成要素を解析
                                   suspend "+ <" || t || ">"
                                   #↓最後の要素でないなら
                                   if n > 0 then suspend "|   " || x2tree(xx)
                                            else suspend "    " || x2tree(xx)
                                 }
      #↑list,set,tableなら、typeを書出して、その中身をまた解析する。

      default                  : suspend "+ " || left(t,7) || " " || image(xx)
      # ↑その他なら、typeと値を書き出す。
    }
  }
end

# test driver
procedure main()
  L := ["A",[],"B",10.5,["C","D",set(["X","Y"]),["E","F",127],"G"],"H"]
  every write(x2t(L))
end
-----$ X2TREE01.ICN ( lines:41 words:157 ) -----------------<cut here

  x2tree01 > x2tree01.txtとした時の結果です。
-----^ X2TREE01.TXT ( date:97-04-23 time:00:30 ) -----------<cut here
<list>
  + string  A
  + <list>
  + string  B
  + real    10.5
  + <list>
  |   + string  C
  |   + string  D
  |   + <set>
  |   |   + string  X
  |   |   + string  Y
  |   + <list>
  |   |   + string  E
  |   |   + string  F
  |   |   + integer 127
  |   + string  G
  + string  H
-----$ X2TREE01.TXT ( lines:17 words:60 ) ------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: The Other Side of Love / Ryuichi Sakamoto featuring Sister M
(icon_217.txt 1997/04/27 PCVAN PIG)

■  Icon > Icon雑記帳(18) jimage.icn             風つかい              目次

  近ごろ書く プログラムには、殆ど generatorを 使っています。つい先頃まで
は、この generatorを、どういう風に使ったら 良いのか 分かりませんでした。
  今は、便利なんで、使えない所以外は、使ってしまう。 という状態です。

  listは、Icon教典 (The Icon Programming Language)を読んだだけで、これは
便利だ と分かりました。
  しかし、generatorの便利さは、使っているとジワジワと効いてくる。 なんつ
うか、漢方薬の効能書きみたいな良さです。
  使ってみないと、わかりませんでしたが、なかなかのスグレモノです。

  こういう良さは、どう説明していけばいいんでしょうかね。やっぱり、実例を
沢山書いていくしかないんでしょうね〜。

  というところで、今回は image()関数を SHIFT-JIS対応にした procedureを作
ってみます。  SHIFT-JISの文字列を解析する procedureは、Icon日記で 作って
いますので、比較的簡単に、SHIFT-JIS対応にできます。

  プログラムを次にあげます。  データタイプが string以外では、image()を、
そのまま使っています。

-----^ JIMAGE.ICN ( date:97-04-27 time:19:37 ) -------------<cut here
# image()を SHIFT-JIS対応にしたもの
# jimage.icn 1997/04/27 windy
# This file is in the public domain.

link hexcvt,sjis4
# hexcvt.icnは BIPLに含まれてる。  Icon日記(14)に引用しています。
# sjis4.icnは Icon日記(28)にて作成したもの。

procedure jimage(x)
  if not (type(x) == "string") then return image(x) # string以外なら
                                                    # image()を使う。
  else {                                            # stringなら
    s := "\""         # 戻り値足しこみ用の空 string
    every L := jcpos(x) do {
      if L[3] == ("C" | "U")  #コントロールコードか未定義文字なら
      then s ||:= ("\\x" || hexstring(ord(L[2]),2))
                           #↑16進表示に変換して "\\"をつけて足し込み
      else s ||:= L[2]     #← 文字ならそのまま足しこみ
    }
    return s || "\""
  }
end

# test data
#procedure main()
#  L := ["ABC","\x00\x02",123,12.5,"あいうアイウabcABCABCabc012"]
#  every write(jimage(!L))
#end
-----$ JIMAGE.ICN ( lines:28 words:95 ) --------------------<cut here

  jimage()を x2treeに使うと、次のようになります。
-----^ X2TREE02.ICN ( date:97-04-27 time:19:32 ) -----------<cut here
# 構造体の構成を tree状に見せる generator
# Recursive generator
# x2tree02.icn 1997/04/27 windy
# x2tree01.icn の main()をコメントアウトし、jimage()を導入。
# This file is in the public domain.
link jimage
procedure x2t(x)
# generator
# x2treeに含めたかったが、うまくいかないので外出し。
  suspend "<" || type(x) || ">"
  suspend "  " || x2tree(x)
end

procedure x2tree(x)
# generator
# show the tree structure of x
  n := *x
  every xx := !x do {       # 構造体の要素を1つづつ取り出す
    n -:= 1
    case t := type(xx) of { # 構造体のタイプにより、
      "string"               : suspend "+ " || left(t,7) || " " || jimage(xx)
      "list" | "set" | "table" : {
                                   suspend "+ <" || t || ">"  
                                   if n > 0 then suspend "|   " || x2tree(xx)
                                            else suspend "    " || x2tree(xx)
                                 }                                 # 再帰↑
      #↑list,set,tableなら、typeを書出して、その中身をまた解析する。  

      default                : suspend "+ " || left(t,7) || " " || image(xx)
      # ↑その他なら、typeと値を書き出す。
    }
  }
end

# test driver
#procedure main()
#  L := ["A",[],"B",10.5,["C","D",set(["X","Y"]),["E","F",127],"G"],"H"]
#  every write(x2t(L))
#end
-----$ X2TREE02.ICN ( lines:39 words:152 ) -----------------<cut here

  SHIFT-JIS対応にした、x2treeで、ビデオ詰め込みプログラムの簡易解法で使って
いる listの構造を書き出すと、次のようになります。
  詰め込みが終わった状態を書出しています。 前のバージョンとの違いは、string
が、""で括られてくるだけです。(汗)
-----^ PACK05.TXT ( date:97-04-27 time:19:38 ) -------------<cut here
詰め込みデータ
<list>
  + <list>
  |   + <list>
  |   |   + integer 352
  |   |   + string  "分"
  |   + <list>
  |   |   + integer 4
  |   |   + string  "あなただけ今晩は"
  |   |   + integer 137
  |   + <list>
  |   |   + integer 3
  |   |   + string  "セルピコ"
  |   |   + integer 130
  |   + <list>
  |       + integer 6
  |       + string  "スタンドバイミー"
  |       + integer 85
  + <list>
  |   + <list>
  |   |   + integer 357
  |   |   + string  "分"
  |   + <list>
  |   |   + integer 8
  |   |   + string  "スティング"
  |   |   + integer 129
  |   + <list>
  |   |   + integer 2
  |   |   + string  "暴力脱獄"
  |   |   + integer 127
  |   + <list>
  |       + integer 5
  |       + string  "天国からきたチャンピオン"
  |       + integer 101
  + <list>
      + <list>
      |   + integer 357
      |   + string  "分"
      + <list>
      |   + integer 9
      |   + string  "グッドモーニングベトナム"
      |   + integer 121
      + <list>
      |   + integer 7
      |   + string  "フランキー&ジョニー"
      |   + integer 118
      + <list>
          + integer 1
          + string  "ジャスティス"
          + integer 118
-----$ PACK05.TXT ( lines:50 words:185 ) -------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: Vivien/篠原美也子
(icon_218.txt 1997/04/27 PCVAN PIG)

■  Icon > Icon雑記帳(19) 構造体 tree表示(2)  風つかい         目次

  Iconの構造体(list,set,table)は、その要素にまた list,set,tableを持てます
が、自分自身をその要素に持つこともできます。 再帰的な構造 とでも いいまし
ょうか。  どんな場合に使うかというと、例えば再帰構成のプログラムの構造を、
表わそうと すると、再帰的な構造体が必要になります。

  前に作った tree表示のプログラム(x2tree01.icn, x2tree02.icn)は、そういう
構造体の場合でも、素直に表示していきますので、延々 treeを書き出します。

  サンプルプログラムを次に示します。
  延々書き出されても困るので、generatorの生成数限定指定で、20個迄として
います。
-----^ RECUR01.ICN ( date:97-04-30 time:09:09 ) ------------<cut here
# 再帰を含む構造の tree書出し
# recur01.icn 1997/04/30 windy
# This file is in the public domain.
link x2tree02  #←再帰未対応版

procedure main()
  L1 := ["A","B"]
  L2 := ["C","D"]
  L3 := ["E","F"]

  put(L1,L2)      # L1の末尾に L2を追加
  put(L2,L3)      # L2の末尾に L3を追加
  put(L1,L3)      # L1の末尾にも L3を追加
  put(L3,L1)      # L3の末尾に 自分の上位の L1を追加(ここで再帰となる。)
  every write(x2t(L1) \20)
                      #↑テストをミスしたときに、20個で止めるため。
end
-----$ RECUR01.ICN ( lines:17 words:51 ) -------------------<cut here

  recur01 >recur01.txtとした時の結果です。
-----^ RECUR01.TXT ( date:97-04-30 time:09:18 ) ------------<cut here
<list>
  + string  "A"
  + string  "B"
  + <list>
  |   + string  "C"
  |   + string  "D"
  |   + <list>
  |       + string  "E"
  |       + string  "F"
  |       + <list>
  |           + string  "A"
  |           + string  "B"
  |           + <list>
  |           |   + string  "C"
  |           |   + string  "D"
  |           |   + <list>
  |           |       + string  "E"
  |           |       + string  "F"
  |           |       + <list>
  |           |           + string  "A"
-----$ RECUR01.TXT ( lines:20 words:75 ) -------------------<cut here

  という訳で、再帰構造の場合は、再帰(Recursion)と表示して そこで treeが
打止めとなるように x2treeを 修正してみました。

  Iconでは、構造体は listで実現しています。 そして listの番号を得る関数
serial()があります。
  この関数を使って、list番号を集合 Sに登録しておいて、既に現れた listが
再び要素として現れたなら再帰と判断しています。

  何処から何処まで再帰するのか分かるように、listの番号も表示します。

  また、複数の枝に同じ listが含まれる場合もありますので、自分より基の
方にある listのみ集合 Sに存在するするように、再帰からの戻りの時点で、
番号を削除しています。(suspend ... do ... のところ。)
-----^ X2TREE03.ICN ( date:97-05-01 time:10:17 ) -----------<cut here
# 構造体の構成を tree状に見せる generator
# Recursive generator
# x2tree03.icn Rev.1.1 1997/04/30 windy Rev.1.2 1997/05/01 windy
# x2tree02.icn を Recursive構造体(自分自身を要素に含む)対応修正。
# This file is in the public domain.
link jimage
procedure x2t(x)
# generator
# x2treeに含めたかったが、うまくいかないので 外出し。
  suspend "<" || type(x) || " " || string(serial(x)) || ">"
                                         #↑listの serial No
  suspend "  " || x2tree(x)
end

procedure x2tree(x)
# generator
# show the tree structure of x
  static S                  # serial No 登録用
  initial {
    S := set([serial(x)])   # serial Noを Sに登録
  }
  n := *x                   # 枝の末端検出用
  every xx := !x do {       # 構造体の要素を1つ づつ取り出す
    n -:= 1
    case t := type(xx) of { # 構造体のタイプにより、
      "string"            : suspend "+ " || left(t,7) || " " || jimage(xx)
      #↓list,set,tableなら、typeを書出して、その中身をまた解析する。
      "list" | "set" | "table" : {
        if not member(S,m := serial(xx)) then { # 再帰構造で なければ
                                                # Sに既に登録済みなら再帰構造
          insert(S,m)       # serial Noを Sへ登録
          suspend "+ <" || t || " " || string(m) || ">"
          suspend (if n > 0 then "|   " else "    ") ||   # Rev.1.2 if...then
                            #↑枝の途中 #↑枝の末端       # else...の冗長修正
                  x2tree(xx) do delete(S,m)
                  # 再帰↑   #↑登録を削除
        }
        else {  # 再帰構造 ならば、書き出すとキリが無いので打止め
          return "+ <" || t || " " || string(m) || "> (Recursion)"
        }
      }
      #↓その他なら、typeと値を書き出す。
      default             : suspend "+ " || left(t,7) || " " || image(xx)
    }
  }
end

# test driver
#procedure main()
#  L1 := ["A","B"]
#  L2 := ["C","D"]
#  L3 := ["E","F"]
#  put(L1,L2)
#  put(L2,L3)
#  put(L1,L3)               # suspend doが働いているかのテスト
#  put(L3,L1)               # Recursiveの stopperが働いているかのテスト
#  every write(x2t(L1) \20) # \20は、テストをドジッた時に、20個で止めるため。
#end
-----$ X2TREE03.ICN ( lines:58 words:257 ) -----------------<cut here
(修正版に差し替え)

  recur01.icnの link指定を変えただけのものです。
-----^ RECUR02.ICN ( date:97-04-30 time:09:43 ) ------------<cut here
# 再帰を含む構造の tree書出し
# recur02.icn 1997/04/30 windy
# This file is in the public domain.
link x2tree03  #←再帰対応版

procedure main()
  L1 := ["A","B"]
  L2 := ["C","D"]
  L3 := ["E","F"]

  put(L1,L2)      # L1の末尾に L2を追加
  put(L2,L3)      # L2の末尾に L3を追加
  put(L1,L3)      # L1の末尾に L3を追加
  put(L3,L1)      # L3の末尾に 自分の上位の L1を追加(ここで再帰となる。)
  every write(x2t(L1) \20)
                      #↑テストをミスしたときに、20個で止めるため。
end
-----$ RECUR02.ICN ( lines:17 words:51 ) -------------------<cut here
(修正版に差し替え)

  recur02 >recur02.txtとした時の結果です。
-----^ RECUR02.TXT ( date:97-05-01 time:10:07 ) ------------<cut here
<list 1>
  + string  "A"
  + string  "B"
  + <list 2>
  |   + string  "C"
  |   + string  "D"
  |   + <list 3>
  |       + string  "E"
  |       + string  "F"
  |       + <list 1> (Recursion)
  + <list 3>
      + string  "E"
      + string  "F"
      + <list 1> (Recursion)
-----$ RECUR02.TXT ( lines:14 words:49 ) -------------------<cut here

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: 河よりも長くゆるやかに/篠原美也子
(icon_219.txt 1997/04/30 PCVAN PIG)

■  Icon > Icon雑記帳(20) dir2tree(1)         風つかい            目次

  せっかく、tree表示のプログラムを 書きましたので、以前から やりたかった
directoryの tree表示もやってみましょう。
  MS-DOSの、dir /s コマンドで、directory情報を 再帰的に 得ること が でき
ます。 この情報を tree表示してみます。

  MS-DOSの、dir /s で、次のようなデータが得られます。  sub-directory内の
fileや sub-directoryの情報が、順次でてきます。
  d:\icon\lec24が current directoryの時に dir /s test_a とした 時の 結果
です。
-----^ TEST_A_D.TXT ( date:97-05-03 time:00:33 ) -----------<cut here

 ドライブ D: のボリュームラベルはありません.
 ボリュームシリアル番号は 2727-1AF8

ディレクトリは D:\ICON\LEC24\TEST_A

.              <DIR>        97-04-29  21:51 .
..             <DIR>        97-04-29  21:51 ..
TEST_B         <DIR>        97-04-29  21:52 TEST_B
BBB      TXT            10  97-04-29  21:56 BBB.TXT
TEST_CCC TXT            15  97-04-29  21:57 TEST_CCC.TXT
TEST_C         <DIR>        97-04-29  21:55 TEST_C
AAA                      6  97-04-29  21:56 AAA
         3 個                  31 バイトのファイルがあります 

ディレクトリは D:\ICON\LEC24\TEST_A\TEST_B

.              <DIR>        97-04-29  21:52 .
..             <DIR>        97-04-29  21:52 ..
HHH      TXT            10  97-04-29  21:59 HHH.TXT
TEST_D         <DIR>        97-04-29  21:52 TEST_D
TEST_III TXT            15  97-04-29  22:00 TEST_III.TXT
GGG                      6  97-04-29  21:59 GGG
         3 個                  31 バイトのファイルがあります 

ディレクトリは D:\ICON\LEC24\TEST_A\TEST_B\TEST_D

.              <DIR>        97-04-29  21:52 .
..             <DIR>        97-04-29  21:52 ..
KKK      TXT            10  97-04-29  22:01 KKK.TXT
TEST_LLL TXT            15  97-04-29  22:02 TEST_LLL.TXT
JJJ                      6  97-04-29  22:01 JJJ
         3 個                  31 バイトのファイルがあります 

ディレクトリは D:\ICON\LEC24\TEST_A\TEST_C

.              <DIR>        97-04-29  21:55 .
..             <DIR>        97-04-29  21:55 ..
EEE      TXT            10  97-04-29  21:58 EEE.TXT
TEST_FFF TXT            15  97-04-29  21:58 TEST_FFF.TXT
DDD                      6  97-04-29  21:58 DDD
         3 個                  31 バイトのファイルがあります 

一覧のファイル総数:
        12 個                 124 バイトのファイルがあります 
        11 ディレクトリ      3,391,488 バイトの空きがあります 
-----$ TEST_A_D.TXT ( lines:46 words:161 ) -----------------<cut here

  この情報を sub-directory毎に、順次 listにまとめて、更に、その listを
親子関係に従って、つないでいく方式とします。

  いきなり、全部の programを 作ろうとしたのですが、わけわかめに なって
しまいましたので、簡単な実験 programを先に作ってみることにしました。

-----^ TEST220B.ICN ( date:97-05-03 time:00:01 ) -----------<cut here
# dir2tree習作
# MS-DOSの "dir /s"が生成する sub-directory毎の情報を listに格納しておき
# その listを linkしていくやり方の実験。linkのための情報は sub-directory
# 情報に含まれているが、どうやって linkをするか ということで、 tableを使
# って listの要素の位置を覚えておく方式の実験を行った。 これでイケそう。
# 実際のプログラムは、loop構成にする。
# test220b.icn 1997/05/02 windy
# This file is in the public domain.
link x2tree03              # tree表示

procedure main()
  T := table()
  L := []
  # top directory "D0" 登録の模擬
  LL := []                 # directory data buffer
  s := "D1"                # directory name情報
  LLL := []                # sub-directory data buffer
  put(LLL,s)               # sub-directoryの模擬
  put(LL,"D0","A0",LLL,"A1") # sub-directory要素の LLへの登録
  put(L,LL)                # 登録
  insert(T,s,LLL)          # table登録 LLLの場所を覚えておくため

  # 中間の directory "D1" 登録の模擬
  LL := []                 # directory data buffer
  s := "D2"                # directory name情報
  LLL := []                # sub-directory data buffer
  put(LLL,s)               # sub-directoryの模擬

  put(LL,"D1","B0",LLL,"B1") # sub-directory要素の LLへの登録
  put(\T[LL[1]] | L,LL)    # 登録

  insert(T,s,LLL)          # table登録 LLLの場所を覚えておくため

  # 末端の directory "D2" 登録の模擬
  LL := []                 # directory data buffer
  put(LL,"D2","C0","C1")   # sub-directory要素の LLへの登録
  put(\T[LL[1]] | L,LL)    # 登録

  write("L")
  every write(x2t(L) \20)
  write("T")
  every write(x2t(T) \20)
                     #↑テストをミスしたときに、20個で止めるため。
end
-----$ TEST220B.ICN ( lines:43 words:159 ) -----------------<cut here

  結果は次のようになります。  なんとか、つながったみたいです。
  データ構造の表示 programは、結構役に立ちますね。(自画自賛)
-----^ TEST220B ( date:97-05-03 time:00:02 ) ---------------<cut here
L
<list 1>
  + <list 2>
      + string  "D0"
      + string  "A0"
      + <list 3>
      |   + string  "D1"
      |   + <list 4>
      |       + string  "D1"
      |       + string  "B0"
      |       + <list 5>
      |       |   + string  "D2"
      |       |   + <list 6>
      |       |       + string  "D2"
      |       |       + string  "C0"
      |       |       + string  "C1"
      |       + string  "B1"
      + string  "A1"
T
<table 1>
  + <list 5>
  |   + string  "D2"
  |   + <list 6>
  |       + string  "D2"
  |       + string  "C0"
  |       + string  "C1"
  + <list 3>
      + string  "D1"
      + <list 4>
          + string  "D1"
          + string  "B0"
          + <list 5>
          |   + string  "D2"
          |   + <list 6>
          |       + string  "D2"
          |       + string  "C0"
          |       + string  "C1"
          + string  "B1"
-----$ TEST220B ( lines:38 words:134 ) ---------------------<cut here

  続きは、次回に。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: La Fiesta / Chick Corea
(icon_220.txt 1997/05/03 PCVAN PIG)

■  Icon > Icon雑記帳(21) dir2tree(2)         風つかい           目次

  directoryの tree表示 programの続きです。  実験が、うまくいきましたので
次は、実際の programです。
  ・dir /s のコマンド発行、結果の取り入れ
  ・結果の listへの変換、親子関係に従って link
  ・結果の表示(別の procedure)
の構成になっています。

-----^ DIR2TREE.ICN ( date:97-05-03 time:13:22 ) -----------<cut here
# ディレクトリーを tree表示する。
# dir2tree.icn Ver.1.1 1997/05/03 windy 1997/05/03 Ver1.2 windy コメント追加
# This file is in the public domain.
# Usage : dir2tree directory_name
# "dir /s" を発行して 得た ディレクトリー情報を tree 表示する。
#link x2tree04      # test用

procedure main(args)
  #######################
  # directory情報を得る。
  #######################
  dir_name := if *args > 0 then args[1] else ""
  tmp   := "$$$tmp.$$$"        # テンポラリファイル名設定
  # ↓環境変数に、テンポラリーディレクトリ名が、設定してあればファイル名
  #   の前に追加
  if s := getenv("TMP") then tmp := s || "\\" || tmp
  # コマンドラインの ディレクトリー名指定 をもとに、"dir /s" コマンド を
  # 発行して tmpへ出力
  system("dir /s " || dir_name || " >" || tmp)

  # directory data sample ↓こんなデータが tmpに格納されます。
  #1              16
  #ディレクトリは D:\ICON\LEC24\TEST_A
  #
  #.              <DIR>        97-04-29  21:51 .
  #..             <DIR>        97-04-29  21:51 ..
  #TEST_B         <DIR>        97-04-29  21:52 TEST_B
  #BBB      TXT            10  97-04-29  21:56 BBB.TXT
  #TEST_CCC TXT            15  97-04-29  21:57 TEST_CCC.TXT
  #TEST_C         <DIR>        97-04-29  21:55 TEST_C
  #AAA                      6  97-04-29  21:56 AAA
  #         3 個                  31 バイトのファイルがあります 
  #  
  #一覧のファイル総数:

  ######################
  # dir /s (dir_name)結果から、directory毎の情報を listへ 登録
  ######################
  dir := open(tmp) | stop("cannot open " || tmp)    # tmpファイルオープン
  L_dir := []               # directory情報登録用 list
  T_dir := table()          # sub-directory list参照 table

  while line := read(dir) do {
    # directory情報を directory毎に、一旦 list(LL)にまとめて、登録する。
    line ? {
                        #↓listにまとめた directory情報を登録
      if find("個") then put(\T_dir[c_dir] | L_dir,LL) 
                            #↑tableに c_dirが存在すれば
      if match(" ") then next          # ごみ行を無視
      if match("一") then break        # "一覧の..."が来たら終わり
      L := p_split()                   # lineを " "で分割→[1]:開始位置
                                       #                   [2]:要素
           #↓lineの1番目の要素
      case L[1][2] of {
        "ディレクトリは" : { LL := []  # directory data buffer
                                       #  ↓lineの2番目の要素の開始位置
                             c_dir := line[L[2][1] : 0]
                             put(LL,[c_dir])
                             #↑listにして LLに追加
                           }
        "."        : next              # 無視
        ".."       : put(LL[1],line)   # その directory自身の情報。
                                       # トップの directory以外では必要
                                       # ないが、全てにつき格納
        default    : { if find("<DIR>")  # sub-directoryだったら
                       then {
                         put(LL,LLL := [line]) # 内容を bufferに追加
                         insert(T_dir,c_dir || "\\" || L[1][2] ,LLL)
                         # ↑sub-directory名称を keyにして tableへ登録
                       }
                       else put(LL,line) # 通常 fileならそのまま追加
                     }
      } # end of case
    } # end of line ?
  } # end of while
  close(dir)
  remove(tmp)                          # delete tmp

  #######################
  # directory情報の表示
  #######################
#  every write(x2t(L_dir))              # list内容確認用表示
#  stop()
  every write(d2t(L_dir))

end

########################
# directory tree表示
########################
procedure d2t(L)
# top directoryの名前/time_stamp処理
  L1 := get(L)    # Lには1個しか要素が無い。それを取り出す。
  L2 := get(L1)   # そのまた top (directory_name,time_stamp)を取り出す。

  suspend L2[1] || L2[2][14:-3]   # top directoryの名前と time stamp
  suspend "  "  || d2tree(L1)     # tree化処理
  #       ↑tree書出し初期位置
end

procedure d2tree(L)
# directoryの処理
# show the tree structure of directory
  n := *L                   # Lのサイズ(末尾要素検出用)
  every LL := !L do {       # listの要素を1つづつ取り出す
    n -:= 1                 # 要素カウンタ−1
    case t := type(LL) of { # 要素のタイプにより、
      "string"  : suspend "+ " || LL        # file
      "list"    : {                         # sub-directory
        suspend "+ " || get(LL)             # sub-directory自体表示
        LLL := get(LL)                      # sub-directoryの中身を取り出し
        get(LLL)                            # 先頭要素を読み飛ばし
                #↓最後の要素でないなら
        suspend (if n > 0 then "|   " else "    ") || d2tree(LLL)
      }
      default   : stop("error")
    }
  }
end

########################
# stringを cを区切りにして分割して、開始位置と要素を返す。
########################
# 動作は、Icon入門講座(7) Iconの特徴(5)スキャンを参考にして下さい。
procedure p_split(line,c)
  /c := ' \t'                               # default
  /line := &subject                         # default
  list := []                                # 戻り値用 list
  line ? {
    while tab(ps := upto(~c)) do
      put(list,[ps,tab(pe := many(~c))])
  }
  return list
end
-----$ DIR2TREE.ICN ( lines:134 words:505 ) ----------------<cut here
(コメント追加のため差し替え)

  長くなりましたので、結果は次回に。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: La Fiesta / Chick Corea
(icon_221.txt 1997/05/03 PCVAN PIG)

■  Icon > Icon雑記帳(22) dir2tree(3)         風つかい              目次

  directoryの tree表示 programの続きです。  sub-directory毎の listを連結
しますと、次のような listができます。
  前回の program中で、コメントアウトしてある list内容表示を 生かしたとき
の結果です。    尚、test時に linkする x2tree04は、x2tree03で 階層毎の 列
の進みを 縮める修正をしたものです。
  この listの中には 冗長な情報が ありますので 表示programで 削って 表示し
ます。
-----^ TEST_A_L.TXT ( date:97-05-03 time:11:27 ) -----------<cut here
<list 2>
 +<list 8>
   +<list 9>
   | +string  "D:\ICON\LEC24\TEST_A"
   | +string  "..             <DIR>        97-04-29  21:51 .."
   +<list 29>
   | +string  "TEST_B         <DIR>        97-04-29  21:52 TEST_B"
   | +<list 61>
   |   +<list 62>
   |   | +string  "D:\ICON\LEC24\TEST_A\TEST_B"
   |   | +string  "..             <DIR>        97-04-29  21:52 .."
   |   +string  "HHH      TXT            10  97-04-29  21:59 HHH.TXT"
   |   +<list 89>
   |   | +string  "TEST_D         <DIR>        97-04-29  21:52 TEST_D"
   |   | +<list 107>
   |   |   +<list 108>
   |   |   | +string  "D:\ICON\LEC24\TEST_A\TEST_B\TEST_D"
   |   |   | +string  "..             <DIR>        97-04-29  21:52 .."
   |   |   +string  "KKK      TXT            10  97-04-29  22:01 KKK.TXT"
   |   |   +string  "TEST_LLL TXT            15  97-04-29  22:02 TEST_LLL.TXT"
   |   |   +string  "JJJ                      6  97-04-29  22:01 JJJ"
   |   +string  "TEST_III TXT            15  97-04-29  22:00 TEST_III.TXT"
   |   +string  "GGG                      6  97-04-29  21:59 GGG"
   +string  "BBB      TXT            10  97-04-29  21:56 BBB.TXT"
   +string  "TEST_CCC TXT            15  97-04-29  21:57 TEST_CCC.TXT"
   +<list 50>
   | +string  "TEST_C         <DIR>        97-04-29  21:55 TEST_C"
   | +<list 146>
   |   +<list 147>
   |   | +string  "D:\ICON\LEC24\TEST_A\TEST_C"
   |   | +string  "..             <DIR>        97-04-29  21:55 .."
   |   +string  "EEE      TXT            10  97-04-29  21:58 EEE.TXT"
   |   +string  "TEST_FFF TXT            15  97-04-29  21:58 TEST_FFF.TXT"
   |   +string  "DDD                      6  97-04-29  21:58 DDD"
   +string  "AAA                      6  97-04-29  21:56 AAA"
-----$ TEST_A_L.TXT ( lines:35 words:195 ) -----------------<cut here

  冗長な情報を削った結果はこうなります。
-----^ TEST_A_T.TXT ( date:97-05-03 time:11:29 ) -----------<cut here
D:\ICON\LEC24\TEST_A  <DIR>        97-04-29  21:51
  + TEST_B         <DIR>        97-04-29  21:52 TEST_B
  |   + HHH      TXT            10  97-04-29  21:59 HHH.TXT
  |   + TEST_D         <DIR>        97-04-29  21:52 TEST_D
  |   |   + KKK      TXT            10  97-04-29  22:01 KKK.TXT
  |   |   + TEST_LLL TXT            15  97-04-29  22:02 TEST_LLL.TXT
  |   |   + JJJ                      6  97-04-29  22:01 JJJ
  |   + TEST_III TXT            15  97-04-29  22:00 TEST_III.TXT
  |   + GGG                      6  97-04-29  21:59 GGG
  + BBB      TXT            10  97-04-29  21:56 BBB.TXT
  + TEST_CCC TXT            15  97-04-29  21:57 TEST_CCC.TXT
  + TEST_C         <DIR>        97-04-29  21:55 TEST_C
  |   + EEE      TXT            10  97-04-29  21:58 EEE.TXT
  |   + TEST_FFF TXT            15  97-04-29  21:58 TEST_FFF.TXT
  |   + DDD                      6  97-04-29  21:58 DDD
  + AAA                      6  97-04-29  21:56 AAA
-----$ TEST_A_T.TXT ( lines:16 words:115 ) -----------------<cut here

  この programだけで、連休がつぶれてしまった。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: La Fiesta / Chick Corea
(icon_222.txt 1997/05/03 PCVAN PIG)

■  Icon > Icon雑記帳  むすび                        風つかい             目次

  雑記帳では、テキスト解析をやろうと思っていたのですが、殆ど generatorの
解説になってしまいました。
  4月も終わったことですので、雑記帳は おしまいとします。(全然、説明に
なっていない。(汗))

  今シリーズでは、私は generatorサルになってしまいました。  前シリーズの
Icon日記で generatorの使い心地の良さを 知ってしまってから、もう 離れられ
ません。
  【イケイケ評価】(Goal-directed evaluation)の威力は、generatorを 使って
みないと分かりませんね。

  会話に使う言葉にしても、その地方地方の 言葉(方言)でないと 表わせない
ものって ありますよね。
  私の母国語の博多弁では、他の方言にはみかけない「進行形」の表現がありま
す。  「〜よる。」という表現です。
  「行きよる。」= 行きつつある。行く途中である。
  この表現が使いこなせると、あなたも、博多弁の達人です。(笑)

  Iconにも そういう独特の【Iconでなければ表わせない】表現が あると思いま
す。これは、ある程度、そのプログラムに、はまってみないと、分からない感覚
だと思います。

  さて、Icon山の周りの茂みを さまよって、なかなか テキスト解析への登り道
へ たどりつけませんが、次のシリーズでは、是非、テキスト解析を やってみま
しょう。

  次のシリーズのタイトルは、Icon散歩道 としようかと思います。 Icon迷い道
の方が合っていると思いますが、それじゃあんまりですので。(笑)

  では、また、お会いする日まで。

風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp)
< IconのWWWは、http://www.cs.arizona.edu/icon/index.html>
BGM: いとおしいグレイ/篠原美也子
BGD: お〜い お茶
(icon_223.txt 1997/05/03 PCVAN PIG)