Icon
Iconは、高水準のプログラミング言語であり、アプリケーションの開発やデータ処理、テキスト処理に適しています。Iconは、多くの人が使用するようになっており、特にUNIXやLinuxシステムでよく使用されています。
このチュートリアルでは、Iconの基本的な概念や文法について説明し、プログラムの作成方法や実行方法を学ぶことができます。また、実際に手を動かしながら学習できるように、多数のサンプルプログラムも提供しています。
このチュートリアルを完了することで、Iconを使用してプログラムを作成するために必要な基礎的な知識を身につけることができます。是非、このチュートリアルを通じてIconを学び、プログラミングの世界に足を踏み入れてみてください。
イントロダクション
編集Iconは、文字列と構造の処理に多くの機能を持つ高水準プログラミング言語です。 Iconには、複数の結果を生成する可能性のある式、成功する結果を自動的に探す目標指向評価、文字列スキャンによる高度な概念レベルの文字列操作など、いくつかの新しい機能があります。
Iconは、高度な文字列処理と簡単なプログラミングという設計思想を重視しており、短く簡潔なプログラムを可能にします。 Iconでは、ストレージの割り当てとガベージコレクションが自動的に行われ、オブジェクトのサイズには制限がほとんどありません。 文字列、リスト、その他の構造はプログラムの実行時に作成され、プログラムを記述する際にそのサイズを知る必要はありません。 値は自動的に期待される型に変換され、たとえば入力として読み込んだ数字の文字列は、明示的な変換なしに数値計算に使用できます。 Iconには予約語を含む式ベースの構文があり、外見上、IconのプログラムはPascalやCのものに似ています。
Iconとは何か
編集Iconは、1977年にRalph Griswoldによって設計された高水準プログラミング言語で、SNOBOL, SL5およびALGOLに影響を受けています。 Iconは、構文的な柔軟性、文字列処理、抽象データ型など、さまざまな機能を提供しています。
Iconの特徴
編集Iconは、高レベルの文字列処理と、プログラミングの容易さと短く簡潔なプログラムを可能にする設計思想に重点を置いています。
Iconの主な特徴としては、以下のようなものがあります:
- 文字列処理 - 文字列処理:文字列を処理するための豊富な機能を備えており、パターンマッチング機能による文字列の便利な分析も可能です。
- 目標指向の評価 - Iconは、制御構造を用いて、評価とバックトラックを組み合わせています。
- 組み込むデータ構造 - 数値、文字列、リスト、文字、レコード、プロシージャ、連想検索が可能なテーブルなどのデータ構造が用意されています。
- ポータビリティ Iconはポータブルな言語であり、様々なプラットフォームでコンパイルして実行することができます。
- Iconは強力で汎用性の高い言語であり、様々なアプリケーションを作成することができます。初心者にも経験豊富なプログラマーにもおすすめです。
Hello world
編集次のプログラムを、hello.icn
の名前で保存してください。
- hello.icn
コマンドラインから、次のようにタイプしてください。
$ icon hello.ico
次のように表示されます。
- 実行結果
Hello world!
プロシージャ
編集プロシージャの使い方を説明するために、先のプログラムを次のように分割しました。
- hello2.icn
mainからのhelloの呼び出しが前方参照になっていることに、気がついたかもしれません。
Iconは、前方参照を処理系が自動的に解決します。
変数
編集Iconにおける変数について
編集動的型付け言語のIconの変数には型はありません。 変数を予め宣言する必要は基本的にありません。
変数に値を割り当てる
編集Hello world を少し書き換えてみます。
- variable.icn
- 実行結果
Hello world! 42
- 最初に、変数xには文字列"Hello world!"を代入しました。
- Iconの代入は
:=
です。 - 一度、xを表示した後、今度は整数42を代入しています。
- 何もなかったかのように、今度は42が表示されます。
このようにIconの変数には型はなく、値(=オブジェクト)が型を持っています。
グローバル変数宣言
編集Iconのグローバル変数は、予約語global
を使って宣言します。
- 実行結果
Begin End
プロシージャmainの中で変数xに代入した結果は、プロシージャfでは参照できていない事がわかります。
- 実行結果
Begin Hello world! End
今度は、プロシージャfから変数xを参照することが出来ました。
違いは、予約語global
を使って宣言したことです。
ローカル変数宣言
編集グローバル変数は、特定の用途では便利ですが以下のようなデメリットがあります。
- 名前の衝突: 大規模なプログラムでは、グローバル変数を使うと、同じ名前の変数が別の場所で誤って定義される可能性があります。これにより、プログラム全体でのデータの整合性が損なわれる可能性があります。
- メモリ使用量: グローバル変数は、プログラム全体で共有されるため、その変数の値を保持するために必要なメモリ量が増加します。大量のグローバル変数を使用すると、プログラムの実行に必要なメモリが不足する可能性があります。
- デバッグの難しさ: グローバル変数を使用すると、プログラムの動作が予測不能になる可能性があります。特に、グローバル変数がどこで設定され、どこで変更されているかを特定するのは困難です。したがって、バグの修正やテストの実行が困難になる可能性があります。
特に、偶発的にグローバル変数と同じ名前を踏んだときには、原因とかけ離れたところで障害が出るので、グローバル変数は多用すべきではありません。
Iconでは、グローバル変数と名前が重なってもローカル変数であることを宣言することが出来ます。
- 実行結果
Begin End
静的変数の宣言と初期化
編集Iconでは、静的変数の宣言には予約語static
を、その初期化には予約語initial
を使います。
- 実行結果
i = 0 i = 1 i = 2 i = 3
予約語
編集Iconの予約語( Reserved Words )は以下のとおりです。 これらは、変数や関数の名前(識別子)に使うことは出来ません。
break by case create default do else end every fail global if initial invocable link local next not of procedure record repeat return static suspend then to until while
演算子と区切子
編集Iconの演算子と区切子は以下のとおりです。 これらは1つ1つがトークンで、間に空白を含めることは出来ません。
! % %:= & &:= ( ) * ** **:= *:= + ++ ++:= +: +:= , - -- --:= -: -:= . / /:= : := :=: ; < <- <-> <:= << <<:= <<= <<=:= <= <=:= = =:= == ==:= === ===:= > >:= >= >=:= >> >>:= >>= >>=:= ? ?:= @ @:= [ \ ] ^ ^:= { | || ||:= ||| |||:= } ~ ~= ~=:= ~== ~==:= ~=== ~===:=
キーワード
編集Iconのキーワードは以下のとおりです。
- &allocated - ストレージ領域で使用されているスペース
- &ascii - 128個のASCII文字から構成される文字集合
- &clock - 現在の時刻から構成される文字列
- &collections - 総計、静的要求によってトリガーされたコレクションの数
- &col - テキスト列のマウスの水平位置
- &column - 現在の実行ポイントのソース列番号
- &control - 最後のXイベントで制御キーが押されていればnull、それ以外はfailure
- &cset - すべての256文字から構成される文字集合
- ¤t - 現在アクティブな共役
- &dateline - 現在の日時
- &date - 現在の日付
- &digits - 10個の10進数字から構成される文字集合
- &dump - 終了ダンプを制御する変数
- &error - エラー変換の有効化/無効化
- &errornumber - 最後に変換されたエラーのエラー番号(failureに変換)
- &errortext - 最後に変換されたエラーのエラーメッセージ(failureに変換)
- &errorvalue - 最後に変換されたエラーの誤った値(failureに変換)
- &errout - 標準エラー出力
- &e - 自然対数の底
- &eventcode - 監視プログラム内のイベント
- &eventsource - 監視プログラム内のイベントのソース
- &eventvalue - 監視プログラム内のイベントからの値
- &fail - 単に失敗する
- &features - このバージョンのIconの機能を識別する文字列を生成する
- &file - 現在の実行ポイントのソースファイルの名前
- &host - 実行中のIconが動作しているホストコンピュータを識別する文字列
- &input - 標準入力ファイル
- &interval - 前のイベントからの経過時間(ミリ秒)
- &lcase - 26個の小文字アルファベットから構成される文字集合
- &ldrag - 左ボタンのドラッグ
- &letters - 52個の文字から構成される文字集合
- &level - 手続き呼び出しのレベル
- &line - 現在の実行ポイントのソース行番号
- &lpress - 左ボタンの押下
- &lrelease - 左ボタンのリリース
- &main - main co-expression
- &mdrag - 中央ボタンのドラッグ
- &meta - 最後のXイベントでメタキーが押されていればnull、それ以外はfailure
- &mpress - 中央ボタンの押下
- &mrelease - 中央ボタンのリリース
- &null - null値
- &output - 標準出力ファイル
- &phi - 黄金比
- &pi - 円周率(円周の直径に対する比率)
- &pos - 文字列スキャンの現在のフォーカスを含む変数
- &progname - プログラム名を含む変数
- &random - ランダム操作の現在のシードを含む変数
- &rdrag - 右ボタンのドラッグ
- ®ions - 領域のサイズを生成する
- &resize - ウィンドウのリサイズ
- &row - テキスト行のマウスの垂直位置
- &rpress - 右ボタンの押下
- &rrelease - 右ボタンのリリース
- &shift - 最後のXイベントでシフトキーが押されていればnull、それ以外はfailure
- &source - 現在の共役を呼び出した共役
- &storage - 各領域に使用されるストレージの量を生成する
- &subject - 文字列スキャンの現在の対象を含む変数
- &time - 実行時間(ミリ秒)
- &trace - 手続きのトレースを制御する変数
- &ucase - 26個の大文字アルファベットから構成される文字集合
- &version - このバージョンのIconを識別する文字列
- &window - 現在のグラフィックス描画コンテキストを含む変数
- &x - マウスの水平位置
- &y - マウスの垂直位置
リテラル
編集Iconのリテラル( Literals )には、数値リテラル( Numeric literals )と引用符付きリテラル( Quotedliterals )の2つのカテゴリに分けられます。
数値リテラル
編集数値リテラルは、さらに2つのカテゴリに分けられます:
整数リテラル
編集整数リテラルには2つの形式があります。
数字リテラル
編集数字リテラルは1つ以上の数字で構成されています。
- 数字リテラルの例
- 0, 1, -90, 876
基数リテラル
編集基数リテラルでは、数字の基数を指定することができます。
- 基数リテラルの書式
- 数字リテラル 基数指定 数字指定
- 基数指定
- r
- R
- 数字リテラル
- 数字リテラルの値は基数を指定し、2から36までの範囲でなければなりません。
- 数字指定
- 数字指定は数字と文字のシーケンスで構成され、aは10を表し、bは11を表し、zまで続きます。
- 数字指定内の大文字と小文字は同等です。
- 数字指定内の文字は基数より小さい値を表さなければなりません。
- 基数リテラルの例
- 2r1011, 2R1111, 4r0320, 16rbadbeef, 36rZoo
実数リテラル
編集実数リテラルには2つの形式があります:
10進数リテラル
編集- 10進数リテラルの書式
- 数字リテラル . [ 数字リテラル ]
- [ 数字リテラル ] . 数字リテラル
指数リテラル
編集- 指数リテラルの書式
- 数字リテラル 指数指定 [ 符号 ] 数字リテラル
- 10進数リテラル 指数指定 [ 符号 ] 数字リテラル
- 指数指定
- e
- E
- 指数指定
- +
- -
引用符付きリテラル
編集引用リテラルは2つのカテゴリに分けられます。
csetリテラル
編集csetリテラルは、シングルクォート( ’ )で囲まれた文字列で構成されます。シングルクォートは、エスケープされていない限り、囲まれたクォート内に現れることはできません。エスケープシーケンスについては後述します。
文字列リテラル
編集文字列リテラルは、ダブルクォート( ” )で囲まれた文字列で構成されます。ダブルクォートは、エスケープされていない限り、囲まれたクォート内に現れることはできません。
エスケープシーケンス
編集エスケープシーケンスを使用することで、それ以外では不便または不可能な文字を文字列リテラルに含めることができます。エスケープシーケンスは、バックスラッシュに続く1つ以上の特殊な意味を持つ文字で構成されます。
エスケープシーケンスとそれに対応する文字は次の通りです。
エスケープシーケンス エスケープシーケンス 文字 \b バックスペース \d 削除 \e エスケープ \f フォームフィード \l ラインフィード \n 改行 \r キャリッジリターン \t 水平タブ \v 垂直タブ \' シングルクォート \" ダブルクォート \ddd 8進数コード \xdd 16進数コード ^c 制御コード
- ラインフィードと改行文字はASCIIでは同じです。両方が異なるコンピュータシステムの用語に適応するために含まれています。
- \ddd のシーケンスは、dが8進数の0、1、...、7である文字に対応します。
- \xdd のシーケンスは、dが16進数の0、1、...、a、...、fである文字に対応します。aやAなどの大文字と小文字の16進数の桁は同じです。望ましい8進数や16進数の数を指定するために、後続の文字がエスケープシーケンスの一部として解釈されないようにするために、十分な桁数のみが与えられれば十分です。たとえば、\43 はASCII文字 # を指定し、\xa は \x0a と等価です。
- 制御コードシーケンス \^c は、ASCII文字の制御コード c に対応します。たとえば、\^A は制御コードAを表します。具体的には、\^c はcの下位5ビットに対応する文字を表します。
- バックスラッシュの後に続く文字が前述のリストに含まれていない場合、バックスラッシュは無視されます。したがって、\a は文字 "a" を表します。
データ型
編集Iconには次にような型があります。
データ型 型名 ニーモニック 例 説明 integer i 42, 2r1010, 16Rbadbeef
整数 real r 3.14, 1.23e45
実数 string s "Hello world!", "The Book"
文字列 cset c 'aeiou', '0123456789'
文字集合 file f &input, &output, &errout
ファイル;open()の戻値として知られます。 null n &null
&nullだけを取とり得る型 procedure p 手続き;戻値ではなく手続きそのもの any type x 全ての型 list L list(3, -1), [-1, -1, -1]
リスト set S set()
集合 table T テーブル record R 全てのレコード型 numeric N 数 = 整数または実数 (i or r) co-expression C create 式
Co-Expression any structure type X 構造型 = レコード・リスト・集合またはテーブル (R, L, S, or T)
- ニーモニックは、Iconのリファレンスマニュアルでも広く使われている型の表現です。
integer
編集整数( i : integer ) は、数値( N : numeric )の一種で、多倍長整数でありメモリがある限り大きな値を表現できます。
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
- 21024
整数シーケンス
編集整数シーケンス( integer sequence )は、型ではく演算子ですが、使い勝手は範囲型と理解して差し支えありません。
2 3 5 7 11 H e l l o !
i := 1 to *s
は、リストや文字列の要素の添字範囲を表すイディオムで[1]、*s
は要素数を返す前置演算子*
を適用しています。
この例は、次のように書くことが出来ます。
- 前置演算子
!
は、リストや文字列をジェネレータ化します。
real
編集実数( r : real ) は、数値( N : numeric )の一種で、IEEE 754の倍精度浮動小数点数( float64 : 8-octet )です。
10.0 ^ 305 = 1e+305 10.0 ^ 306 = 1e+306 10.0 ^ 307 = 1e+307 10.0 ^ 308 = 1e+308 10.0 ^ 309 = inf.0 10.0 ^ 310 = inf.0
string
編集文字列( s : string )は、文字のシーケンスで、構造型( #any structure type )の一種です。
"Hello" || "Hello" = "HelloHello" "Hello" || "World" = "HelloWorld" "Hello" << "World" = "World" "Hello" <<= "Hello" = "Hello" "Hello" <<= "World" = "World" "Hello" >>= "Hello" = "Hello" "Hello" == "Hello" = "Hello" "Hello" ~== "World" = "World" "World" || "Hello" = "WorldHello" "World" || "World" = "WorldWorld" "World" <<= "World" = "World" "World" >> "Hello" = "Hello" "World" >>= "Hello" = "Hello" "World" >>= "World" = "World" "World" == "World" = "World" "World" ~== "Hello" = "Hello"
- 条件式を引数にした関数は、条件が不成立だった場合(失敗した場合)関数そのものがなかったことにされます。
cset
編集文字集合( c : cset)は、文字の集合で順序はなく、構造型( #any structure type )の一種です。
&digits ** &digits = &digits &digits ** &ucase = '' &digits ++ &digits = &digits &digits ++ &ucase = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' &digits -- &digits = '' &digits -- &ucase = &digits &digits -- &digits = '' &digits -- &ucase = &digits &ucase ** &digits = '' &ucase ** &ucase = &ucase &ucase ++ &digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' &ucase ++ &ucase = &ucase &ucase -- &digits = &ucase &ucase -- &ucase = '' &ucase -- &digits = &ucase &ucase -- &ucase = '' ~ &digits = '\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\d\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' ~ &ucase = '\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\d\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
file
編集ファイル( f : file)
file(/etc/hosts): type = file 127.0.0.1 localhost ::1localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
null
編集Null( n : null )は、&null
だけを取り得る型です。
&null: type = null &null: type = null
- キーワード
&null
のほか、未初期化のリストの要素も値として&null
を保持しているように見えます。
procedure
編集手続き( p : procedeure )
procedure add2: type = procedure; func(17, 7) = 24 procedure sub2: type = procedure; func(17, 7) = 2 procedure mul2: type = procedure; func(17, 7) = 119 procedure div2: type = procedure; func(17, 7) = 2 procedure mod2: type = procedure; func(17, 7) = 3
any type
編集全ての型( x : any type)
list
編集リスト( L : list )
[ 2, 3, 5, 7, 11, ] [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ] [ &null, &null, &null, &null, &null, &null, &null, &null, &null, &null, ] [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ] [ [ 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, ], ]
まず、main
という手続きが定義されています。これはプログラムのエントリーポイントとなる部分です。
最初の行では、ary
という変数に整数の配列 [2, 3, 5, 7, 11]
を代入しています。そして、inspect(ary)
を呼び出して、ary
の内容を表示しています。inspect
は後述します。
次に、ary
に別の値を代入し、再びinspect(ary)
を呼び出してその内容を表示しています。これは、list(n, x)
を使用して長さがn
で、全ての要素がx
であるリストを生成しています。
さらに、ary
に別の値を代入し、再びinspect(ary)
を呼び出してその内容を表示しています。ここでは、list(n)
を使用して長さがn
で、全ての要素が初期値であるリストを生成しています。
次の行では、every
ループを使用して、i
を1からary
の要素数まで繰り返します。ループの中で、ary[i]
にi
の値を代入しています。このループにより、ary
の各要素がインデックスの値になるようになります。
最後に、make_matrix(n, x)
を呼び出して4x4の行列(要素が全て0.0)を生成し、その内容をinspect
を用いて表示しています。
次に、inspect
という手続きが定義されています。
inspect
は、引数として渡されたオブジェクトを文字列形式で表示するための手続きです。
この手続きでは、result
という変数を初期化し、"[ "
という文字列を代入しています。そして、every
ループを使用してarg
の要素に順番にアクセスします。
ループ内では、各要素elt
の型がlist
であるかどうかをチェックしています。もしelt
がリストであれば、再帰的にinspect(elt)
を呼び出し、結果の文字列をresult
に追加します。
それ以外の場合、elt
の値を文字列に変換してresult
に追加します。image(elt)
はオブジェクトを文字列に変換する組み込み関数です。
最後に、result
に"]"
を追加して、結果の文字列を返します。
最後に、make_matrix
という手続きが定義されています。
make_matrix
は、指定されたサイズn
と初期値x
で要素が全てx
である行列を生成するための手続きです。
まず、長さがn
のリストL
を生成し、それぞれの要素を初期値x
で初期化します。
この手続きでは、every
ループを使用して、L
の各要素に対してlist(n, x)
を適用し、要素をリストに置き換えています。
最後に、生成された行列L
を返します。
set
編集集合( S : set)
5, 3, 2, 7, 11, 1, &null, 5, 10, 4, 9, 3, 8, 2, 7, 1, 6,
[TODO: insert(), delete(), S1 ** S2, S1 ++ S2, S1 -- S2]
table
編集テーブル( T : table)は、キーと対応する値のペアからなる順序付けされない構造型( #any structure type )の一種です。 これらのペアは要素と呼ばれます。 キーは一意であり、値にアクセスするための索引として機能します。 テーブルは、データの保持、検索、更新などの操作を効率的に行うために使用されます。 Iconのテーブルは、他のプログラミング言語の連想配列やマップに似た機能を提供します。しかし、Iconのテーブルはより柔軟であり、キーと値のペアのコレクションとして表現されます。キーは任意のデータ型であることができます(整数や文字列など)。さらに、テーブル内の要素の順序は重要ではありません。
Iconのテーブルは、次のような特徴を持っています:
- 要素の追加と削除: テーブルに新しいキーと値のペアを追加することができます。また、キーに対応する要素を削除することもできます。
- 値の参照と変更: テーブル内の特定のキーに対応する値にアクセスすることができます。また、既存のキーに新しい値を割り当てることで値を変更することもできます。
- 検索と存在確認: テーブル内に特定のキーが存在するかどうかを確認することができます。存在する場合は、そのキーに対応する値を取得できます。
- サイズの管理: テーブル内の要素の数を管理することができます。要素の追加や削除によってテーブルのサイズが変化します。
テーブルは、データのグループ化や関連データの管理、高速な検索や更新など、さまざまな場面で有用です。Iconのテーブルは、高水準の抽象化と操作の容易さを提供し、プログラミングタスクを効率的に解決するための強力なツールとなっています。
"c":3 "a":1 "b":2
record
編集レコード( R : recode )は、複数のフィールド(属性)から成るデータ構造です。レコードは固定サイズであり、各フィールドは名前で参照されます。Iconのレコードは、他のプログラミング言語における構造体やレコード型に類似しています。
レコードはグローバルなスコープを持ち、プログラム全体で利用可能です。レコードの宣言は手続きの内部ではなく、プログラムのトップレベルで行われます。
レコードの宣言は以下のような形式を取ります:
ここで、レコード名はレコードの識別子であり、フィールド1からフィールドnまでは各フィールドの名前です。フィールド名は識別子のルールに従う必要があります。
レコードはレコードコンストラクタ関数を使用してインスタンス化されます。レコードコンストラクタ関数はレコード名に対応し、各フィールドに値を指定して呼び出されます。
レコードのフィールドは、ドット演算子を使用して参照および代入されます。例えば、レコード名.フィールド名
の形式でフィールドにアクセスできます。
Iconのレコードは静的なデータ構造であり、フィールドの値は変更可能です。また、フィールドへのランダムなアクセスやフィールドの反復処理もサポートされています。
type(pt) = point pt.x = 3 pt.y = 4
numeric
編集数( N : numeric)は、整数( integer )または実数( real )です。
co-expression
編集Co Expression( C : co-expression )
any structure type
編集構造型( X : any structure type )は、レコード・リスト・集合またはテーブル (R, L, S, or T)のいずれかです。
前処理
編集すべてのIconのソースコードは、コンパイル前に前処理( Preprocessing )を通過します。プリプロセッサディレクティブは、プリプロセッサの動作を制御し、Iconコンパイラには渡されません。
プリプロセッサディレクティブの一般的な形式は次の通りです。
$define
編集まず、定数の定義が可能です。$defineディレクティブを使用して、シンボルを定義し、それに値を与えることができます。定義されたシンボルは、コンパイル前にその値に置き換えられます。
0 3 6 9 12
- $define は、Cの #define と似ていますが、関数型のマクロの定義・展開には対応していません。
$undef
編集定義した定数の定義を取り消すことが出来ます。
$include
編集また、他のファイルをプログラムに挿入することもできます。$includeディレクティブを使用すると、指定したファイルの内容がその場所に挿入されます。
$ifdef
編集さらに、定義された定数に応じてコードの含有または排除が可能です。定数の値に基づいて、特定のコードブロックを実行するかスキップするかを決定することができます。
$line
編集前処理で、エラーや警告に使われる行番号(とファイル名)を変更することが出来ます。 この機能は、トランスパイルによって生成された Icon のコードの診断に使われることを意図しています。
式と演算子
編集Iconは、構文は比較的小さなセットですが、式と演算子は非常に多種多様です。
- 様々な式
- 0 や "Hello world!” のようなリテラル
- write("Hello world!”) のような関数呼び出し
- i や j のような識別子
- -i や *str のような単項演算子式
- 1 + 1 や 2 ^ 8 のような二項演算子
- if i < j the i else j のような制御構造
- obj.x のようなフィールド参照
- 1 to 10 by 2 のような to-by
- ary[3] のような要素参照
- ary[1:8] のようなスライス
- &fail のようなキーワード
- ( 2 + 3 ) のようなグループ化
- { 式 } のようなブロック
これらは全て式で、式でないものはプロシージャ定義・レコード定義や変数の宣言など少数派です。
算術演算子
編集Iconには、以下の算術演算子が用意されています。
算術演算子 演算子 操作 優先度 結合 例 説明 + 加算 1 左から右 N1 + N2
N1 と N2 の和 - 減算 1 左から右 N1 - N2
N1 と N2 の差 * 乗算 2 左から右 N1 * N2
N1 と N2 の積 / 除算 2 左から右 N1 / N2
N1 と N2 の商 % 剰除 2 左から右 N1 / N2
N1 と N2 の除算の剰り ^ 累乗 3 右から左 N1 ^ N2
N1 の N2 乗
評価は、優先度が高い演算子から行われます。
1 + 2 * 3
は、乗算のほうが優先度が高いので
1 + ( 2 * 3 )
の順で評価されます。
優先度が同じ演算子は、結合方向に従って評価されます。
1 - 2 + 3
の結合は、左から右なので
( 1 - 2 ) + 3
の順で評価されます。
また、
2 ^ 3 ^ 4
の場合、累乗は右から左に結合するので
2 ^ ( 3 ^ 4 )
の順で評価されます。
数値比較演算子
編集数値比較演算子 例 意味 N1 < N2
N1 は N2 未満 N1 <= N2
N1 は N2 以下 N1 > N2
N1 は N2 より大きい N1 >= N2
N1 は N2 以上 N1 = N2
N1 は N2 と等しい N1 ~= N2
N1 は N2 と等しくない
関数
編集関数の引数は左から右に評価されます。もし引数の評価が失敗した場合、関数は呼び出されません。 一部の関数は、与えられた引数の集合に対して複数の結果を生成する場合があります。 もし引数が複数の値を生成する場合、関数は異なる引数値で繰り返し呼び出されることがあります。
[TODO:]
演算子の関数的呼び出し
編集演算子は、通常は式の中で使われますが、関数的に呼び出すことも出来ます。
例えば
3 + 4
は
"+"(3, 4)
と
演算子を表す文字列 ( 引数リスト )
の形式になります。
演算子の優先度と結合性
編集下の表は、演算子を優先度順にグループ化し、結合方向も示しました。
演算子 | 結合性 |
---|---|
左から右 | |
左から右 | |
|
左から右 |
|
右から左 |
左から右 | |
左から右 | |
左から右 | |
|
左から右 |
左から右 | |
|
左から右 |
|
右から左 |
左から右 | |
左から右 | |
左から右 |
制御構造
編集Iconには、以下の制御構造があります。
- if ... then ... else:条件分岐
- case of:多方向分岐
- until:条件が偽になるまで、処理を繰り返す
- while:条件が真である限り、処理を繰り返す
- repeat:条件が真である限り、処理を繰り返す
- every:ジェネレータに対して、順次処理を実行する
- fail:強制的な失敗
ゴール指向の評価
編集Iconプログラミング言語における「Goal-directed evaluation(ゴール指向の評価)」は、条件式の成功や失敗を通じて制御構造を駆動する評価メカニズムの一部です。
Iconでは、条件式は成功すると結果を生成し、失敗すると結果を生成しません。例えば、比較演算子 x > y
は、x
がy
よりも大きい場合に成功しy
の値を返します。それ以外の場合は失敗します。
また、「x > y > z
」は、y
の値がx
とz
の間にある場合に成功し、z
の値を返します。
このようにIconでは論理演算ではなく、成功すると値を返し、失敗すると失敗が上位に伝搬する仕組みを基本としています。
以下は、Iconの条件式を使用した例です。
この例では、max
関数は、2つの引数 a
と b
のうち、大きい方の値を返します。
条件式 a > b
が成功する場合は、 a
を返し、失敗する場合は b
を返します。
if-then-else
編集Iconのif
は以下のような構文を持ちます。
式A
は条件式
が成功した場合に実行され、式A
は条件
が失敗した場合に実行されます。
- 実行結果
- else を含んだ例です。
- 実行結果
- [else 式B]は省略できます。
- 実行結果
- else if で複数の条件で分岐できます。
- これは最初のelseに次のifが結合した結果です。
case of
編集Iconのcase of
文は、複数の値に対して条件分岐を行うために使用されます。
以下は、case of
文の基本的な構文です。
<value>
: 条件分岐の対象となる値。<pattern>
:<value>
と比較されるパターン。:
の左側には、<pattern>
がマッチした場合に評価される式が書かれます。<expression>
:<pattern>
がマッチした場合に評価される式。default
: どのパターンにもマッチしない場合に評価される式。必ず最後に書かれます。
以下は、具体的な例です。
- この例では、
day
に渡された文字列が曜日の名前と一致するかどうかを調べ、一致する場合にはその曜日を表示します。 - 一致しない場合には、「Unknown day.」と表示します。最後の
_
は、どの曜日の名前にも一致しなかった場合に評価される式を指定しています。
while
編集Iconにおけるwhile
は、条件が満たされている限り、繰り返し実行する制御構造です。Iconでは真偽値(Boolean)ではなく、式の成功または失敗で条件を評価します。
0 1 2 3 4 5 6 7 8 9
until
編集Iconのuntil
文は、条件が失敗している限り繰り返し処理を繰り返し、条件が成功するとループが終了します。
0 1 2 3 4 5 6 7 8 9
repeat
編集Iconのrepeatは、続く式を永久に繰り返します。
0 1 2 3 4 5 6 7 8 9
この例では[if i >= 10 then break]で脱出しています。
& と |
編集&
は二項の論理積演算子です。
- a が失敗するとその場で失敗します(everyで修飾されない限りbは評価されません)
- a が成功した場合、bが評価され成功したならbが式の値となり、失敗したなら失敗します。
やや禅問答のようですが、比較演算子と基本的には同じです。
|
は二項の論理和演算子です。
- a が成功するとaが式の値となります(everyで修飾されない限りbは評価されません)
- a が失敗した場合、bが評価され成功したならbが式の値となり、失敗したなら失敗します。
&
と|
の使用例
1 abc 3 3 1 1 abc 3 3 1
if 変数 := 式 then 関数( 変数 )
のように、if では条件式で代入を行うことが可能です。- この形式は
関数( 式 )
と等価で、式が「失敗」すると関数は実行されません。
- +これは、論理演算子式だけではなく、式一般に言え、例えば
find( pattern, string )
のように string から pattern を含む文字列を返す関数でも、 pattern が見つからなければ「失敗」し、find() を引数に含む関数は実行されません。このような仕組みをゴール指向の評価と呼び、ゴール指向の評価を多用するIconでは、while/forは余り使う必要がなく、次に説明するeveryが反復の主役になります。
every
編集大概のプログラミング言語で反復構造の解説のトリを務めるのは、for
あるいはforeach
ですが、Iconにはfor
もforeach
もなく、every
を使います。と言ってもevery
は単なる制御構造ではなく、パターンマッチングに基づくより高度な制御を実現することができます。
前述の while until や repeat の例と等価なループをevery
を書いてみましょう。
0 1 2 3 4 5 6 7 8 9
0 to 9
は、to-by式を使ったジェネレータです。
everyはジェネレータから値を1つずつ取り出して変数iに代入し、その値を画面に出力するという操作を行います。
さらに、このようにも書けます。
他の他のプログラミング言語には余り見ることが出来ない構文ですが、これで writes()
が 0 to 9
の回数だけ繰り返されます。
to-by
編集to-by式は、0 to 9 by 2
のように省略可能なby 整数
を使い増分を指定できます(省略された場合は 1 が仮定されます)。
0 2 4 6 8 9 8 7 6 5 4 3 2 1 0
break
編集breakは、repeatで示したとおりループを中断して脱出します。
また、ループも式であり値を持ちますが、break に値を渡すとループの式の値になります。
0 1 2 3 4 5 6 7 8 9 x = 123 0 1 2 3 4 5 6 7 8 9 x = 111
next
編集next
は、ループ内で使用される制御文の1つです。next
は、現在のループイテレーションをスキップして、次のイテレーションに進むために使用されます。
next
は通常、条件式を使用して特定の条件が満たされた場合にループイテレーションをスキップするために使用されます。
1 3 5 7 9
上記の例では、1から10までの数値を反復処理するループがあります。if i % 2 = 0 then next
の行によって、数値が偶数の場合にはnext
が実行され、ループイテレーションがスキップされます。したがって、奇数の数値のみが出力されます。
next
は、while
、until
、repeat
、every
など、さまざまなループ構造で使用できます。また、
ネストされたループ内での使用も可能です。
return
編集return は、手続きの戻り値を返すことと、手続きを終了し呼び出し元の手続きに変えるという2つの機能があります。
1 1 2 3 5 8 13 21 34 55
これはフィボナッチ数列を計算するためのいくつかの異なるプロシージャ(手続き)の実装です。フィボナッチ数列は、最初の2つの数字が0と1であり、それ以降の数は前の2つの数の和である数列です。
各プロシージャは引数として整数 n
を受け取り、フィボナッチ数列の n
番目の値を返します。
以下に各プロシージャの解説を示します:
fib0
プロシージャ: このプロシージャは最も基本的な形式で、条件分岐を用いて再帰的にフィボナッチ数列を計算しています。n
が2未満の場合はn
を返し、それ以外の場合はn - 1
番目とn - 2
番目のフィボナッチ数列の和を返します。fib1
プロシージャ:fib0
プロシージャと同様に、条件分岐を用いて再帰的にフィボナッチ数列を計算しています。異なる点は、if
文のelse
節が省略されており、その場合にはreturn
文だけが続くということです。fib2
プロシージャ: このプロシージャは一時変数result
を導入しています。条件分岐によって再帰的にフィボナッチ数列を計算し、結果をresult
に代入します。最後にresult
を返します。fib3
プロシージャ:fib1
プロシージャと同様に、条件分岐を用いて再帰的にフィボナッチ数列を計算しています。異なる点は、return
文が式として直接使用されていることです。条件式が真の場合にはn
を返し、偽の場合にはfib(n - 1) + fib(n - 2)
の結果を返します。fib
プロシージャ: このプロシージャでは、再帰呼び出しを利用してフィボナッチ数列を計算します。条件式2 > n
が真の場合はn
を返し、偽の場合はfib(n - 1) + fib(n - 2)
を返します。main
プロシージャ:fib
プロシージャを呼び出して、1から10までのフィボナッチ数列を順番に出力しています。
これらのプロシージャは、異なる構文の使用や一時変数の導入など、同じ目的のためにいくつかの異なる方法でフィボナッチ数列を計算しています。 どのプロシージャを使用しても、正しいフィボナッチ数列の値が得られるはずです。
suspend
編集サスペンド( suspend )は、ジェネレータプロシージャで使います。
素数を返す関数を考えてみます。
2 3 4 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
この関数をジェネレータに書き換えてみます。
2 3 4 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523
- まず「100までの素数」から「最初の100個の素数」に変えたので結果は異なります。
every i := seq(2) do
のseq(2)
が目を引きますが、2 to ∞
と同じ意味で、これ自体がジェネレータです。suspend i
がここでの核心で、定義している手続きはここまで来ると return のように呼び出し元に返りますが、値を返すのではなくジェネレターを介し、返されたジェネレターは suspend の引数を返します。every writes(gen_primes() \ 100, " ")
が定義したジェネレータの呼び出しでgen_primes() \ 100
は生成を100個で打ち切っています。もし打ち切らないと無限に素数を生成してしまいます。- もし100までの素数で打ち切りたい場合は
every i := gen_primes() do if i > 100 then break else writes(i, " ") write()
- とします。
ジェネレータ
編集Iconでは、式や関数は単一の値を返すだけでなく、呼び出されるたびに新しい値を返すことができます。これらはジェネレータと呼ばれ、Icon言語の重要な部分です。
ジェネレータは、式や関数の評価によって生成される"結果シーケンス"を返します。結果シーケンスには、式や関数によって生成できるすべての可能な値が含まれます。結果シーケンスが使い切られると、式や関数は失敗します。
Iconでは、どの手続きでも単一の値または複数の値を返すことができます。制御は、予約語 fail、returnおよびsuspendを使用して行います。これらの予約語がない手続きは、実行が手続きの終わりに到達するとキーワード&fail
を返します。
ジェネレータに変換するには、予約語suspend
を使用します。これは、「この値を返し、再度呼び出されたときにこの点から実行を開始する」という意味で、C言語のstaticの概念とreturnの組み合わせのようなものです。ジェネレータを作成するには、whileループの中にsuspendステートメントを配置します。このように、数値のシーケンスを簡単に作成できます。
また、Iconには、オルタネーターと呼ばれる、ブール演算子のような機能もあります。これを使うと、条件分岐をシンプルに記述することができます。
オルタネータ
編集Iconのオルタネータは、論理和(OR)演算子と同様に機能するもので、複数の値のリストを評価することができます。例えば、以下のように書くことができます。
この式は「もし y が x または 5 よりも小さければ ...」と読めますが、実際には、オルタネータは値をリストから取り出して、式を評価し、条件を満たす場合はその値を返します。この場合、最初に y < x を評価し、x が y よりも大きければ、その値を返して y の値を出力します。x が y よりも小さければ、次に y < 5 を評価します。もし y が x または 5 よりも小さければ、y の値を出力します。しかし、y が x および 5 よりも大きい場合、オルタネータは評価する値がなくなり、条件式が失敗します。その場合、if 文は失敗し、write 文は実行されません。
オルタネータは、Pythonのジェネレータ式のように、リストやシーケンスの要素を評価するための手軽な方法として使うことができます。また、オルタネータは、関数の引数を評価する前に、必ず成功するようにしてくれるため、簡潔なコードを書くことができます。例えば、以下のようなコードがあります。
このコードは、先ほどの例と同じ結果を得ることができます。オルタネータを使うことで、条件を満たす値が存在する場合に限り、評価されるため、無駄な処理を避けることができます。
脚註
編集附録
編集コードギャラリー
編集エラトステネスの篩
編集エラトステネスの篩を、若干 Icon らしく書いてみました。
- エラトステネスの篩
- 実行結果
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
最大公約数と最小公倍数
編集最大公約数と最小公倍数を、若干 Icon らしく書いてみました。
- 最大公約数と最小公倍数
- 実行結果
gcd2(30, 45) => 15 gcd(30, 72, 12) => 6 lcm2(30, 72) => 360 lcm(30, 42, 72) => 2520
- ※ 他のプログラミング言語の例では、可変長引数を使っていますが、Iconではリストを渡しました。
二分法
編集二分法を、若干 Icon らしく書いてみました。
- 二分法
- 実行結果
0.9999999999417923 1.0000000000291038
- 旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Icon に移植しました。
構文
編集program ::= declaration | declaration program declaration ::= "global" identifier-list | "invocable" "all" | "invocable" proc-list | "link" link-list | "procedure" IDENTIFIER "(" [ parameter-list ] ")" ";" [ locals ] [ "initial" expression ";" ] [ expression-sequence ] "end" | "record" IDENTIFIER "(" [ field-list ] ")" identifier-list ::= IDENTIFIER | IDENTIFIER "," identifier-list proc-list ::= string-literal | string-literal "," proc-list link-list ::= file-name | file-name "," link-list file-name ::= IDENTIFIER | string-literal parameter-list ::= identifier-list | IDENTIFIER "[" "]" | identifier-list "," IDENTIFIER "[" "]" locals ::= local-specification identifier-list ";" | local-specification identifier-list ";" locals local-specification ::= "local" | "static" field-list ::= field-name | field-name "," field-list expression-sequence ::= [ expression ] | [ expression ] ";" expression-sequence expression ::= "(" [ expression ] ")" | "{" expression-sequence "}" | [ expression-list ] | expression "." field-name | expression [ expression-list ] | expression [ range-specification ] | [ expression ] "(" expression-list ")" | [ expression ] "{" expression-list "}" | "(" expression-list ")" | prefix-operator expression | expression infix-operator expression | expression "to" expression [ "by" expression ] | "create" expression | "return" [ expression ] | "suspend" [ expression ] [ do-clause ] | "fail" | "break" [ expression ] | "next" | "case" expression "of" "{" case-list "}" | "if" expression "then" expression [ "else" expression ] | "repeat" expression | "while" expression [ do-clause ] | "until" expression [ do-clause ] | "every" expression [ do-clause ] | IDENTIFIER | KEYWORD | literal expression-list ::= expression | expression "," expression-list range-specification ::= expression ":" expression | expression "+:" expression | expression "–:" expression do-clause ::= "do" expression case-list ::= case-clause | case-clause ";" case-list case-clause ::= expression ":" expression | "default" ":" expression literal ::= numeric-literal | quoted-literal numeric-literal ::= integer-literal | real-literal integer-literal ::= digit-literal | radix-literal digit-literal ::= digit | digit digit-literal digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" radix-literal ::= radix radix-specification digit-specification radix ::= "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32" | "33" | "34" | "35" | "36" radix-specification ::= "r" | "R" digit-specification ::= /0-9A-Za-z/+