作者: Hiroshi Shinohara
日時: 2003/10/18(13:59)
  Iconには、generatorという データを次々に生成して、そのデータストリームを
処理していくしかけがあります。

  前回は generatorを 次々につないでいけることを確認しました。
  <strg>          <left>          <lineg>
  +----------+    +----------+    +----------+
  |データ生成| => |フィルター| => |フィルター| => 結果ファイル
  +----------+    +----------+    +----------+
                                   ↑一行バッファー

  最終的には、こんなかっこうになりました。

           # ↓1行整形procedure ↓順列生成procedure     番兵↓(順列生成の終了)
  every s := lineg(n_limit,,left(strg(s_seed,n_char),n_char) | &null)
    do write(s)            # ↑左寄せ桁合わせ関数

  (generatorではない)通常の procedureのように、引数に指定できて、先頭に every
を置けば、各 generatorが発生するデータを、シラミつぶしに試してくれます。

  でも、引数がこんなにネストすると、後で見ると ウワッ! どう区切るのだろう?
目まいが...となってしまいます。

  そこで、分けて書けないかと試してみました。  色々試してみて、括弧式を使うと
分けて書けることが分かりました。  サンプルプログラムはこんなんです。
-----^ GEN05.ICN ( date:03-10-17 time:21:57 ) --------------<cut here
####################
# generatorの使い方
####################
# gen05.icn Rev.1.1 2003/10/17 風つかい
# test03の引数を分離
# This file is in the public domain.

procedure main()
  every ( x  := !"abc", #↓        "a","b","c"を順次生成
          y  := !"ABC", #↓↓      "A","B","C"を順次生成
          s3 := test03(   x,y) )  # x || y と y || xを順次生成
    do writes(s3," ")
  write()
end


# s1とs2の連結 と s2とs1の連結 を生成する generator
# 例: test03("x","y") -> "xy","yx"を生成
procedure test03(s1,s2)
  suspend (s1 || s2) | (s2 || s1)
end
-----$ GEN05.ICN ( lines:21 words:73 ) ---------------------<cut here

  括弧式は、括弧内の全項のアンド条件をとりますので、everyのシラミつぶし動作も
全項についてやってくれる訳です。

  動作結果は、以前の動作結果と同じです。
-----^ GEN05.1 ( date:03-10-17 time:22:58 ) ----------------<cut here
aA Aa aB Ba aC Ca bA Ab bB Bb bC Cb cA Ac cB Bc cC Cc 
-----$ GEN05.1 ( lines:1 words:18 ) ------------------------<cut here

  これで、最初の式も、うまく分けて書けるかと思ったのですが、少し細工が必要
でした。  簡単なテストプログラムで試してみました。
-----^ GEN06.ICN ( date:03-10-18 time:13:26 ) --------------<cut here
####################
# generatorの使い方
####################
# gen06.icn Rev.1.1 2003/10/17 風つかい
# 引数の指定を分離
# This file is in the public domain.

procedure main()

  # 例1 複雑な引数の例
  write("期待する結果")
                # ↓generator  ↓generator "A","B","C"
  every   s3 := test05(test04(!"ABC") | &null)  # 構成を単純化したもの
                      # ↑桁合わせ       ↑ストッパー
    do writes(s3," ")
  write()

  # 例2 引数を括弧式を使って分離したもの(これだと期待通りに動かない)
  write("\n例2の結果")
  every ( x  := !"ABC",             # "A","B","C"を順次生成
          y  := test04(x),          # 桁合わせ
          s3 := test05(y | &null) ) # データ生成と ストッパー生成をまとめる。
    do writes(s3," ")               # procedureの引数と同じイメージ。
  write()

  # 例3 引数を括弧式を使って分離したもの(これだと期待通りに動く)
  write("\n例3の結果")
  every ( x  := !"ABC",             # "A","B","C"を順次生成
          y  := test04(x),          # 桁合わせ
          s3 := test05(y)   ) |     # データ生成と ストッパー処理を分けて
        ( s3 := test05(&null) )     # 最後に "|"でつなぐ。
    do writes(s3," ")
  write()

end

procedure test04(s)
  return left(s,2,"_")
end

procedure test05(s)
       # ↓ストッパーでなければ
  if \s then suspend s || "y" else suspend "end"
                             # ↑ストッパーなら
end
-----$ GEN06.ICN ( lines:45 words:134 ) --------------------<cut here

  動かすとこうなります。
-----^ GEN06.1 ( date:03-10-18 time:13:26 ) ----------------<cut here
期待する結果
A_y B_y C_y end 

例2の結果
A_y end B_y end C_y end 

例3の結果
A_y B_y C_y end 
-----$ GEN06.1 ( lines:8 words:17 ) ------------------------<cut here

  関数/procedureの引数に全部押し込んだ場合と、括弧式で分けた場合では、
シラミつぶしのやり方が少し異なるようです。
  でも、例3のやり方なら良いということで、深く追求しないでおきます。(汗)

  ということで、引数を分けて書くと、こんな風になります。
-----^ LINEG2.ICN ( date:03-10-18 time:13:42 ) -------------<cut here
####################
# 一行整形プログラムの習作
####################
# lineg2.icn Rev1.2 2003/10/18 風つかい
# This file is in the public domain.
link msperm, # msperm  # 文字の重複を許す 順列を生成  generator
     lineg   # lineg   # 一行整形 generator

procedure main(Larg)
  s_seed  := \Larg[1] | stop("lineg2 種文字列 生成する順列の最大長 行長")
  n_char  := \Larg[2] | *s_seed   # デフォルト:種文字列の長さ
  n_limit := \Larg[3] | 79        # 一行指定長

# 当初の形
#          # ↓1行整形procedure ↓順列生成procedure     番兵↓(順列生成の終了)
# every s := lineg(n_limit,,left(strg(s_seed,n_char),n_char) | &null)
#   do write(s)            # ↑左寄せ桁合わせ関数

# 引数指定を分割
  every ( x := strg(s_seed,n_char),       # ↓
          y := left(x,n_char),            # ↓
          s := lineg(n_limit,,y)     ) |  # y | &null ではダメ。
        ( s := lineg(n_limit,,&null) )    # こんな風に分けないといけない。
    do write(s)

end

####################
# テストデータ生成 generator
####################
# arg [1]: string   種文字列
#     [2]: integer  生成順列長の最大
# value  : string  (generator)

procedure strg(s_seed,n_char)
  /n_char := *s_seed  #  デフォルト:種文字列の長さ
        # ↓重複順列生成
  suspend msperm(s_seed,1 to n_char)
                      # ↑数生成 1〜n_char 
end


# lineg2.icn Rev1.2 2003/10/18 風つかい strgの everyは不要なので削除
# lineg2.icn Rev1.1 2003/10/17 風つかい ポスト用に link修正。
# lineg.icn Rev1.2 2003/10/17 風つかい linegの引数を分ける。
# lineg.icn Rev1.1 2003/10/16 風つかい
-----$ LINEG2.ICN ( lines:46 words:169 ) -------------------<cut here

  一行整形 procedureは、続けて呼ぶとゴミがつく問題がありましたので、
修正しました。(static変数のクリア忘れ)
-----^ LINEG.ICN ( date:03-10-17 time:23:58 ) --------------<cut here
## 21 ##############
# 表示・印字用一行整形 generator
####################
# generatorから単語を読込み、区切り文字を挟んで並べ、一行指定文字数をオーバー
# したら出力する generator。 一行に単語を出来るだけ多く並べるために作成。
####################
# arg [1]: integer 一行指定文字数(画面・用紙の横幅)
#     [2]: string  区切り文字(列) デフォルト:半角スペース
#     [3]: string (generatorを指定する)  データ終了時には &nullを付加
# value  : string  表示・印字用一行文字列         # ↓番兵
# Usage  : every s := lineg(n_limit,s_delim,gen() | &null) do ..
# lineg Rev.1.2 2003/10/16 風つかい

procedure lineg(n_limit,s_delim,s_gen)
  static s_buf        # スタティック宣言(次回に呼ばれた時まで保存される)
  initial{            # 初回だけ実行
    s_buf := ""       # バッファーを空文字で初期化
  }
  /n_limit := 79      # デフォルト 一行文字数
  /s_delim := " "     # デフォルト 単語区切り文字

  # 単語の蓄積/書き出し
  if \s_gen then {    # データが終わり(&null)で無ければ

    # バッファーに新単語を足すと指定文字数を越えるなら
      # ↓ *s_bufは、s_bufのサイズを示す。(ここでは、文字列 s_bufの長さ)
    if (*s_buf + *s_delim + *s_gen) > n_limit then {
      if *s_buf > 0 then {               # バッファーが空でなければ
        suspend s_buf do s_buf := ""     # バッファー書き出し   # (Rev.1.2)
      }
    }

    # 新単語を足し込み
                      # ↓バッファーが空(新単語が行の先頭)ならば
      s_buf ||:= if *s_buf = 0 then s_gen else s_delim || s_gen
          # ↑文字列足し仕込み                         ↑文字列の連結

          # ここで 新単語がリミットより長いケースもあるが
          # それは 次のサイクルで処理

  } # end of if \s_gen then 

  # データが終わりなら、残り(指定文字数に満たずバッファー残っている分)を出力
  else if *s_buf > 0 then suspend s_buf do s_buf := ""       # (Rev.1.2)

end

# lineg Rev.1.2 2003/10/16 風つかい 訂正 データ末尾処理にバッファークリア追加
#               (再度呼ばれるとゴミが発生)。 バッファークリア場所変更。
# lineg Rev.1.1 2003/10/14 風つかい
-----$ LINEG.ICN ( lines:50 words:189 ) --------------------<cut here

  lineg2 ATGC 3 60 >lineg2.436 とすると、こうなります。
-----^ LINEG2.436 ( date:03-10-18 time:13:42 ) -------------<cut here
A   T   G   C   AA  AT  AG  AC  TA  TT  TG  TC  GA  GT  GG 
GC  CA  CT  CG  CC  AAA AAT AAG AAC ATA ATT ATG ATC AGA AGT
AGG AGC ACA ACT ACG ACC TAA TAT TAG TAC TTA TTT TTG TTC TGA
TGT TGG TGC TCA TCT TCG TCC GAA GAT GAG GAC GTA GTT GTG GTC
GGA GGT GGG GGC GCA GCT GCG GCC CAA CAT CAG CAC CTA CTT CTG
CTC CGA CGT CGG CGC CCA CCT CCG CCC
-----$ LINEG2.436 ( lines:6 words:84 ) ---------------------<cut here

  Iconは、色々と面白いしかけがありますので、お試し頂けるとうれしいです。

風つかい(hshinoh@...)
IconのWWWは、  http://www.cs.arizona.edu/icon/
UniconのWWWは、http://unicon.sourceforge.net/index.html
BGM: Kessel's Kit / Barney Kessel