0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

スクリプト言語 KINX/関数とクロージャ

Last updated at Posted at 2020-03-02

はじめに

前回の続き。過去の解説は以下を参照。概要は以下の初回記事「スクリプト言語 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 のクラスはまさにその 延長線上 にある。

おわりに

ということで、次はクラス。モジュールも実装したので、合わせてお届けしよう。

例によって★欲しさのための宣伝。ちょっとずつ増えているのが非常にうれしい。皆さん、ありがとうございます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?