はじめに
前回の続き。過去の解説は以下を参照。概要は以下の初回記事「スクリプト言語 KINX(ご紹介)」を参照してください。
- スクリプト言語 KINX(ご紹介)
- Kinx 基本編(1) - プログラム基礎・データ型
- Kinx 基本編(2) - 制御構造
- Kinx 要素 - 関数とクロージャ(今回)
とりあえず「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」なスクリプト言語 KINX、というキャッチフレーズを書いておかないと、なぜこの記事に JavaScript とか Ruby とかのタグが付いているかの説明ができないな。AC/DC は付ける必要ないけど。もちろん某作品へのオマージュを含んでいるので、正式なキャッチフレーズには使えなさそうですが。
関数とクロージャ
見た目は JavaScript の代表。ただ、最近の JavaScript はこんな感じじゃなくなってきているな。変わりすぎだよ。
関数
通常の関数
構造化プログラミングの時代から、共通の処理はひとつにまとめることは行われてきた。オブジェクト指向全盛の今でもそれは変わらない。
function add(a, b) {
return a + b;
}
System.println(add(1, 2)); // 3
引数を受け取り、色々とホゲホゲして値を返すことができる。
引数は、今どきの JavaScript で超絶便利になったスプレッド演算子によって分解、統合が可能。まるで JavaScript の話をしているみたいだが、Kinx の話だ。この機能によって可変引数なんかも実現できる。
function toArray(...a) {
var b = [...a]; // copy
return b + [3];
}
System.println(toArray(1, 2).join(', ')); // "1, 2, 3"
ちなみにこのサンプルでやっていることに意味は無いので...、機能説明用。
再帰呼び出しも可能。ただし、末尾再帰の最適化はやってない。やるのは簡単だと思う。尚、実際やるとしても実行時にしか判断できないのと、スタックトレースが正しく取得できない問題がある(そういうものだと思えばいいのかもしれない)。
function fib(n) {
if (n < 3) return n;
return fib(n-2) + fib(n-1);
}
System.println("fib(34) = ", fib(34)); // fib(34) = 9227465
さらに、現代の関数は単に処理が共通化できるだけではないのがミソだ。
関数は静的スコープ(レキシカル・スコープ)を持ち、クロージャが実現可能。クロージャに関しては後述する。
無名関数
無名関数として式中に直接関数定義を書ける。関数を別の関数の引数にもできる。
var calc = function(func, a, b) {
return func(a, b);
};
System.println(calc(function(a, b) { return a + b; }, 2, 3)); // 5
ラムダ式
上記の無名関数をより簡潔に書けるようにしたのがラムダ式。function
の代わりに &
を、ブロックの代わりに => expression
の形をとる。上記サンプルを書き換えるとこうなる。
var calc = &(func, a, b) => func(a, b);
System.println(calc(&(a, b) => a + b, 2, 3)); // 5
実は、expression
の部分はブロックも取れるので、何か複数の処理を記載したい場合は &(args) => { ... }
の形で記載することができる。逆にこの機能のため、expression
に直接オブジェクトを書くことができない点に注意。オブジェクトを直接返したい場合は、({ a: 100 })
とかっこで括ることで回避する。
ブロック・オブジェクト
派生して、ブロック・オブジェクトを定義できるようにした。以下のように &{...}
の形でブロックを引数として渡すことができる。
var a = 10;
var doit = &(block) => block();
System.println(doit(&{
return a + 100;
}));
中身は引数無しの無名関数でしかないのだが、ブロックを渡してる感じが出ていいかなと思って。単純に &() => { ... }
をさらに簡潔に記述できるようにしただけ。
話はそれるが、&(a,b) => {...}
を &{|a,b| ... }
とか書けるようにすると多少 Ruby っぽくなる...かな。意味があるかどうかは別として。実装は簡単。見た目は JavaScript のポリシーからするとやってはいけないのかもしれないが、思想は Ruby で何でもありな世界観だと思えばやっていいのかもしれない。
時を戻そう。
クロージャ
クロージャとは静的スコープを持つ関数オブジェクトのこと。よくあるサンプルは以下。
function newCounter() {
var i = 0; // a lexical variable.
return function() { // an anonymous function.
++i; // a reference to a lexical variable.
return i;
};
}
var c1 = newCounter();
System.println(c1()); // 1
System.println(c1()); // 2
System.println(c1()); // 3
System.println(c1()); // 4
System.println(c1()); // 5
静的スコープを持つことによって、関数呼び出しに対して状態を持つことができる所がポイント。ん?クラス・インスタンスみたいだって?正解。実際、ある意味 JavaScript のクラスの概念はここから派生している。そして、Kinx のクラスはまさにその 延長線上 にある。
おわりに
ということで、次はクラス。モジュールも実装したので、合わせてお届けしよう。
例によって★欲しさのための宣伝。ちょっとずつ増えているのが非常にうれしい。皆さん、ありがとうございます。