Skip to content

mitchan0321/perfume

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

プログラミング言語Perfumeイントロダクション

【はじめに】

この文書は、プログラミング言語Perfumeの最初の最初についてのとっかかりを説明するものである。 この文書では説明しきれない設計思想や機能が他にもたくさんあるため、詳細については以下のPDFのマニュアルを参照されたい。

ここからPDFマニュアルがダウロードできます

【特長】

以下に、Perfumeの特徴を思いつくままいくつか挙げる。

  • Perfume言語は “文法を持たない” 自由なスクリプト言語である。
  • 整数型や実数型、文字列型など以外に、関数やブロック("{" と "}" で囲まれた文の集まり)もファーストクラスオブジェクトである。
  • ブロック自体がクロージャであり、プログラム中に現れたブロックは他の関数へ渡したり、関数から戻り値として受け取ることが可能である。 これによりPerfume言語自身でシンタックスを拡張することが容易である。
  • とてもシンプルなオブジェクトシステムを持つ。全てのクラスは、実態としてはオブジェクトであり、 あるクラスから派生したインスタンスはそのクラスのオブジェクトを委譲先とするモデルとなっている。
    また、クラスではなくオブジェクトに直接メソッドを定義することができる(Rubyの特異メソッドと同等のもの)。
    さらに、クラスに後からメソッドを定義すれば、それが即そのクラスから生成されたインスタンスにも反映される仕組みである。
  • 正規表現が利用可能である。正規表現を表すための専用文字列型(RQuote型データ ≒ Raw string と同等の機能を持ったリテラル表記)がある。

【変数】

変数にはローカル変数、インスタンス変数、グローバル変数およびクロージャ変数がある。

  1. ローカル変数は、関数に閉じた変数であり、setコマンドにより設定および上書きされる。スコープは関数内となる。
    (注)ここで言うコマンドとは、Perfumeのプログラムの実行文における命令のことである。
  2. インスタンス変数は、オブジェクトのプロパティであり、そのオブジェクトのメソッド内にてsetsコマンドにより設定および上書きされる。 スコープはオブジェクトのメソッド内となる。
  3. グローバル変数は、プログラムのどこからでも参照可能なスコープを持った変数であり、defvarコマンドにより設定され、 setvarコマンドで値を上書きされる。
  4. クロージャ変数は、プログラム中にブロック("{" と "}" に囲まれた部分)が現れた際、このブロックが現在の環境にあるローカル変数辞書を キャプチャしたものである。

上記1.~4.のそれぞれの変数は、変数名としてシンボルが割り当てられる。シンボルとは、A-Z、a-z、0-9、と記号 ( _-*/%!@^|&~=+?.<> ) を組み合わせた、 プログラマが決めた識別子である。

変数の参照については、シンボル名の先頭に "$" 文字を付与することにより行われる。
変数の参照順は、次のスコープの順序で行われる:

ローカル変数 → クロージャ変数 → インスタンス変数 → グローバル変数

以下、いくつか例を挙げる。

set a 1;                # シンボルaをローカル変数として1を設定する。
sets Cond <t>;          # シンボルCondをインスタンス変数として真偽値の真を設定する。順番が前後するが、真偽値のリテラルとして、真(=true)は <t>、偽(=false)は <nil> で表す。
defvar PI 3.14;         # シンボルPIをグローバル変数として3.14を設定する。
println $a;             # ローカル変数aを表示する。(ここでローカル→クロージャ→インスタンス変数→グローバル変数の順で値を検索する)
println $Cond;          # インスタンス変数Condを表示する。
println $PI;            # グローバル変数PIを表示する。
set o [new];            # オブジェクトを生成し、ローカル変数oに設定する
$o method foo (x) {     # オブジェクトoにメソッドfooを定義する(後述)
    set a $x;           # メソッドfooの中でローカル変数を設定
    sets a [$x + 1];    # オブジェクトoにインスタンス変数aを設定
    println $a;         # ここではローカル変数のaが参照される   
    println [sets a];   # 名前が衝突している場合は、明示的にインスタンス変数を取得するため「sets 変数名」とすることで取得可能(後述)
};

【データ型】

Perfumeのデータ型は以下の通りである。

(1) シンボル

A-Z、a-z、0-9、と記号 ( _-*/%!@^|&~=+?.<> ) を組み合わせた識別子。変数名やクラス名、関数名、メソッドおよび辞書のキー値として用いる。 Perfumeでは引数列やリスト要素などの区切り文字としてスペースを使用するため、シンボルに利用可能な文字については制約は緩く設定している。

(2) 整数

0-9 および先頭の"-"により構成される整数値。多倍長整数が利用可能なため、任意の長さの整数が扱える。

(3) 実数

0-9、"."、先頭の符号および、e表記の指数部により構成される浮動小数点。64ビット浮動小数点が扱える。

(4) 文字列

" と " で囲まれた部分を値とする任意の文字コード(内部32ビットのUnicode)の配列。

(5) ブロック

"{" と "}" に囲まれた複数の実行文により構成される処理単位。このブロックは、ファーストクラスオブジェクトであり、ソース上に現れたときの ローカル変数辞書をキャプチャする。 ここで言うローカル変数辞書とは、関数呼び出しに伴いスタックに積まれる辞書構造である。

関数が呼ばれるたびにローカル変数用の辞書が生成され、この辞書中にローカル変数名をキーとした値が保存される。ブロックは、この辞書への参照を持つことになる。 このブロックは、関数に渡したり、変数に代入したり、関数の戻り値とすることなどができる。

(6) 参照型

変数の値を参照したいときは、シンボルの先頭に「$」を付ける。「$」がついていないシンボルはそのままシンボルオブジェクトとして扱われる。

繰り返しとなるが、変数を参照するスコープの順序は以下の通りである。

ローカル変数 → クロージャ変数 → インスタンス変数 → グローバル変数

(7) 実行ブロック

"[" と "]" に囲まれた複数の実行文により構成される処理単位。この実行ブロックは、文を評価する際に先に実行され、値に変換された後に文が実行される。

set x 10;
foo {result $x};        # → ブロック(fooにブロックが渡る)
foo [result $x];        # → 実行ブロック(ブロックの評価後の値10がfooに渡る)

(8) 関数

関数もファーストクラスオブジェクトであり、別の関数に渡したり、変数に代入したり、関数の戻り値とするなどができる。

関数を生成するコマンドは次の通り。

fun (x) {println $x};                   # 名前なし関数(ラムダ)を定義する(即値として関数を返す)
set f [fun (x) {println $x}];           # 名前なし関数(ラムダ)を定義し、ローカル変数fに設定する
$f 10;                                  # fに設定された名前なし関数を実行する
defun foo (x) {println $x};             # 名前付き関数fooを定義する
foo 10;                                 # 関数fooを実行する

fun コマンドで生成された関数は、変数経由で呼び出しが可能である。

defun コマンドで生成された関数は、グローバルに関数名が関数として登録されるため、いつでも名前で呼び出すことが可能である。

関数の処理部分もブロックであるため、関数が定義された環境のローカル変数群をキャプチャしている。

(9) オブジェクト

クラスまたは別のオブジェクトから生成されたオブジェクト。「new クラス名」(クラス名を省略した場合はObjectオブジェクトから生成される) により生成する。 オブジェクトにはプロパティ(インスタンス変数)を設定可能である。 プロパティをオブジェクト外から設定する場合は、オブジェクトの set! メソッドを使用する。

set o [new];                            # オブジェクトを生成し、変数oへ設定する
$o set! x 1;                            # オブジェクトのプロパティ(インスタンス変数) x に 1 を設定する

クラスは、classコマンドにより生成する。

class bar 親クラス                      # bar というクラスを作成する。メソッドはまだ無い(が、親クラスのメソッドは呼び出すことが可能)

メソッドはオブジェクトまたはクラスに対して method メソッドを用いて行う。

bar method foo (x) {println $x};        # クラスbarにメソッドfooを定義する
Integer method foo2 (x) {println $x};   # Integerクラスに新たにfooを定義する。この定義以降、1 foo "Hello" のような呼びだしが可能になる。
$o method foo (x) {println $x};         # オブジェクトにメソッドfooを定義する

オブジェクトのメソッドを呼び出す方法は、オブジェクト メソッド名 引数リスト... となる。

$o foo 10;                              # オブジェクトoのメソッドfooを呼び出す

クラスは class コマンドにより生成されるが、その実態は、グローバルにクラス名をキーとして設定されたオブジェクトである。 つまり、class newClass とすることで、新たにオブジェクトが生成され、newClass という名前でその生成されたオブジェクトがクラスとして登録される。 そして、new newClas とすることで、newClass オブジェクトを委譲先とする新しいオブジェクトが生成されるのである。

class newClass;                         # newClass というクラスを生成する
newClass method foo (x) {println $x};   # newClass に foo というメソッドを定義する
set o [new newClass];                   # newClass のオブジェクトを生成し、o というローカル変数に設定する
$o foo 10;                              # オブジェクト o の foo メソッドを呼び出す
$o method bar (x) {println $x};         # オブジェクト o にメソッド bar を定義する。これは newClass のメソッドではない
newClass method baz (x) {               # newClass クラスに baz というメソッドを定義する。これは newClass のメソッドでもあり、オブジェクト o のメソッドにもなる
    println $x;                         # パラメータx(ローカル変数)を表示する。
    sets inst_x $x;                     # パラメータxをインスタン変数 inst_x に設定する。
    println $inst_x;                    # インスタンス変数inst_xを表示する。
};

(10) 真偽値

真または偽を表すデータ型。真(true)をリテラル <t> で表し、偽(false)をリテラル <nil> で表す。

(11) リスト

"(" と ")" で囲まれた中に、任意のデータ型の要素を順番に並べたものである。要素の数は0でも良い。 以下、リストの例となる。

()                                      # 空のリスト(要素数0)
(a b c)                                 # シンボル a b c の3つの要素を持つリスト
(1 10.0 "abc")                          # 整数 1、実数 10.0、文字列 "abc" の3つの要素を持つリスト
($x $y [fun () {println "Hello."}])     # xの値、yの値、関数(ラムダ) の3つの要素を持つリスト
(x . y)                                 # x と y のドット対も定義できる

リストのイテレータが存在し、リストクラスのメソッドである each を呼び出すことによりイテレータが実行できる。

(1 2 3) each do: {| e | println $e};    # リストの要素をすべて印字する。"|" と "|" で囲んだ引数リストについては、yield コマンドで説明する。

(12) 辞書

辞書は dict コマンドにより生成される。辞書には、キーを指定して値を設定したり、また、キーを指定して設定された値を取り出したりすることができる データ構造である。

set d [dict];                           # 辞書を生成し、変数 d へ設定する
$d set key1 "abc";                      # 辞書にキー値 key1 で値 "abc" の文字列を設定する
$d get key1;                            # 辞書に key1 のキーで設定された値を取り出す("abc"が返る)

また、まとめてキー値を設定したり内容を参照することが可能である。Lispのドット対と同じ表記のリストを用いる。

set d [dict ((a . 1) (b . "abc"))];     # 辞書が生成され、キー値aには整数1、キー値bには文字列"abc"が設定される
$d pairs;                               # ドット対リストの形で辞書dの中の全ての要素が結果として ((a . 1) (b . "abc")) のように返る
$d keys;                                # リストの形で辞書dの中の全ての要素のキーが結果として (a b) のように返る

辞書のイテレータが存在し、リストクラスのメソッドである each を呼び出すことによりイテレータが実行できる。

set d [dict ((a . 1) (b . 2) (c . 3));
$d each do: {| k v | println $k "," $v};        # 辞書のすべての要素のキー値と値を印字する。

(13) 正規表現文字列(RQuote型データ)

正規表現文字列は、' と ' で囲まれた正規表現を表す文字列である。主に、String::=~ のパラメータで使用される。

通常の文字列型とは異なり、「\」はエスケープ文字を意味せず、そのまま正規表現エンジンに渡される。

なお、正規表現文字列の中にシングルクオート(')を含めたい場合は、シングルクオートをふたつ続けて記述する。

(例) "It's" にマッチする正規表現 → 'It''s'

【構文】

Perfumeには所謂他のプログラミング言語のような構文は存在しない。 ルールは以下の通りである。

  • プログラムは文のリストである。各文は ";" により終端し、この ";" が現れた時点でその文が評価・実行される。そのため、ひとつの文は複数の行に渡って記述が可能である。 ただし、ブロックの最後の文については、";" が省略可能である。
    以下の例では、「$i = 10」、「result <t>」、「result <nil>」は全てブロックの最後の文で ";" が省略された例である。

    (例)if {$i = 10} then: {result <t>} else: {result <nil>};

  • 文は命令とそれに続くパラメータのリストである。

  • 命令とパラメータ間は1つ以上の空白、タブ、改行文字により区切る。このことにより、シンボルに使用可能な文字種の制約を緩くしている。

文の最初の要素がシンボルまたは関数への参照であれば、それは関数として認識され、下記の例だと、set コマンドや if コマンドが呼び出される。

set a 1;                                                # setコマンドは、ローカル変数aに1を設定する文
if {$a >= 1} then: {return <t>} else: {return <nil>};   # ifコマンドは最初の条件式{$a >= 1}を評価し、then: パラメータもしくは else: パラメータを実行する
set f [fun () {println "Hello"};                        # setコマンドにより変数fに関数を設定する
$f;                                                     # そして、関数を実行する

最初の要素がオブジェクトの場合は、そのオブジェクトに対して2番目の要素であるメソッドを検索し、そしてそのメソッドを実行する。

set o [new];                                            # setコマンドにより変数oにオブジェクトを設定する
$o method foo () {println "Hello"};                     # $o オブジェクトにメソッド foo を定義する
$o foo;                                                 # $o オブジェクトの foo メソッドを実行する

【実引数】

実引数とは、関数、メソッドを呼ぶ際に、関数、メソッドへ実際に渡す値である。 実引数は、その文が関数呼び出しであれば、コマンドの次の要素から文末 ";" までの要素全てがその関数に渡される。 その文がオブジェクトのメソッド呼び出しであれば、メソッドの次の要素から文末 ";" までの要素全てがそのメソッドに渡される。

関数呼び出しの例:

fun1 x y z;                     # 関数 fun1 を、引数 x y z を伴い呼び出す

オブジェクト呼び出しの例:

$o meth1 x y z;                 # オブジェクトoのメソッド meth1 を、引数 x y z を伴い呼び出す

関数名、オブジェクト、引数の並びの区切り記号はすべて空白文字(スペース、タブ、改行)である。 したがって、例えば

$x++1

と記述すると、$xを1インクリメントする事にはならず、「x++1」という変数の参照となる。 意図通り変数xをインクリメントする場合は、

$x ++ 1

とし、各トークン間に空白を入れなければならない。

【仮引数】

仮引数とは、関数、メソッドが呼ばれた後、その関数、メソッドに渡される値を格納するローカル変数の定義である。

位置引数

fun コマンド、defun コマンド、method メソッドにより定義される関数の仮引数リスト。

defun 関数名 (仮引数シンボル ...) {... };

のように、リスト中に仮引数名を列挙して定義する。

関数の中では、実引数の並び順にそれぞれの値が仮引数シンボルのローカル変数名に割り当てられる。

名前付き引数

fun コマンド、defun コマンド、method メソッドにより定義される変数の名前付き仮引数リスト。

defun 関数名 (引数名: 仮引数名 ...) {... };

のように、リスト中に「引数名: 仮引数名」として列挙する。

関数の中では、仮引数名シンボルにローカル変数名が割り当てられる。

名前付き引数は引数名で仮引数の内容を示すため、複数の名前付き引数名を指定する場合でも、呼び出し側からは順不同で指定可能であり、また、 指定を省略することも可能である。

defun foo (pos-x: x pos-y: y) {
    println "PosX=" $x ", PosY=" $y
};
foo pos-y: 10 pos-x: 20;                        # → x=20, y=10 
foo pos-x: 30;                                  # → x=30, yは存在しない

上記の「foo pos-x: 30」の例では、foo の中で変数 y を参照した場合に変数が存在しないため例外が発生する。 関数の中で名前付き引数が渡されているかどうかを判定するためには、set? コマンド(後述)を用いる。

なお、ブロック中で発生するExceptionを検知するために、tryコマンドが用意されている(後述)。 このコマンドは、try ブロック catch: 例外ブロック fin: 後処理ブロックの形をしており、一般的な言語における try ~ catch ~ finally と 同様なことを実現可能なコマンドである。

オプション引数が定義された関数を呼び出す際、例えば defun foo (v: x) {} という関数定義がある場合、これを呼び出す際に foo :v のように名前付き引数のキーワード に対して前に「:」を付けることで、仮引数 x に対して <t> が渡される。つまり、foo v: <t> の呼び出しと等価となるが、この「:キーワード」のことをスイッチ引数と呼んでいる。

オプション引数

引数の長さが不定長の定義ができる。名前付き引数のように (args: vars) と、キーワード args: を指定することにより、位置引数が割り当てられた以降の実引数が、 リストの形で vars に設定される。

【基本的なコマンドとクラス】

コマンド

ここでは基本的なコマンドを現すが、これ以外にもたくさんのコマンドが存在する。正式には、PDFマニュアルを参照のこと。

println

println 値 ...; 

値を標準出力に出力する。

read

read 変数名; 

標準出力から1行読み込み変数名に設定する。戻り値も読み込んだ値となる。

set

set 変数名 値;

変数名をローカル変数とし、値を設定する。なお、setコマンドでは、set (変数名1 変数名2 ...) (値1 値2 ...) の形式で多値の代入を行うことが可能である。 もし値を省略した場合は、ローカル変数名の値を返す($変数名と同じ)。

unset

unset 変数名;

変数名のローカル変数を削除する。

set?

set? 変数名;

変数名のローカル変数が存在する場合は<t>を返す。

sets

sets 変数名 値;

メソッドの中で、変数名をインスタンス変数とし、値を設定する。 もし値を省略した場合は、インスタンス変数名の値を返す($変数名と同じ)。

setc

setc 変数名 値;

現在キャプチャしているクロージャ変数辞書に存在する変数名に値を設定する。 なお、クロージャ変数辞書は、関数呼びだし毎にネストして存在するが、setc コマンドでは、一番直近(内側)のクロージャ変数のみ設定可能である。

defvar

defvar 変数名 値;

変数名をグローバル変数とし、値を設定する。グローバル変数が存在しない場合に利用可能。

setvar

setvar 変数名 値;

既に設定されているグローバル変数に対して値を設定する。

if

if {条件ブロック} then: {条件が真のときに実行するブロック} else: {条件が偽の時に実行するブロック};
if {条件ブロック} {真ブロック} {偽ブロック};

if コマンドである。真ブロック、偽ブロックを名前付き引数 then:/else: で指定する方法と、位置パラメータで指定することが可能。 名前付き引数の場合は else: ブロックのみを指定することも可能である。 条件ブロック部は、評価の結果 <nil> の場合は偽ブロックが、それ以外は全て真として真ブロックが実行される。

cond

cond {条件ブロック1} {評価ブロック1} {条件ブロック2} {評価ブロック2} ...;

条件ブロックnが<t>のとき評価ブロックnが実行される。cond式全体を抜けるときは評価ブロックの中でbreakを実行する。そうでなければ次の条件ブロックの評価を行う。

case

case 値 値1 {評価ブロック1} 値2 {評価ブロック2} ... default: {デフォルト評価ブロック};

値が値nと等しければ評価ブロックnが実行される。いずれとも等しくなければデフォルト評価ブロックが実行される。

while

while {条件ブロック} do: {繰り返しブロック}

条件が<nil>以外の間繰り返しブロックを実行する。

fun

fun (引数リスト) {関数本体};

無名関数を生成する。戻り値は関数オブジェクトとなる。

defun

defun 関数名 (引数リスト) {関数本体};

名前付き関数を定義する。 なお、関数本体が実行された場合、関数の戻り値は、returnがある場合はreturnで指定した値、returnが無い場合はブロック内で一番最後に実行した文の結果となる。

yield

yield {| 引数リスト | ブロック} 引数 ...;

ブロックオブジェクトを実行する。その際、引数で与えられた実引数が、引数リストで指定したシンボルにローカル変数として与えられる。 ここで、ブロック内の引数リスト定義が現れた。ブロックの引数リストは、ブロックが yield により実行される際、引数を与えるすることが可能で、 ブロック内でその引数を受け取るための書式である。これは、"|" と "|" で囲まれたローカル変数のリストとして指定する。 引数リストのことをマニュアルでは「バインドリスト」と呼んでいる。

class

class クラス名 親クラス;

クラス名で参照可能なクラスオブジェクトを生成する。その際、親クラスを指定することにより、クラスの継承(動作としてはメソッドの委譲)が行われる。

dict

dict ((key . value) ...);

辞書を生成する。 初期値は、(key . value) のドット対のリストで与える。

try

try {実行ブロック} catch: {| e | エラーハンドラブロック} fin: {終了処理ブロック};

いわゆる try ~ catch ~ finally と同等なもの。エラーの内容(例外シンボルとメッセージのドット対リスト)は、エラーハンドラブロックのバインドリストで渡される。 ここで、e は、(例外シンボル . "メッセージ") のドット対の形式で渡される。

throw

throw 例外シンボル メッセージ;

例外をスローする。スローされた例外は、上位の try コマンドにて補足できる。 また、例外は関数を超えて伝播するため、プログラムでtryにより例外を補足しなかった場合は、最終的にトップレベルのインタプリタがエラーを表示する。 もちろん途中の処理においてtryに例外を捕捉し適切に処理するようにすることも可能である。

exit

exit 終了コード;

処理系を終了する。

eq?

eq? 値1 値2;

値1と値2を比較し、同じであれば<t>をそうでなければ<nil>を返す。値1、値2がプリミティブ型であれば、同値で<t>となる。オブジェクト型であれば、同じオブジェクトであれば<t>を返す。

return

return 値;

関数の戻り値を「値」とし、関数を抜ける。

result

result 値;

値を返す。result :last のように、「:last」オプションを指定することで、resultコマンドの直前の文の値を参照できる。

break

break 値;

イテレータやcondコマンドを抜ける。値を指定した場合は、イテレータの戻り値となる。

continue

continue;

イテレータの残りの文の評価をスキップし、イテレータの次の要素の評価へ移る。

Objectクラス

Objectクラスは全てのオブジェクトの親であり、ここで定義されているメソッドは全てのオブジェクトで利用可能である。

Object::string

Object string;

オブジェクトを文字列として表記可能な情報に変化して返す。

Object::method

Object method メソッド名 (引数リスト) {メソッド本体};

オブジェクトにメソッドを定義する。

Object::set!

Object set! インスタンス変数名 値;

オブジェクト外から、オブジェクトのインスタンス変数に値を設定する。オブジェクト内(メソッド内)では、sets コマンドによりインスタンス変数を設定可能。

Object::get

Object get インスタンス変数名;

オブジェクトに設定されているインスタンス変数の値を取得する。オブジェクト内(メソッド内)では、「$変数名」の参照によりインスタンス変数を参照可能。

Object::apply

Object apply {ブロック};

オブジェクトのスコープでブロックを実行する。メソッドを定義するまでもない処理をオブジェクト内で実行したいときに使用する。

Integerクラス

多倍長整数を扱うクラスである。

Integer::演算子

Integer + 値;
Integer - 値;
Integer * 値;
Integer / 値;
Integer % 値;

これらのメソッドは、整数値に値を、加算、減算、積算、除算、剰余算するという基本的な演算をするものである。

Integer::比較演算子

Integer = 値;
Integer !₌ 値;
Integer > 値;
Integer >= 値;
Integer < 値;
Integer <= 値;

これらのメソッドは、整数値と値を比較し、メソッドに応じた条件により真偽値を返すものである。

Integer::++

Integer ++ [ 増分 ];

整数の値を1インクリメントする。増分が指定された場合は増分だけ加算される。

Integer::--

Integer -- [ 増分 ];

整数の値を1デクリメントする。増分が指定された場合は増分だけ減算される。

Integer::each

Integer each to: 上限値 do: {| i | 処理ブロック};

整数オブジェクトのイテレータ

Integer::..

Integer .. N do: ブロック;

整数のNまでのリストを生成し、do: ブロックでイテレータを実行する。

Realクラス

実数を扱うクラスである。

Real::演算子

Real + 値;
Real - 値;
Real * 値;
Real / 値;
Real % 値;

これらのメソッドは、実数に値を、加算、減算、積算、除算、剰余算するという基本的な演算をするものである。

Real::=比較演算子

Real = 値;
Real !₌ 値;
Real > 値;
Real >= 値;
Real < 値;
Real <= 値;

これらのメソッドは、整数値と値を比較し、メソッドに応じた条件により真偽値を返すものである。

そのほか三角関数、対数関数などもある。

Stringクラス

文字列を扱うクラスである。

String::append!

String append! 値 ...;

文字列を追加する。

String::.

String . 値...;

文字列が追加された新しい文字列を返す。

String::split

String split sep: separator;

文字列を separator で分割しリストで返す。 separator が指定されない場合は連続した空白文字(スペース、改行、タブ)をひとつのセパレータとして分割する。 また、separator が "" の場合は1文字単位で分割する。

String::=~

String =~ regexp;

regexpでマッチした部分を返す。

String::len

String len;

文字列の文字長を返す。

String::sub

String sub start end;

start から end までの部分文字列を返す。

String::eval

String eval;

文字列をPerfumeのスクリプトとして評価する。

Listクラス

リストを扱うクラスである。

List::car

List car;

リストのcar部を返す。

List::cdr

List cdr;

リストのcdr部を返す。

List::len

List len;

リストの要素数を返す。

List::get

List get index;

リストのindex番目の要素を返す。

List::append!

List append! val;

リストの最後にvalを追加する。

List::each

List each do: {| i | 処理ブロック};

リストのイテレータ。

List::filter

List filter {| i | 条件式};

リストを条件式で真になった要素のみで再構築する。

List::map

List map {| i | 処理ブロック};

リストの各要素に処理ブロックを適用した要素で再構築する。

List:inject

List inject 初期値 {| 総和値 個別値 | 処理ブロック};

所謂Rubyのinject。

List::concat

List concat [ 値 | リスト ] ...

元のリストに値またはリストの全要素を追加した新しいリストを返す。

Dictクラス

辞書を扱うクラスである。

Dict::set

Dict set キー 値;

辞書のキーに対して値を設定する。

Dict::set

Dict set? キー;

辞書のキーに対して値が設定されいてる場合は <t> を返す。

Dict::len

Dict get キー;

辞書の要素数を返す。

Dict::get

Dict get キー;

辞書のキーに設定されている値を取得する。

Dict::each

Dict each do: {| k v | 処理ブロック};

辞書のイテレータ。k にキー、v にキーに設定されている値が設定される。

Dict::keys

Dict keys;

辞書に設定されているキーのリストを返す。

Dict::pairs

Dict pairs;

辞書に設定されているキーと値のリストを返す。

Fileクラス

ファイルの入出力を行うためのクラスである。

File::open

File open ファイルパス mode: i | o | io | a;

ファイルパスをモード(入力、出力、入出力、追記)でオープンする。

File::gets

File gets;

ファイルからテキストを1行入力する。

File::puts

File puts 値;

ファイルに値を1行テキストで出力する。

File::close

File close;

ファイルをクローズする。

File::each

File each do: {| r | ファイル行処理ブロック};

ファイルのイテレータである。

その他

上記以外にも、ファーストクラスであるブロックを扱うBlockクラス、ファイルおよびメモリを塊りとして扱うBulkクラス、 コルーチンを実現するCoroクラス、Fileクラスを仮想化したキュー入出力を行うStreamクラス、配列を扱うVectorクラスなどが存在する。 詳細は、上で紹介したPDFのマニュアルを参照のこと。

【実際に試してみる】

Perfumeインタプリタを起動する

(シェルから)
% perfumesh
>               ← perfumeインタプリタのプロンプトが表示される

Hello world

println "Hello world.";

inject (reduce)

1 .. 100 : inject 0 do: {| s i | $s ++ $i};     # 1から100までの総和を表示する。

ファイルを表示

defun mycat (name proc: out) {
    if {set? out} else: {set out {| in | println $in}};
    set f [new File];
    $f open $name mode: i;
    try {
        $f each do: {| r |
            yield $out $r;
        }
    }
    fin: {
        $f close;
    };
};

# 実行例1
mycat "test_file.txt";                  # test_file.txt の内容を標準出力へ表示

# 実行例2
set f [new File];                       # mycat を用いて、test_file.txt の内容を out_file.txt へ出力する
$f open "out_file.txt" mode: o;
try {
    mycat "test_file.txt" proc: {| i | $f puts $i};
} fin: {
    $f close;
};

クロージャ

defun cj-test () {
    set i 0;
    return [fun () {$i ++}];        # クロージャを含む関数オブジェクトを返す
};
set c [cj-test];
$c;     # -> 1
$c;     # -> 2
$c;     # -> 3

fizzbuzz(ループバージョン)

set i 1;
while {$i <= 100} do: {
    println [
        cond
        {[$i % 15] = 0} {break "fizzbuzz"}
        {[$i % 5] = 0}  {break "buzz"}
        {[$i % 3] = 0}  {break "fizz"}
        <t>             {break $i}
        ;       # ← cond コマンドの終わり
    ];
    $i ++;
};

fizzbuzz(リストバージョン)

1 .. 100 do: {| i |
    cond
    {[$i % 15] = 0} {break "fizzbuzz"}
    {[$i % 5] = 0}  {break "buzz"}
    {[$i % 3] = 0}  {break "fizz"}
    <t>             {break $i}
    ;       # ← cond コマンドの終わり
};

quick ソート

defun simple-sort (l) {
    if {[$l len] <= 1} then: {return $l};
    pivot ::= $l >>;
    
    [simple-sort [$l filter {| i | $i < $pivot}]]
      concat ($pivot)
    [simple-sort [$l filter {| i | $i >= $pivot}]];
};

simple-sort (6 10 3 7 9 2 8 1 5 4);             # -> (1 2 3 4 5 6 7 8 9 10)
simple-sort ("を" "ん" "A" "z" "0" "あ");       # -> ("0" "A" "z" "あ" "を" "ん")