Icon
Iconは、米国アリゾナ大学のグリスウォルド(Griswold)により開発されたプログラミング言語である。
Iconはテキスト処理を目的として作られ、Griswoldが以前に作成したSNOBOLの後継として作られたが、仕様はかなり異なる。
言語の特徴としては、ゴール指向評価(原語:テンプレート:Lang-en-short)、呼び出しごとにデータを返すジェネレータ、手続きをそのものを変数に格納する「コ・エクスプレッション」(co-expression)などがある。
Iconの後継にはUniconがある。
基本構造
Iconの基本構造は以下のようになる。
# コメント link ライブラリ procedure main(args) メインルーチン end procedure 関数(引数) サブルーチン return サブルーチンの返り値(省略可) end
メインルーチンはmain関数に記述する。なおIconは一度中間形式にコンパイルするときサブルーチンの形式をチェックしてから、再度コンパイルしなおして実行ファイルを生成するので、サブルーチンは宣言の必要がなくソースコードのどの位置に書いても良い。
代入式には糖衣構文がある。例えば、
x := x + n
は、
x +:= n
と、表記が可能である。
ブロック構造は{~}
で範囲を指定する。
データ構造
Iconはリスト、テーブル型(連想配列)、集合、レコード型(構造体)など、多数のデータ構造を提供している。 例えばリストを得るには
cat := ["muffins", "tabby", 2002, 8]
などとすればよい。
ただし上記にないデータ構造も、配列、スタック、キューはリストとの区別が無いだけであり、文字列 は文字型の配列として扱うので、これらのデータ構造の機能をもっていないというわけではない。
またIconの変数は基本的にデータの型を宣言する必要が無いが、データ型が存在しないというわけではなく、型推論が行われているだけである。このため、上記のデータ構造を利用する場合は使用前に宣言を行う必要がある。この宣言は代入文の形で行われる、例えば連想配列の場合
t := table() t["abc"] := 1
となる。これは連想配列t
を使用する前にt := table()<code>によって<code>t
が連想配列であることを明記している。
ジェネレータとevery文
ジェネレータとは呼び出しごとに値を返す機構である。一見関数と同じに見えるが、返す値が複数であることが大きく違う。
例えば
if x = ((1 to 5) | y) then write("True") else write("False")
というプログラムがあったときこれはx
が1から5またはy
と等しいなら「True
」そうでなければ「False
」を表示するプログラムである。
このジェネレータの値を逐次処理するためのIconの基本ループとしてevery文が存在する。このevery文は例えば
every i := 1 to 5 do write(i)
と表記すると1~5までの数字を画面に印字する。このevery の後に来る文はジェネレータであればなんでもよいので上の文は
every write(1 to 5)
と記述しても良い。
なお、ジェネレータは自分で作成することも可能である、単純に返り値をreturn
ではなくsuspend
にすることで実装される。例として奇数を出力し続けるジェネレータは以下のようになる、
procedure odd() x := 1 repeat { suspend x x +:= 2 } end
イメージとしてはsuspend
は値を返した後、サブルーチンを中断せずに次の処理に移るものである。これを
every i := odd() do write(i)
と実行した場合、永久に奇数を表示し続ける。
ゴール指向評価
Iconの中心的な特徴の1つは制御構造を真理値によるものから、成功か失敗によるものへと変更したことである。このモデルでは、
if a < b
のような単純な比較は多くの言語のように
- 「もし式全体が真と評価されるならば」
という意味ではなく、
- 「もし処理全体が成功ならば」
というような意味である。この場合は比較が成り立つなら、<
演算子は成功する。よってIconとその他の言語で実行結果は同じとなる。この方式がより興味深いのは、
if a < b < c
のような場合である。<
演算子は、比較が成り立つときは成功であると同時に、二番目の引数を値として返す。したがって、a < b
の部分を評価して成功すれば、値としてb
を返すので、つづいてb < c
を評価することになる。当然、評価の時に一度でも失敗すれば、全体として失敗である。
このような比較は、ほとんどの言語ではこのまま記述することができないが、Iconでは可能である。
この方式の有用性がより明確になるのは、現実の例で考えたときである。Iconでは
if a := read() then write(a)
は標準入力から標準出力へと1行をコピーする。この例はファイルが存在しないなどの理由でread()
がエラーを発生したときでも正しく動作する。その場合は、a := read()
が失敗し、write(a)
は呼ばれない、という単純な動作である。
成功と失敗は例外処理のように関数を遡る、つまりネストした関数呼び出しの中で失敗が起こると呼び出し側の関数も失敗する。例として、入力ファイルの内容すべてを出力するプログラムは
while write(read())
と書ける。read()
が、例えばファイルの終わりに達して。失敗すると、呼び出しを遡ってwrite()
も同様に失敗する。よってファイルの内容すべてを出力して停止する。比較のためにJava風の擬似コードを考える。
try {while ((a = read()) != EOF) write (a) ;} catch (Exception e) {/* 何もしない */}
この場合、2つの比較が必要とされる。1つはファイルの終端(EOF
)であり、もう1つはその他のすべてのエラー(Exception
)である。
「何らかのゴールに達するまで評価が続けられる」という意味で、Iconではこのような方式をゴール指向評価という。上の例でのゴールはファイル全体を読み出すことであり、まだ読むべき情報が有るあいだはread()
は成功し続け、無くなったら失敗する。従って、戻り値を調べる文や余分な構文は必要なく、ゴールが言語によって直に記述される。