作者: Hiroshi Shinohara
日時: 2003/10/15(22:00)
  連休に思いついたネタを整理してポストしていますが、結構時間がかかりますね。
他人に説明ができてはじめて、本当の理解ができると、何かの本で読んで それ以来、
勉強中の内容をなるべく文章にして、ポストするようにしています。

  今日は、パイプのお話です。 私はいまだに MS-DOS環境で Iconを動かしています。
MS-DOSでは、Unixをまねて(疑似)パイプがサポートされています。

  +----------+                        +----------+
  |データ生成|->標準出力 => 標準入力->|フィルター|->標準出力
  +----------+                        +----------+
  個々には単純な処理のフィルターをいくつか組合せて、最終的には複雑な処理を
を行えるのが、Unixの特長の一つだと、大昔に習った記憶があります。
  これは、O/Sを介した処理のお話ですが、Iconのプログラムをこういうイメージで
作れないかと。
  フィルター procedureを組合せて、複雑な処理を行うプログラムを作る練習をして
みます。

  まず、元ネタのデータ生成とフィルターのプログラムを作りましょう。
  データ生成プログラムとして、順列生成プログラム strgを作ります。
  コマンドラインから 種文字列と 生成文字列長を指定して順列を生成します。

-----^ STRG.ICN ( date:03-10-15 time:20:47 ) ---------------<cut here
####################
# コマンドライン文字列から 指定文字数以内の全ての文字列を生成
####################
# strg.icn Rev1.2 2003/10/15 風つかい
link msperm  # msperm  # 文字の重複を許す 順列を生成  generator

             # ↓コマンドライン引数
procedure main(Larg)
         # ↓ \Larg[1]は Larg[1]が存在すればその値 しなければ fail (stopを実行)
  s_seed := \Larg[1] | stop("strg 種文字 文字数")    # 指定無きは、Usage表示
  n_char := \Larg[2] | *s_seed    # 生成文字長  指定無きは、種文字列の長さ
            # ↓順列生成 generator
  every write(msperm(s_seed,1 to n_char))
                           # ↑ 数列 1..n_char生成 generator
end

# strg.icn Rev1.2 2003/10/15 風つかい everyを 1個に。
# strg.icn Rev1.1 2003/10/13 風つかい
-----$ STRG.ICN ( lines:18 words:71 ) ----------------------<cut here

  strgで引用している procedureです。
-----^ MSPERM.ICN ( date:03-10-15 time:20:45 ) -------------<cut here
## 34 ##############
# 英文字列の nΠm  n文字から m個を重複して選ぶ順列 generator
####################
# n個から、重複を許して m個を選ぶ。
# arg  [1]: s  strings 種文字
#      [2]: m  integer
# value:       strings
# Usage: every ss := msperm(s,m) do ...
# ("abc",2) -> "aa","ab","ac","ba","bb","bc","ca","cb","cc"
# msperm Rev.1.1 1997/05/18 風つかい
# string_e.icnより抜粋

procedure msperm(s,m)
  /m := *s                            # default 文字数
  if m = 0 then return ""             # 再帰終了。
  #       ↓種文字から1文字取り出す
  suspend !s || msperm(s,m-1) # ←自分自身を呼んで更に1文字取り出し連結
  #          ↑ 文字列の連結      これを m文字になるまで繰り返す。
end
-----$ MSPERM.ICN ( lines:19 words:76 ) --------------------<cut here

  これを、strg abAB 3 > strg.43 として動かすと結果はこうなります。
-----^ STRG.43 ( date:03-10-15 time:20:29 ) ----------------<cut here
a
b
A
B
aa
ab
(中略)
BAA
BAB
BBa
BBb
BBA
BBB
-----$ STRG.43 ( lines:84 words:84 ) -----------------------<cut here

  次はフィルターです。上のようなデータを生成した時に、縦長表示では行数が
が多くなります。(これが、今回のプログラムを作り始めた発端です。)
  そこで、スペースを夾んで横になるべく詰める処理を、フィルターで作りましょう。
-----^ LINEFORM.ICN ( date:03-10-15 time:20:38 ) -----------<cut here
####################
# 出力整形フィルター
####################
# 標準入力から単語を読込み、区切り文字を夾んで出力。
# 一行指定文字数をオーバーしたら改行を入れる。
####################
# Usage : (標準出力->) | lineform 一行文字数 区切り文字
# lineform.icn Rev.1.1 2003/10/15 風つかい
# This file is in the public domain.

procedure main(Larg)
  n_limit := \Larg[1] | 79      # デフォルト 一行文字数
  s_delim := \Larg[2] | " "     # デフォルト 単語区切り文字

  # 整形文字列の書き出し
  s_buf    := ""                # バッファーを空文字で初期化
  while s_new := read() do {
    # バッファーに新単語を足すとリミットを越えるなら
      # ↓ *s_bufは、s_bufのサイズを示す。(ここでは、文字列 s_bufの長さ)
    if (*s_buf + *s_delim + *s_new) > n_limit then {
      if *s_buf > 0 then {      # バッファーが空でなければ
        write(s_buf)            # バッファー書き出し
      }
      s_buf := s_new            # ここで 新単語がリミットより長いケースもあるが
    }                           # それは 次のサイクルで処理

    # バッファーに新単語を足し込める
    else {       # ↓バッファーが空ならば
      s_buf ||:= if *s_buf = 0 then s_new else s_delim || s_new
          # ↑文字列足し仕込み                ↑文字列の連結
    }
  } # end of while

  # 余り処理
  if *s_buf > 0 then write(s_buf)

end
-----$ LINEFORM.ICN ( lines:37 words:136 ) -----------------<cut here

  データ生成の strgと linformを組み合わせて、
  strg abAB 3 | lineform 40 >lineform.43 としますと、結果はこうなります。
  strgの出力が lineformへの指定通り 横 40文字以内にまとまりました。
-----^ LINEFORM.43 ( date:03-10-15 time:20:40 ) ------------<cut here
a b A B aa ab aA aB ba bb bA bB Aa Ab AA
AB Ba Bb BA BB aaa aab aaA aaB aba abb
abA abB aAa aAb aAA aAB aBa aBb aBA aBB
baa bab baA baB bba bbb bbA bbB bAa bAb
bAA bAB bBa bBb bBA bBB Aaa Aab AaA AaB
Aba Abb AbA AbB AAa AAb AAA AAB ABa ABb
ABA ABB Baa Bab BaA BaB Bba Bbb BbA BbB
BAa BAb BAA BAB BBa BBb BBA BBB
-----$ LINEFORM.43 ( lines:8 words:84 ) --------------------<cut here

  この結果をみると、単語が1文字〜3文字なので 縦の並びが揃ってません。
  単語当たりの表示幅を合わせたい とか思ってしまいます。
  そこで、幅合わせのフィルターを作ります。
-----^ LEFT.ICN ( date:03-10-15 time:20:37 ) ---------------<cut here
####################
# 出力整形フィルター
####################
# 標準入力から単語を読込み、左寄せ桁合わせを行い出力。
####################
# Usage : (標準出力->) | left 桁数 補填文字
# left.icn Rev.1.1 2003/10/15 風つかい
# This file is in the public domain.

procedure main(Larg)
  n_str := \Larg[1] | 4        # デフォルト 桁数
  s_pat := \Larg[2] | " "      # デフォルト 補填文字

  # 整形文字列の書き出し
  while line := read() do write(left(line,n_str,s_pat))
                               # ↑左寄せ桁合わせ関数
end
-----$ LEFT.ICN ( lines:17 words:58 ) ----------------------<cut here

  これを strgと lineformの間に挟んで、
  strg abAB 3 | left 3 | lineform 40 >lineform.43l としますと、結果は
こうなります。
-----^ LINEFORM.43L ( date:03-10-15 time:20:55 ) -----------<cut here
a   b   A   B   aa  ab  aA  aB  ba  bb 
bA  bB  Aa  Ab  AA  AB  Ba  Bb  BA  BB 
aaa aab aaA aaB aba abb abA abB aAa aAb
aAA aAB aBa aBb aBA aBB baa bab baA baB
bba bbb bbA bbB bAa bAb bAA bAB bBa bBb
bBA bBB Aaa Aab AaA AaB Aba Abb AbA AbB
AAa AAb AAA AAB ABa ABb ABA ABB Baa Bab
BaA BaB Bba Bbb BbA BbB BAa BAb BAA BAB
BBa BBb BBA BBB
-----$ LINEFORM.43L ( lines:9 words:84 ) -------------------<cut here

  表示幅が揃いました。  これでいいジャン、必要ならこんなバッチ処理の
スクリプトを作っておいてもいいし〜。 という意見もあると思います。
-----^ SHAPE.ICN ( date:03-10-15 time:20:20 ) --------------<cut here
####################
# 整形プログラム
####################
# バッチ処理
# shape.icn Rev.1.1 2003/10/15 風つかい

procedure main(Larg)
  s_seed  := \Larg[1] | stop("shape 種文字 文字数 一行幅")
                      # ↑指定無きは、Usage表示
  n_char  := \Larg[2] | *s_seed    # 生成文字長  指定無きは、種文字列の長さ
  n_limit := \Larg[3] | 79         # 一行幅
  system("strg " || s_seed || " " || n_char || " | left " || n_char ||
         " | lineform " || n_limit)
end
-----$ SHAPE.ICN ( lines:14 words:62 ) ---------------------<cut here

  これで、shape abAB 3 40 とすれば、同じ結果が得られます。
  でも、もう少しやってみます。 それは次回に。

風つかい(hshinoh@...)
IconのWWWは、  http://www.cs.arizona.edu/icon/
UniconのWWWは、http://unicon.sourceforge.net/index.html
BGM: 杉本喜代志クインテット Live 六本木 MINGOS MUSICO