Pike

Pikeは、C言語に似た文法を持つ高水準プログラミング言語です。豊富なライブラリとオブジェクト指向プログラミング機能を備えています。また、高速な実行速度が特徴で、Webアプリケーション、ネットワークプログラミング、データベース処理など、幅広い用途で使用されています。
本チュートリアルでは、Pikeの基本的な文法と機能を初心者向けにわかりやすく解説します。Pikeを学ぶことで、プログラミングの基礎を身につけ、実践的な開発に応用できるようになることを目的としています。本チュートリアルを通して、Pikeの魅力や活用方法を理解し、その可能性を広げることができるでしょう。
Pikeは、C言語に似た文法を持つ高水準プログラミング言語です。豊富なライブラリとオブジェクト指向プログラミング機能を備えています。また、高速な実行速度が特徴で、Webアプリケーション、ネットワークプログラミング、データベース処理など、幅広い用途で使用されています。
Pikeは、以下のような特徴を持っています。
Pikeは、小さくシンプルなスクリプトを記述するために使用することもでき、さらには非常に大規模なプログラムにも使用できます。 世界中のWebサーバーであるRoxen WebServerとCaudiumは、どちらもPikeで記述されています。 Pikeの高度なデータ型とソケットの組み込みサポートにより、インターネットアプリケーションでの使用に理想的です。
Pikeは、GNU General Public License(GPL)、GNU Lesser General Public License(LGPL)、およびMozilla Public License(MPL)に基づくフリーソフトウェアです。 UNIX、Linux、Solaris、macOS X、Microsoft Windowsなど、多くのオペレーティングシステムで利用可能です。
Pikeは、Webアプリケーション、ネットワークプログラミング、データベース処理、ゲーム開発など、多岐にわたる用途に利用されています。
具体的には、以下のような場面で活用されています。
Pikeは、UNIX、Linux、Windows、macOSなどの多くのプラットフォームで利用可能です。 Pikeの最新バージョンをダウンロードするには、Pikeの公式ウェブサイト( http://pike.lysator.liu.se/ )を訪問してください。
Linuxユーザーの場合、Pikeは多くのディストリビューションのパッケージリポジトリに含まれています。例えば、DebianやUbuntuの場合、以下のコマンドを使用してPikeをインストールすることができます。
Windowsユーザーの場合、PikeのWindowsバージョンをダウンロードしてインストールする必要があります。 ダウンロードしたファイルを実行して、ウィザードに従ってインストールしてください。
macOSユーザーの場合、Homebrewなどのパッケージマネージャーを使用してPikeをインストールすることができます。
Pikeは、ビルド済みバイナリの他にソースコードも公開されているので、ソースコードからビルドしてインストールすることも可能です。
ここで問題になるのが、現在配布されているソースコードからビルドする過程で pike 自身が使われている点です。
このため、FreeBSDの様なビルド済みバイナリが配布されていないプラットホームでは、ビルドそのものが出来ません。
これを解決するには、ビルドにpike 自身が使われていなかった頃の過去のバージョン(例えば、0.6)をビルドしインストールし、その後、過去のバージョンのpikeを使って新しいバージョン(例えば、8.0)をインストールする二段階のビルドプロセスが有効です。
この方法で、FreeBSD 13.1/amd64 に Pike v8.0 release 1738 をインストールできました。
Pikeをインストールした後は、まずバージョンを確認します。以下のコマンドを使用してPikeのバージョン確認してください。
コマンドラインからPikeを起動することができます。以下のコマンドを使用してPikeを起動してください。
Pikeの対話型シェルが起動し、コードを実行することができます。
また、Pikeで書かれたスクリプトファイルを実行する場合は、以下のようにファイル名を指定して実行してください。
以上のように、Pikeをダウンロードしてインストールし、コマンドラインから起動することができます。
前の節で実行したプログラムを解説しましょう。
したがって、このプログラムの出力は、"Hello world!" という文字列に改行文字が続いたものになります。
Pikeはインタープリタ言語であるため、変数に宣言が必要ないと思われるかもしれません。 しかし、Pikeでは変数宣言が必須であり、変数の型がコンパイル時に決定されるため、型安定性が高くなります。
変数を宣言する場合、以下のように宣言します。
たとえば、整数型の変数xを宣言し、初期値に42を代入する場合、次のようになります。
Pikeの変数宣言では、型名を省略できませんが、初期値を代入しない場合には、次のように変数宣言をすることができます。
Pikeにはいくつかの基本的なデータ型があります。以下にそれらを説明します。
これらの基本的なデータ型は、Pikeプログラムで変数を宣言する際に使用されます。例えば、int number_of_monkeys;は整数型の変数number_of_monkeysを宣言しています。
また、基本的なデータ型の他にもコンテナ型と呼ばれるデータ型があります。コンテナ型は他のデータを格納するためのデータ型であり、配列(array)、マップ(mapping)、マルチセット(multiset)などがあります。これらのコンテナ型は複雑なデータ構造を扱うために使用されますが、基本的なデータ型と同様に変数を宣言して使用することができます。
まず、基本型の変数が宣言されています。以下のような変数があります:
次に、コンテナ型の使用例が示されています。
Pikeでは変数は必ず宣言が必要なことは既に述べましたが、初期化は必須ではありませんでした。
では未初期化の値を参照するとどうなるでしょう?
整数型変数iの未初期化の値が0である点はともかく、他の型の変数も全て整数型の0が入っています。 未初期化の場合、でたらめな値が入ることは問題ですが、それっぽい値が入っていると初期化忘れを発見することが遅れます。 整数の0は比較的容易に検査できるため、初期化の不備を簡単に発見できます。 しかしながら、初期化を必ず行う習慣を身につけることが最も重要です。
またpike-9.x(開発バージョン)では、新しい型名 auto の導入が予定され、型推論を行うことが可能になります。 型推論には初期化が必須なので、初期化忘れが減る事も期待できます。
Pikeにおける列挙型はenumというキーワードを用いて定義されます。以下は、簡単な色の列挙型を定義し、それを使用して文字列を出力するプログラムの例です。
上記のプログラムでは、Colorという列挙型を定義し、3つの定数 RED、BLUE、GREEN を定義しています。その後、main()関数内でColor型の変数colorを宣言し、BLUEで初期化しています。switch文を使ってcolorの値によって分岐し、該当する文字列を出力しています。
enum は、Pike 7.2.30 で追加されました。
Pikeにおいて、いくつかの関数はコード中で1文字または2文字で呼び出すことができ、コードを短くすることができます。 これらの関数は演算子と呼ばれ、既にたくさんの例でその動作を見てきました。
演算子は機能に基づいてカテゴリーに分類されますが、注意が必要です。なぜなら、一部の演算子には、それらが属するカテゴリーの範囲を超えた意味があるからです。
以下は整数に関する演算子の例です。
以下は文字列に関する演算子の例です。
Pikeの演算子の中には、数学的な意味から離れた定義のものもあります。
このプログラムでは、Pikeの文字列の/演算子と、配列の*演算子を使っています。
まず、文字列 str を "/" (スペース) で分割して、array(string) 型の変数 ary に代入しています。 この演算により、ary には str を単語ごとに分割した文字列の配列が格納されます。 例えば、ary[0] には "The" が格納されます。 この動作は、多くのスクリプティング言語のsplit()関数に相当します。
次に、ary の各要素を ", " で結合して、新しい文字列 s2 を作成しています。 この演算により、s2 には ary の各要素が ", " で結合された文字列が格納されます。 例えば、s2 には "The, quick, brown, fox, jumps, over, the, lazy, dog" が格納されます。 この動作は、多くのスクリプティング言語のjoin()関数に相当します。
最後に、swrite 関数を使って、s2 の内容を表示しています。 write 関数はsprintf 関数の機能を含んでおり、指定したフォーマットに従って、文字列を生成する関数です。 "%O\n" は、文字列を出力するフォーマット文字列であり、%O は、Pike のオブジェクトを文字列に変換するための変換タイプです。
「Pikeでは演算子も関数」とは、Pikeにおいて演算子 (たとえば +, -, *, / など) が単なる記号ではなく、それぞれが対応する関数として定義されているということを指します。
例えば、+ 演算子は、2つの数値を足し合わせるために使われますが、実際には `+ 関数として定義されています。
以下のように `+ 関数を使って足し算を行うことができます。
また、* 演算子も同様に `* 関数として定義されています。以下のように、文字列に対して * 関数を使って、指定した回数だけ繰り返した新しい文字列を作ることができます。
このように、Pikeでは演算子も単なる記号ではなく、それぞれが対応する関数として定義されています。
このような関数の性質をもつ演算子の型を調べてみましょう。
このコードは、Pikeで関数を定義して呼び出す方法を示しています。
最初に、`+ 関数を f という変数に代入しています。ここで + は、加算演算子 "+" に対応する関数です。その後、f に渡す引数を指定して、f 関数を呼び出しています。
次に、f に別の関数を代入しています。ここでは、ラムダ式を使って、与えられた2つの引数を掛け合わせる関数を定義しています。その後、f 関数を再度呼び出して、新しい関数が正しく動作していることを確認しています。
Pikeでは、関数を変数に代入することができるため、動的に関数を切り替えることができます。この機能を使うことで、プログラムの柔軟性を高めることができます。
Pikeにおけるラムダ関数とは、無名の関数を定義するための方法の一つです。ラムダ関数を定義するには、lambda キーワードを使います。
例えば、以下のような lambda 関数を定義してみましょう。
このコードでは、lambda キーワードを使って、引数として整数 x を受け取り、x * x を返す square 関数を定義しています。 その後、square(3) のようにして、引数として整数 3 を渡して square 関数を呼び出しています。 この呼び出しによって、9 が返されます。
配列の要素ごとに操作を行うために、map()には便利な構文があります。この構文を使用することで、コードをより読みやすくすることができます。
定数は、プログラミングにおいて特定の値に名前を付けるために使われるもので、一度定義されたらその値を変更することができません。
構文は以下のようです。
Pike言語では、定数は初期値からの型推論を行います。つまり、定数の型は初期値に基づいて自動的に決定されます。変数とは異なり、明示的な型宣言は必要ありません。
Pikeのsprintf()は、文字列をフォーマットするための関数です。 write()もsprintf()と同じ書式文字列を使ってフォーマット出来ます。
このプログラムは、Pikeでwrite関数を使用して、数値、文字列、および整数のフォーマットを行う方法を示しています。
最初の例では、%dと%fを使用して、整数と浮動小数点数をフォーマットしています。 %fでは、小数点以下4桁まで表示するように指定されています。
次の例では、%sを使用して、文字列をフォーマットしています。 write関数を使用することで、埋め込む文字列を動的に変更することができます。
次に、文字幅の指定について説明します。文字幅を指定するには、%sの前に%10sのように数字を入れます。 これは、表示する文字列が占める幅を指定するものです。 また、%-10sのように-を付けると、左揃えになります。 %10sでは右揃えになりますが、指定した文字列が占める幅よりも長い場合は、文字列の右端から切り捨てられます。 %|10sでは中央揃えになります。
最後に、符号と修飾子の使用について説明します。%+dを使用すると、符号を付けて整数をフォーマットすることができます。 %0*dを使用すると、整数を指定した桁数になるように0でパディングして表示することができます。
それぞれのwrite関数の使用例を実行した際に得られる出力は、コメントの後に示されています。
sprintf()関数は、書式文字列という特殊な文字列を受け取り、それを解釈して文字列を作成します。
書式文字列は、出力する文字列内で特定の値を置き換えるために使用されます。書式文字列は、%に続く文字で始まり、特定の形式で引数を受け取ることができます。たとえば、%dは整数の書式文字列であり、%sは文字列の書式文字列です。
書式文字列には、変換タイプ( conversion type )に修飾子( params )を追加することができます。修飾子は、書式文字列に対して動作を変更するために使用され、変換タイプは、出力の形式を制御するために使用されます。変換タイプと修飾子は、%の後に続けて指定することができます。
変換タイプは、省略できず必ず1つだけ指定します。
修飾子は省略できます。
この章では、Pikeのすべての制御構造について説明します。制御構造は、プログラムの実行フローを制御するために使用されます。 ただし、プログラムを一時停止させる関数や単純な関数呼び出しは、ここでは制御構造とは見なしれません。
条件分岐は、テスト条件を与えられ、実行するコードを選択する制御構造です。 これらは、バイナリの「実行するかしないか」から、入力パラメータに基づいて実行するコードを選択する大規模なコード表まで、さまざまです。
最も単純な条件分岐は、if文です。if文は、文が期待されるどこにでも書くことができ、次のように見えます。
カッコの後ろやelseの後ろにはセミコロンがないことに注意してください。if文の処理は次のように行われます。
要するに、条件式がtrueの場合は文1が実行され、それ以外の場合は文2が実行されます。 条件式がfalseの場合に何かを実行する必要がない場合は、else部分を全て省略することができます。
逆に、条件式がfalseの場合に何かを評価する必要がない場合は、not演算子を使用して式のtrue/false値を反転する必要があります。
ここにある文は、この章の残りの文と同様に、文のブロックである場合があります。ブロックとは、セミコロンで区切られた文のリストで、中括弧 { } で囲まれています。注意点として、ブロックの後ろにはセミコロンをつけないでください。上記の例は次のようになります。
また、複数のif文を順番に配置して、最初の式がfalseである場合は、次の式、次の式などを続けて、最初のtrue式が見つかるまで続けることもできます。
上記の例の特殊な場合は、各式で1つの変数を異なる値と比較する場合です。 そのようなアプリケーションでは、下記で説明するswitch文を使用してパフォーマンスを向上させ、コードを簡素化することができます。
switch文は、条件に応じて複数の選択肢の中から1つを選択するための、より高度な制御構造です。以下のような形式になります。
switch文は、if文よりも少し複雑な構造をしていますが、まだかなり単純です。 まず、式を評価し、次のブロック内の全てのcase文を検索します。 式によって返された値と等しいものが見つかれば、Pikeはそのcase文の直後に続くコードを実行し続けます。 breakが出現すると、Pikeはスイッチブロックの残りのコードをスキップし、ブロックの後に続くコードを実行します。 次のcase文の前にbreakがなくても、必ずしも厳密に必要ではありません。 次のcase文の前にbreakがない場合、Pikeは単に実行を続け、そのcase文の直後のコードも実行します。
上記の例のcase文の1つは、範囲であるという点で異なります。 この場合、定数3と定数4の間の任意の値が文3にジャンプするようになります。 範囲は、定数3と定数4の値を含むことに注意してください。
反復は、コードを0回以上実行するために使用されます。これは、かなり異なる方法で実行できるため、4つの異なる反復制御構造があります。これらはすべて非常に似ているように見えるかもしれませんが、適切な時に適切なものを使用すると、コードは短く、シンプルになります。
whileは、ループ制御構造の中で最もシンプルなものです。elseの部分を除いたif文のように見えます。
動作方法の違いは大きくないため、式が真の場合に文が実行されます。 その後、式が再評価され、真であれば文が再度実行されます。 それから式を再度評価し、繰り返します... 以下は使用例です。
これにより、show_recordが1、2、3、4の値で呼び出されます。
forは、単にwhileの拡張です。ループをより短く、コンパクトに書くための方法を提供します。構文は以下のようになります。
forは、以下の手順を実行します。
これにより、whileセクションの例は次のように書くことができます。
do-whileは、whileと似ていますが、必ず一度は文が実行されことが異なります。
動作方法の違いは大きくないため、式が真の場合に文が実行されます。 その後、式が再評価され、真であれば文が再度実行されます。 それから式を再度評価し、繰り返します... 以下は使用例です。
これにより、show_recordが100で呼び出されます。
foreachは、ループ内の各反復に対して明示的なテスト式を評価しない点でユニークです。 代わりに、foreachはセット内の各要素について1回だけ文を実行します。foreachには2つの構文があり、最初の構文は次のようになります。
foreachが行うことは以下の通りです。
Pike-7.4.10 から foreach に新しい構文が追加されました。
上記のループ制御構文は、すべての問題を解決するのに十分ですが、すべての問題に簡単な解決策を提供するには不十分です。 ループの途中で抜け出すことができる機能が不足しています。これを行う方法は3つあります。
break文は、ループやswitch文の中で使用され、そのブロックから抜け出します。以下は、break文の構文と例です。
条件式が真である限り、ループ処理を繰り返します。しかし、if文の条件式が真になった場合、break文が実行され、whileループから抜け出します。
continue文は、ループ内で使用され、その時点での処理を中断し、次のループ処理を開始します。以下は、continue文の構文と例です。
条件式が真である限り、ループ処理を繰り返します。しかし、if文の条件式が真になった場合、continue文が実行され、ループの先頭に戻って、次のループ処理を開始します。
return文は、関数内で使用され、その時点での処理を終了し、関数から値を返します。以下は、return文の構文と例です。
my_func関数内で、xとyを受け取ります。if文の条件式が真の場合、0を返します。それ以外の場合は、xとyの合計値を返します。return文が実行されると、関数内の処理は終了し、関数から値が返されます。
関数は、Pikeプログラムの主要な構成要素の1つであり、複数の操作をまとめて実行するために使用されます。Pikeでの関数の基本的な概念は、他の多くのプログラミング言語と同様に、引数を受け取り、実行可能なコードブロックを定義し、戻り値を返すことです。

Pikeでは、関数の定義の構文は以下のようになります。
引数リストは、「仮引数型 仮引数名」のペアを「,」で区切った0個以上のリストです。
関数の呼び出しの構文は以下のようになります。
引数リストは、実引数アを「,」で区切った0個以上のリストです。
ここでは、変数に戻値を保持していますが、式の中の項に関数を使ったり、そのまま他の関数の実引数にしたりすることも可能です。
上記のコードは、add2という関数を定義し、その関数をmain関数から呼び出しています。
add2関数は、2つの整数 a と b を受け取り、それらの値を足して結果を返す関数です。
main関数では、add2(2, 3)を呼び出して、その結果を変数 n に格納しています。この場合、引数として 2 と 3 を渡しているので、関数内の a には 2、b には 3 が渡されます。結果として、add2(2, 3) の返り値である 5 が変数 n に格納されます。
最後に、write関数を使って変数 n の値を表示しています。出力結果は n = 5 となります。
この例では、関数 add2 を定義して再利用性を高め、main関数からその関数を呼び出しています。関数を使うことで、同じ処理を繰り返し書く必要がなくなり、コードの保守性と可読性を向上させることができます。
Pikeプログラミングでは、引数の配列展開を行うことができます。 配列展開は、f( @({ 2, 3 }) )のように配列に「@」を前置し、この場合はf( 2, 3 )と同じ意味になります。 配列展開を使うと、配列を個々の引数として関数に渡すことができます。以下に例を示します。
上記のコードは、add2という関数に配列展開を使用して引数を渡し、その結果を変数nに格納しています。 pairというarray(int)型の配列には2つの要素が含まれており、それぞれ2と3です。
add2(@pair)の部分で、@演算子を使用してpairの配列展開を行っています。 これにより、add2関数には2と3の2つの引数が個別に渡されます。
add2関数では、引数aとbを受け取り、それらを足した結果を返しています。 その結果が変数nに格納されます。
最後に、write関数を使用して変数nの値を表示しています。出力結果はn = 5となります。
配列展開を使うことで、配列の要素を個別の引数として関数に渡すことができます。これにより、コードの記述を簡潔にし、柔軟性を高めることができます。
通常の関数は、引数の数は一定ですが、引数の数を可変にすることも出来ます。
仮引数配列は、配列で引数リストと同じ内容が入ります。
なお、配列展開は可変長引数を受け取る関数に対しても使用することができます。 可変長引数を持つ関数に配列を渡す場合も、配列展開を行って可変長引数として渡すことができます。
上記のコードは、可変長引数を持つsum関数に配列展開を使用して引数を渡し、その結果を変数nに格納しています。aryというarray(int)型の配列には1から10までの整数が含まれています。
sum(@ary)の部分で、@演算子を使用してaryの配列展開を行っています。これにより、sum関数には1から10までの10個の引数が個別に渡されます。
sum関数では、可変長引数restを受け取り、それらの引数を合計しています。foreachループを使用して、可変長引数restの各要素nにアクセスし、それを変数xに加算しています。
最後に、変数xの値が変数nに格納され、write関数を使用して表示しています。出力結果はn = 55となります。
この例では、可変長引数を持つ関数に配列展開を使用することで、配列の要素を個別の引数として関数に渡すことができます。可変長引数を使用することで、引数の数が可変的である場合でも柔軟に対応することができます。
Pikeでは、関数内で自己を呼び出す再帰的な関数を定義することができます。これにより、反復処理を使用せずに問題を解決できる場合があります。
たとえば、次のような再帰的な関数を定義できます。
この関数は、与えられた整数nの階乗を計算します。再帰的呼び出しを使用して、nが0になるまで、nから1ずつ引いていきます。
たとえば、次のようにこの関数を呼び出すことができます。
この場合、resultには120が格納されます。
「暗黙のラムダ」は前処理および/または後処理が必要なコードを埋め込む便利な方法です。 関数呼び出しで始まり、直後にブレースブロックが続く文では、ブロックがラムダ関数に変換され、呼び出される関数の最後の引数として渡されます。
timesという関数を定義して、その関数に引数として実行回数とブロックを渡し、ブロックを指定された回数分実行するという機能を持っています。
main関数では、times関数を呼び出し、第1引数に3を、第2引数に{ write("Hello world! <%d>\n", x++); }という暗黙のラムダを渡しています。この暗黙のラムダは、times関数内でblock()という形式で呼び出されます。block()の中では、write関数を用いて、"Hello world! <x>"というメッセージを表示し、xの値をインクリメントしています。したがって、このプログラムは、"Hello world!"というメッセージと、それに続くxの値を示すメッセージを3回表示します。
times関数から制御が戻った後、main関数は、write("Hello world! <%d>\n", x++);によって、"Hello world! <3>"というメッセージを表示し、xをインクリメントします。したがって、このプログラムは、合計で4回の"Hello world!"メッセージを表示し、xの値は4になります。
このように、関数呼び出しに続くブロックが呼び出し先の関数の隠れた最後の関数引数となる例は、Ruby、Groovy、Kotlinなどに見ることが出来ます。
開発中の Pike-9.0 では、__ARGS__を使って暗黙のラムダへ引数を渡すことが出来るようになります。
ラムダ式は外部の変数 x をキャプチャし、クロージャーとして機能します。これにより、ラムダ式内で x の値を変更することができます。 クロージャーは、関数内で定義された変数を含む関数オブジェクトであり、関数が終了してもその変数のスコープが維持されます。 この例では、times 関数内で定義された i と main 関数内で定義された x の2つの変数が存在し、ラムダ式が x をキャプチャして変更しています。 これにより、ラムダ式が終了した後でも x の値を保持できます。
Pikeでは、型は2つの異なるコンテキストで使用されます。コンパイル時と実行時です。一部の型はコンパイル時のみ使用されます(void、mixed、およびすべての構造型)、その他のすべての型は実行時にも使用されます。
Pikeには、基本型とポインタ型の2つの種類の実行時データ型があります。基本型は変数に割り当てられたときにコピーされますが、ポインタ型は単にポインタがコピーされるため、同じものを指す2つの変数が得られます。ポインタ型は、array、mapping、multiset、program、object、およびfunctionの6つがあります。これらはすべてポインタ型であり、メモリ上の何かを指します。この「何か」は、それに対するポインタがもうない場合に解放されます。変数にポインタ型の値を割り当てる場合、この「何か」はコピーされません。代わりに、それに対する新しい参照が生成されます。これらの型のいずれかを関数の引数として渡す場合、関数が「何か」を変更する可能性があるため、特別な注意が必要です。この効果を望まない場合は、値を明示的にコピーする必要があります。後述します。
整数を使用する場合は、キーワードintを使用します。
浮動小数点数を使用する場合は、キーワードfloatを使用します。
文字列を使用する場合は、キーワードstringを使用します。 文字列は、二重引用符で囲まれた文字列リテラルを使用して表します。
この節では、ポインタ型について説明されています。基本型は非常に基本的な型であり、ほとんどのポインタ型は、基本型を格納する興味深い方法です。 ポインタ型には、配列、連想配列、マルチセット、プログラム、オブジェクト、関数があります。 これらはすべて、メモリ内の何かを指すポインタであり、ポインタがなくなると、その「何か」は解放されます。 ポインタ型の値を変数に代入すると、この「何か」がコピーされるのではなく、それに対する新しい参照が生成されます。 関数にこれらの型の値を引数として渡す場合には、特別な注意が必要であり、関数が「何か」を変更する可能性があるため、この効果が望ましくない場合には、明示的に値をコピーする必要があります。
Pikeでは、配列は複数の要素を格納するためのデータ構造です。配列は、コンマで区切られた要素のリストで初期化することができます。
このコードでは、aという配列が初期化され、要素として2, 3, 7が格納されます。
配列の要素にアクセスするには、配列名に角括弧を使用して、インデックスを指定します。例えば、a[0]は配列aの最初の要素を表します。a[1]は2番目の要素、a[2]は3番目の要素を表します。
配列の要素に値を代入するには、同様に角括弧を使用して、インデックスを指定し、代入する値を指定します。
配列に要素を追加するには、配列の最後に新しい要素を追加する += 演算子を使用します。
foreach 文を使うと配列要素を順に処理を行うことが出来ます。
foreach文は、指定された配列の各要素を順番に取り出し、ループを実行するための制御構文です。このプログラムでは、foreach(a, mixed e)という文で、配列aの各要素を取り出して、それをeに代入しています。mixedは、Pikeのデータ型で、任意の型の値を受け入れることができます。
if文は、eの型に応じて、適切な形式で表示するために、条件分岐を行います。intp(e)、floatp(e)、stringp(e)は、それぞれeが整数、浮動小数点数、文字列であるかどうかを判定する関数です。eが整数であれば、"%d "というフォーマット文字列を使用して、その整数を表示します。eが浮動小数点数であれば、"%f "というフォーマット文字列を使用して、その浮動小数点数を表示します。eが文字列であれば、"\"%s\" "というフォーマット文字列を使用して、その文字列をダブルクォーテーションで囲んで表示します。
最後のelse節は、eがどの型にも該当しない場合に実行され、"Unknown type"という文字列を表示します。
[TODO:このコードは要素に配列を含む配列に対応していません。]
Pikeにおける連想配列は、キーと値のペアの集合を表現するデータ型であり、mappingという名前で定義されています。
連想配列は、キーと値のペアを追加することができます。キーには文字列、整数、浮動小数点数、オブジェクトなどを使用することができ、値には任意のデータ型を格納することができます。
以下は、mappingを使用したサンプルコードです。
このコードでは、mappingを使用して、キーと値のペアを格納するmを宣言し、キーに文字列、整数、配列などを使用しています。また、mに新しいキーと値のペアを追加するために、m["key2"] = "value2";のように書いています。
foreach文では、mの各要素をkとvに代入して、write関数を用いて、kとvを適切なフォーマットで文字列に変換し、write関数で出力しています。
上記のプログラムは、以下のような出力を生成します。
mappingは、連想配列を実装するための主要なデータ型であり、Pikeの様々な機能に使用されます。
Pikeにおけるマルチセットは、要素の重複を許した、要素の集合を表現するデータ型であり、multisetという名前で定義されています。
マルチセットは、要素を追加することができ、同じ要素を複数回追加することもできます。
Pikeにおいて、プログラムとはC++におけるクラスに相当します。 プログラムは、そのプログラムに定義された関数や変数がどのように定義されているかの表を保持します。 また、コード自体、デバッグ情報、継承された他のプログラムへの参照も保持します。 ただし、プログラム自体にデータを格納するスペースはありません。 プログラム内の情報は、ファイルまたは文字列がPikeコンパイラを通過したときに収集されます。
次に議論するオブジェクトに格納するために必要な変数スペースは、オブジェクトに格納されます。
プログラムを書くことは簡単で、プログラムをメモリにロードするには、compile_fileやキャスト演算子を使うことができます。 キャスト演算子を使用する場合、プログラムはキャッシュされ、次回以降は同じプログラムが返されます。 また、classキーワードを使用すると、プログラムの中に別のプログラムを書くことができます。
データ領域を作成するには、プログラムをインスタンス化する必要があり、ポインタを使用して、新しいオブジェクトを作成し、そのオブジェクト内のcreate関数を呼び出すことで実現されます。 すべてのプログラムは文字列をコンパイルして生成されます。 このために、compile、compile_file、compile_stringという3つの関数があります。 また、キャスト演算子を使用することもできます。
==や!=を使用することで、2つのプログラムが同じかどうかを確認できます。 また、p[args]やp->identifierのような演算子や関数も重要です。
プログラムはデータを保存する場所を持たず、どのようにデータを保存するかを示しているだけであるため、データを保存するにはオブジェクトが必要です。 オブジェクトは、プログラムから複製されたメモリのチャンクであり、複数のオブジェクトを1つのプログラムから作成できます。 各オブジェクトには、独自の変数があり、そのオブジェクト内の関数がこれらの変数で操作を実行します。 オブジェクト指向プログラミングは非常に柔軟で、データの保存方法を変更しても、オブジェクトの外部に変更を加える必要はありません。
オブジェクトの文字列によるインデックス化で、文字列がオブジェクト内の関数名である場合、関数が返されます。 しかし、その名前にもかかわらず、関数は実際には関数ポインターです。関数ポインターは、他のデータ型と同じように渡すことができます。 関数ポインターは、関数名を引数として受け取り、その関数が指し示すオブジェクトと、その関数を実行するための関数ポインターを返す関数も存在します。 また、関数内で別の関数を定義するために lambda キーワードを使用することもできます。 Pike では関数のオーバーロードはできません。関数ポインターには、関数の呼び出しや関数名の取得、関数が定義されたオブジェクトの取得などの機能があります。
コンパイル時の型には、「void」と「mixed」の2つの型があります。
「void」は、値の存在またはオプション性を示すために使用されます。関数の戻り値として使用されることが多く、関数が値を返さないことを示します。また、関数パラメータの型セットの1つとして使用されることがあり、その場合は、呼び出し元がそのパラメータを省略することができ、その場合は特殊な値UNDEFINEDにデフォルトで設定されます。
「mixed」は、任意の型の値を渡すことができ、実際のランタイムで使用される値の型が完全に不明であることを示すために使用されます。この型は、コンテナクラスを実装する場合(クラス内のコードで実際の値が操作されない場合)、または実際のコンパイル時の型が複雑になりすぎた場合の便宜的なフォールバックとして使用されます。
また、基本型をパラメーターで指定して基本型をサブタイプ化するか、タイプユニオン演算子(|)を使用して複数の代替型を指定することで、より特定のコンパイル時の型を構築することができます。ただし、実行時の型は宣言されたコンパイル時の型と異なる場合があるため、注意が必要です。
変数を宣言する際には、変数がどの種類の変数であるかを指定する必要があります。intやstringなどの多くのタイプに対しては、非常に簡単に指定できます。しかし、より興味深い変数の宣言方法があります。
いくつかの例を見てみましょう。
これらの例を見ると、型を指定する興味深い方法があることがわかります。
以下は可能なもののリストです。
Pikeコードはコンパイラに送信される前に、プリプロセッサを経由します。 プリプロセッサは、ソースコードをPike内部表現に変換し、いくつかのシンプルな正規化と整合性チェックを実行し、プログラマがファイルに入力した「プリプロセッサディレクティブ」を実行します。 プリプロセッサディレクティブは、単純なコード生成と操作を許容する非常に単純なプログラミング言語のようなものです。 コードプリプロセッサは、Pike内でcpp呼び出しによって呼び出すことができます。
プリプロセッサは、ファイルの文字エンコーディングを決定する必要があるため、最初に2つのバイトを調べ、この表に従って解釈します。 バイト0とバイト1の組み合わせに対して、その他のバイトについては、#charsetディレクティブが見つかるまでiso-8859-1エンコーディングとして扱われます。 EBCDICで書かれたプログラムで#charsetディレクティブが省略されている場合はエラーになります。#charsetディレクティブをエンコードすることも可能です。
プリプロセッサは、文字列以外の連続した空白文字を改行以外は単一の空白文字に変換し、すべての//や/***/コメント、#!行を削除します。 PikeはANSI/DECエスケープシーケンスを空白として扱います。
定義に特定の値を設定することもでき、この値はソースコード内の定義が置かれた場所に挿入されます。 マクロには最大254個の引数があります。

Pikeはオブジェクト指向プログラミング(OOP)をサポートしており、PikeのOOP機能を使うことでプログラムのモジュール化や再利用性の向上が可能になります。
クラスはオブジェクトの設計図であり、オブジェクトはそのクラスの実体です。クラスは、そのクラスが持つ属性(フィールド)と操作(メソッド)を定義します。オブジェクトは、そのクラスが持つ属性の値と、そのクラスが定義する操作を実行することができます。
この例では、クラス定義の例として、人物のクラスが作成されています。クラス定義は、classキーワードで始まり、波括弧 { } で囲まれたメンバ変数とメソッドの定義を含みます。
クラスのメンバ変数は、各オブジェクトに1つずつ存在します。この例では、人物の名前を表す文字列型の変数nameと年齢を表す整数型の変数ageが定義されています。
メソッドは、クラスのオブジェクトに適用できる機能を記述します。この例では、say_helloというメソッドが定義されています。このメソッドは、"Hello, I'm 名前."というメッセージが表示します。
クラスのオブジェクトは、クラス名を呼び出し可能な関数のように使用されます。例えば、john->say_hello();は、johnオブジェクトのsay_helloメソッドを呼び出します。
この例では、createという特別なメソッドが定義されています。このメソッドは、オブジェクトをクローンする際に指定した引数を処理します。 このメソッドは、C++プログラマーがコンストラクタと呼ぶものに相当します。
この例では、クラスにdestroyというメソッドが定義されていませんが、このメソッドを定義することもできます。 このメソッドは、オブジェクトが消える直前に実行されるもので、C++プログラマーがデストラクタと呼ぶものに相当します。 ただし、Pikeは自動ガベージコレクションを備えているため、C++ほどは頻繁にデストラクタが必要になることはありません。
Pikeのクラスは、単なる構造体としても使用できます。例えば、上記のコードの
john->name, john->ageのようにインスタンス->メンバ変数の構文でインスタンスのメンバ変数を参照できます。
次の例では、クラスの配列を作りメンバ変数をフリーハンドに扱っています。
このコードは、住所録の項目を表すための address_book_entry というクラスを定義し、そのクラスを使用して住所録を作成しています。
まず、address_book_entry クラスは、名前、住所、電話番号の3つのメンバ変数を持っています。
そして、main 関数では、まず空の住所録を表す配列 address_book を作成します。
次に、address_book_entry クラスから2つのエントリー alice と bob を作成し、それぞれの名前、住所、電話番号を設定します。
そして、これらのエントリーを address_book 配列に追加します。
最後に、foreach ループを使用して、address_book 配列に含まれるすべてのエントリーを出力します。 各エントリーの情報は、write 関数を使用して整形され出力されます。
このコードを実行すると、以下のような出力が得られます:
これは、2つのエントリーが正常に作成され、住所録に追加され、正常に出力されていることを示しています。
Pikeにおいて、プログラムとクラスは同じであることが驚くかもしれません。つまり、Pikeではプログラムとクラスは同義語として使用されることがあります。
ファイル内のPikeプログラムはクラス定義であり、ファイルで定義されたメソッドはそのクラスのメソッドであり、グローバル変数(つまり、メソッドの外で定義された変数)はメンバ変数です。必要であれば、ファイル内のコードに暗黙的に「class {}」があると想像できます。
ただし、細かいクラスごとにそれぞれのファイルを用意しなければならないとなると面倒になるので、「class {}」の表記も用意されています。
見覚えがあるコードですが、class { } で囲まれていませんね。
Pikeのクラスとプログラムの関係は特異的ですが、類例としてはZigの構造体とプログラムの関係があります。
複素数は実数部と虚数部から成る数の形式ですが、Pikeには組み込みの複素数型がないため、独自のクラスを作成しました。
complex クラスは、実数部と虚数部をメンバ変数として持ち、加算、減算、乗算などの操作を行う演算子を提供しています。また、絶対値や文字列化も実装されています。
このPikeのコードは、complex(複素数)クラスを定義し、複素数の演算や機能を実装しています。以下に各部分の解説をします。
まず、complexクラスの定義です。クラス内で使用されるプライベートな変数 _real と _imag が定義されています。 また、`real() メソッドと `imag() メソッドが定義され、それぞれ _real と _imag の値を返します。
次に、create() メソッドが定義されています。このメソッドは、コンストラクタの役割を果たします。 可変長引数を受け取り、最初の引数を実部 _real に、残りの引数(もし存在する場合)を虚部 _imag に代入します。
加算、減算、乗算の演算子メソッドが定義されています。例えば、+演算子に対して complex クラスのインスタンスとの加算を行うと、実部と虚部をそれぞれ足した結果の新しい complex インスタンスが返されます。また、+演算子に対して mixed 型(異なる型の加算も許容する)の引数を受け取る場合は、引数を実部として足し合わせた新しい complex インスタンスが返されます。
`absメソッドは、複素数の絶対値を返すためのメソッドです。絶対値は実部の二乗と虚部の二乗の和の平方根として計算されます。
_sprintf メソッドは、複素数を文字列として表示するためのフォーマットを提供します。
最後に、main 関数では、complexクラスのオブジェクトを作成し、それらの各メソッドを呼び出しています。 また、foreachループを使って、配列の各要素について complex オブジェクトを作成し、それらの加算、減算、乗算を計算しています。
継承は、既存のクラスを基盤として、新しいクラスを作成することができる仕組みです。継承により、既存のクラスのすべてのフィールドとメソッドを新しいクラスに引き継ぐことができます。また、新しいクラスには、継承元のクラスには存在しないフィールドやメソッドを追加することができます。これにより、より特化したクラスを作成することができます。
Pikeでは、クラス継承はinheritキーワードを使用して宣言されます。継承元のクラスは、inheritキーワードの後に指定されます。 図形を例にクラスの継承を解説します。
まず、pointクラスが定義されています。このクラスは2次元の座標を表します。コンストラクタでx座標とy座標を受け取り、その値を保持します。moveメソッドは、座標を指定した量だけ移動させるためのメソッドです。内部でx座標とy座標を更新し、移動したことを表示します。また、_sprintfメソッドは、オブジェクトの文字列表現を返すための特殊なメソッドです。
次に、shapeクラスが定義されています。このクラスは図形を表すための基本クラスです。shapeクラスはpointオブジェクトを保持しており、その位置を表します。createメソッドは、指定された座標に新しいpointオブジェクトを作成し、locationに代入します。moveメソッドは、locationの座標を指定した量だけ移動させるためのメソッドです。内部でpointオブジェクトのmoveメソッドを呼び出しています。また、_sprintfメソッドは、オブジェクトの文字列表現を返すための特殊なメソッドです。
最後の、rectangleクラスが定義されています。このクラスはshapeクラスを継承しており、矩形を表します。createメソッドでは、親クラスのcreateメソッドを呼び出して位置を設定し、さらに幅と高さも設定します。_sprintfメソッドでは、親クラスの_sprintfメソッドを呼び出して親クラスの情報を表示し、それに続けて矩形固有の情報である幅と高さも表示します。
main関数では、実際にrectangleオブジェクトを作成し、操作結果を表示しています。最初にrctという名前の矩形オブジェクトを作成し、その次にrct2という別の矩形オブジェクトを作成しています。各オブジェクトの情報を表示しています。
時には、2つまたはそれ以上のクラスから継承したい場合があります。これはPike(およびC++)で動作しますが、Javaでは動作しません。ただ、複数の継承を記述すれば良いです。
の例では、すべてのオブジェクトのすべてのメソッドとメンバ変数に誰でもアクセスできました。 例えば、次のように年齢を減らすことが非常に簡単でした。
あれ?このjohnの年齢は30歳で、今はマイナス20歳になっている?
他のクラスからその変数に触れないように、メンバ変数 age へのアクセスを制御したいと思います。 このような用途のために、メソッドまたはメンバ変数の定義で、データ型の前に書かれるいくつかのアクセス修飾子があります。 例えば、人物の年齢はメンバ変数 age によって表されます。
これを変更して、
とすると、同じクラス内のメソッドのみがその変数にアクセスできるようになります。
次のアクセス修飾子が存在します。
クラスにコンストラクタ(つまり、create という名前のメソッド)がある場合、これを static に宣言することが良いアイデアです。 それはオブジェクトの構築中にのみ呼び出されるべきであり、static でない場合、継承に関連するいくつかの型の不整合が生じる可能性があります。
Pikeでは、ファイルI/Oに Stdio オブジェクトを使います。
このPikeのコードは、ファイル "/etc/hosts" を読み込み、その中身を標準出力に出力するプログラムです。
まず、string型の変数 fname に "/etc/hosts" というファイルのパスを代入しています。次に、Stdio モジュールの read_file() 関数を使って、fname に指定されたファイルを読み込んでいます。読み込んだ結果は、string|int型の変数 buff に代入されます。
string|int 型とは、string 型または int 型のいずれかの値が代入可能な型です。これは read_file() 関数が、ファイルの読み込みに成功した場合はファイルの内容を string 型で返し、失敗した場合は 0 を返すためです。エラーが発生した場合、intp() 関数を使って buff の型が int 型であることを確認し、Stdio.perror() 関数を使ってエラーの詳細を標準エラー出力に出力した後、プログラムは終了します。
読み込みに成功した場合、write() 関数を使って、buff の内容を標準出力に出力します。最後に、プログラムは終了します。
このコードは、システムファイル "/etc/hosts" の中身を表示するために使用されます。ただし、root権限が必要な場合があるため、注意が必要です。
このプログラムは、ファイルの書き込みと読み込みを行い、それが正常にできたかどうかをチェックします。
まず、main()関数が定義されています。 次に、string fname = "sample.txt";でファイル名を指定し、int cnt;で書き込んだバイト数を格納する変数を宣言します。 また、エラーが発生した場合のエラーメッセージを格納する変数int|array errを宣言しています。 この変数は、catchによって例外が発生した場合に、理由とスタックトレースが格納されます。
catchのブロック内では、Stdio.write_file関数を使用して、指定されたファイルにテキストを書き込みます。 catchブロックの外側で、arrayp関数を使用して、err変数の型が配列であるかどうかをチェックします。 もし配列であれば、エラーメッセージを出力し、return 2;でプログラムを終了します。
次に、string|int buff = Stdio.read_file(fname);で、指定されたファイルからテキストを読み込んで、読み込んだテキストをstring型の変数buffに格納します。 もし読み込みに失敗した場合は、intp関数を使用して、buff変数の型が整数型であるかどうかをチェックします。 整数型であれば、Stdio.perror(fname);でエラーメッセージを出力し、return 1;でプログラムを終了します。
最後に、読み込んだテキストをwrite(buff);で出力し、return 0;でプログラムを正常に終了します。

エラトステネスの篩を、若干 Pike らしく書いてみました。
最大公約数と最小公倍数を、若干 Pike らしく書いてみました。
二分法を、若干 Pike らしく書いてみました。