作者: Hiroshi Shinohara
日時: 2003/11/02(14:05)
  Iconのプログラムでは、generatorをよく使います。その動作確認のために generator
の出力を書き出すことがよくあります。
  例えば、Iconの組込 generatorに、数を順に発生させる seq()がありますが この先頭
の 10個の書き出しは、こんな風になります。

-----^ G_TEST1.ICN ( date:03-11-02 time:00:27 ) ------------<cut here
####################
# generatorの出力を一行に書き出す
####################
# g_test1.icn Rev1.1 2003/11/02 風つかい
# 1,2,..,10 -> "1 2 3 .. 10"
# This file is in the public domain.

procedure main()
  s := " "       # 間隔文字

  n := 0         # 先頭検出用
             # ↓ 1,2,..と数を発生する generator
  every ss := seq() \10 do {     # \10は、generatorの生成数を 10個に限定
    if n > 0 then writes(s,ss)   # 先頭でなければ、生成数と間隔文字を出力
             else writes(ss)     # 先頭なら、生成数を出力
    n +:= 1
  }
  write()        # カーサ戻し
end
-----$ G_TEST1.ICN ( lines:19 words:72 ) -------------------<cut here

  出力は、こんな風になります。
-----^ G_TEST1.1 ( date:03-11-02 time:00:07 ) --------------<cut here
1 2 3 4 5 6 7 8 9 10
-----$ G_TEST1.1 ( lines:1 words:10 ) ----------------------<cut here

  今まで、generatorの出力の書き出しは、main()の中でやっていました。
度々ですので、generatorの書き出し procedureを作ってみようと思いました。

  で、単純にこんな風にしてみました。
-----^ G_TEST2.ICN ( date:03-11-02 time:00:27 ) ------------<cut here
####################
# generatorの出力を一行書き出す
####################
# g_test2.icn Rev1.1 2003/11/02 風つかい
# 1,2,..,10 -> "1 2 3 .. 10" を、書き出し procedureを使って、出力しようと
# したが...
# This file is in the public domain.

procedure main()
        # ↓ 1,2,..と数を発生する generator
  g_out(seq() \10)    # \10は、generatorの生成数を 10個に限定
end

procedure g_out(gen,s)
  /s := " "           # 間隔文字
  n := 0              # 先頭検出用
  every ss := gen do {
    if n > 0 then writes(s,ss)   # 先頭でなければ、生成数と間隔文字を出力
             else writes(ss)     # 先頭なら、生成数を出力
    n +:= 1
  }
  write()             # カーサ戻し
end
-----$ G_TEST2.ICN ( lines:23 words:80 ) -------------------<cut here

  g_outという procedureを作って、この引数に generatorを指定すれば、
generatorの出力を取り出して、一行に書き出してくれる予定です。
  でも、動かしてみますと、あれれ。
-----^ G_TEST2.1 ( date:03-11-02 time:00:07 ) --------------<cut here
1
2
3
4
5
6
7
8
9
10
-----$ G_TEST2.1 ( lines:10 words:10 ) ---------------------<cut here

  一行に書き出すつもりが、generatorの各々の出力が、一行ずつに出力されます。
  procedureの引数に generatorを指定しても問題ないと試してあるし、なして〜?

  悩んだあげく、私の知識では解決できそうにありません。 そこで、
     Iconの拡張版 Uniconの開発プロジェクト
     http://unicon.sourceforge.net/index.html

のメーリングリストで、質問してみたところ、親切に解説して頂きました。
  g_out(seq() \10) で、g_outの引数に seq() \10 を 引き渡したつもり なのですが、
g_outの引数に渡っているのは、seq() \10 ではなく seq() \10 が生成したデータ
とのこと。
  seq() \10 が生成するのは、1 〜 10 の数ですが、それが 順に 10回 g_outに渡る
のだそうです。
  そこで、g_outの  every ss := gen do .. で、seq() \10 から、順にデータを引き
出しているつもりが、実際は データが 1個しかない状態とのこと。  それが 10回
繰り返されるそうです。
  g_outは、生成データが 1個だと、それをそのまま書き出しますので、10個のデータ
が順に書き出されることになります。
  「procedureの引数に generatorを指定できる。」というのは、正しいのですが、
動きとしては、解説のようになるそうです。
  正確には、「procedureの引数に 式(expression)を指定できる。」だそうですが。

  ということで、データだけ渡るのがマズイのなら、procedureを渡せばよいだろうと
作ったのが、次の procedureです。

-----^ G_TEST3.ICN ( date:03-11-02 time:00:46 ) ------------<cut here
####################
# generatorの出力を一行に書き出す
####################
# g_test3.icn Rev1.1 2003/11/02 風つかい
# 1,2,..,10 -> "1 2 3 .. 10" の書き出し
# procedureを引数として渡すやり方
# This file is in the public domain.

procedure main()
          # ↓ generatorを g2sの引数にして渡す
  write(g2s(seq,,10))  # また procedureの引数も g2s渡す
end

####################
# generatorの出力書き出し procedure
####################
# arg [1]: procedure(generator)
#     [2]: list(引数)
#     [3]: generatorの出力生成数
#     [4]: 間隔文字
# value  : (none)
procedure g2s(gproc,Larg,n_limit,s)
  /s := " "       # delimiter
  /Larg := []     # argument of gproc
  s1 := ""        # 足し込み用 空文字列
             # ↓生成数指定が無いか?
  every n := if /n_limit then gproc ! Larg           # 生成数指定が無ければ
                         else gproc ! Larg \n_limit  # あれば
    do s1 ||:= (n || s)  # s1に足し込み (nは、自動的に文字列へ変換される。)
  return s1[1:-*s]
            # ↑最後に余分についている間隔文字の手前まで
end

# 補足
# gproc ! Larg は、procedureの引数を listで与える記法。
#  例えば、write("ABC","DEF") は、write ! ["ABC","DEF"] とも書ける。
# 部分文字列 s[1:-1]の -は、位置を最後から数えるやり方。
#  例えば、"ABCD"[1:-1]は、最初から最後の手前までの "ABC" となる。
-----$ G_TEST3.ICN ( lines:38 words:139 ) ------------------<cut here
  直接書き出すと、汎用性が減りますので、文字列を作成するようにしてみました。

  結果は、こうなります。
-----^ G_TEST3.1 ( date:03-11-02 time:00:39 ) --------------<cut here
1 2 3 4 5 6 7 8 9 10
-----$ G_TEST3.1 ( lines:1 words:10 ) ----------------------<cut here

  メーリングリストでは、そういうやり方もあるけど、Co-Expression を使うのが
いいよ と教えて頂きました。 それは次回に。

  尚、Iconは元々テキスト処理用に開発されましたが、Unicon Projectは、更に
Graphic処理、Network処理、OO(オブジェクト指向)、等の拡張のためのプロジェクト
です。

風つかい(hshinoh@...)
IconのWWWは、  http://www.cs.arizona.edu/icon/
UniconのWWWは、http://unicon.sourceforge.net/index.html
BGM: Round About Midnight / 杉本喜代志