無名関数

無名関数の書き方。

ラムダ式とかクロージャとかの概念と関係あるかもしれない。

通常の関数定義・メソッド定義は関数定義・メソッド定義参照。

C++

C++11やC++14にはlambda式があるらしい。

本の虫: C++14の新機能: ジェネリックlambda
http://cpplover.blogspot.jp/2014/09/c14-lambda_30.html

Java

Java8からは、関数型インターフェースのインスタンスとしてラムダ式が書けるようになった。

ラムダ式

抽象メソッド1つだけのインターフェースを関数型インターフェース(SAM Type, single abstract method type)といい、 Java8からは、関数型インターフェースを継承する匿名クラスのインスタンスをラムダ式のように簡単に書けるようになっている。

Java7までの書き方の例

Collections.sort(lst, new Comparator<String>(){
    public int compare(String str1, String str2){
        str1.compareToIgnoreCase(str2);
    }
});

Java8からの書き方の例

Collections.sort(lst, (str1, str2) -> str1.compareToIgnoreCase(str2));

この書き方をしても関数が第一級オブジェクトになるわけではなく、あくまで関数型インターフェースの匿名クラスのインスタンスになる。

関数の引数には型を書くこともできるが、普通は型推論ができるので、上記のように省略する。

引数の型を書く例

Collections.sort(lst, (String str1, String str2) -> str1.compareToIgnoreCase(str2));

引数が1つで型を省略する場合は、引数のカッコも省略できる。

引数と本体の間の矢印はScalaでは => だが、Java8では -> を使う。

本体部分に式を直接書くと、その式が関数の返り値となる。本体部分を {} で囲むと、メソッド定義と同じように、返り値はreturn文を書く必要がある。

匿名クラスの中で this と書くと、匿名クラス自体のインスタンスを指すが、ラムダ式の中で this と書くと、その関数の外側のインスタンスを指すことになる。このラムダ式の見た目が一見クラスのようには見えないので、この仕様のほうが確かに直感通りかもしれない。

ラムダ式が動作する仕組み

ラムダ式のクラス(SAM Type のサブクラス)は、コンパイル時にはクラスファイルが生成されず、属する外側のクラスの1メソッドとしてコンパイルされる。そのラムダ式を初めて生成するときにJVMの中で動的にクラスファイルが生成されてロードされる。その際にinvokedynamicが使われている。

ラムダ式ごとに事前にクラスファイルを生成すると、クラスローディングが重くなって実行時のパフォーマンスに影響するからだと思われる。

詳しくはこちらにある(全部は読めてないが)
http://www.myexpospace.com/JavaOne2012/SessionFiles/CON6080_PDF_6080_0001.pdf

メソッド参照

ラムダ式の代わりにメソッド名でも、関数型インターフェースのインスタンスを生成できる。

Object::toString // x -> x.toString() と同じ

Object a = ...;
a::toString // () -> a.toString() と同じ

StringBuilder::new // () -> new StringBuilder() と同じ

Scala

Scalaは無名関数を簡単に書ける仕組みがいくつかあり、高階関数を使いこなすのが簡単。

2つの文字列を引数にとってそれを比較する関数の例

(str1, str2) => str1 compareToIgnoreCase str2

引数の型を書く例

(str1: String, str2: String) => str1 compareToIgnoreCase str2

引数が1つの場合は、引数のカッコも省略できる。

引数と本体の間の矢印はJava8では -> だが、Scalaでは => を使う。 Scalaでは -> は別の意味の演算子として存在する。

本体部分の式が関数のの返り値となる。本体部分を {} で囲むと、複数の式を書けるが、最後の式が返り値となる。

別の書き方として _ を使って関数の本体部分のみを書くこともできる。

_ compareToIgnoreCase _

式の中のそれぞれの _ が引数になるので、上記の場合は2引数の関数となる。この書き方だと各引数は1回しか式の中に登場できず、引数の順番は、式の中の _ と同じ順番になる。なので複数の引数の関数を書くには制約が多いし、読みづらいが、 1引数の非常に簡単な式の関数を書くには _ は便利である。

PHP

PHP5.3以降で使える無名関数の例

$f = function ($arg) {
    echo "$arg\n";
};
$f("Hello");

PHP7以降で使える、無名関数をその場で呼び出す例

(function ($arg) {
    echo "$arg\n";
})("Hello");

無名関数を宣言している外側の変数を無名関数の中から参照するには、 use で宣言する必要がある。代入する場合はさらに & を付ける。

$var1 = ...;
$f = function ($arg) use ($var1) {
    echo "$arg, $var1\n";
}
$f("Hello");

$var1 = ...;
$var2 = ...;
$f = function ($arg) use (&$var1, &$var2) {
    $var1 = $arg;
    $var2 = $arg;
}
$f("Hello");

無名関数はClosureクラスのインスタンスになる。メソッド定義の中で無名関数を定義しても、その無名関数は外側のクラスとは別のClosureクラスの扱いになるので、外側のクラスのprivateなフィールドやメソッドにはアクセスできない。(※PHPのバージョンによって違うかも)

無名関数 | PHP Manual
http://php.net/manual/ja/functions.anonymous.php

Closure クラス | PHP Manual
http://www.php.net/manual/ja/class.closure.php

無名関数の他に、可変関数という、関数名の文字列が入った変数からその関数を呼び出す仕組みがある。

function f($arg) {
    echo "$arg\n";
}

$fname = "f";
$fname("Hello");

可変関数 | PHP Manual
http://www.php.net/manual/ja/functions.variable-functions.php

Perl

無名関数の例

my $f = sub {
    ...;
};

$f->(...);

無名関数をその場で呼び出す例

sub {
    ...;
}->(...);

クロージャーとして使う例

sub createCounter {
    my $counter = 0;
    sub {
        $counter++;
        print "$counter\n";
    }
}

my $f1 = createCounter;
$f1->(); # => 1
$f1->(); # => 2
$f1->(); # => 3

my $f2 = createCounter;
$f2->(); # => 1

$f1->(); # => 4

関数の必要な文脈で定義済みのサブルーチンを参照したい場合は、サブルーチンの名前に \& を付けた関数への参照を使えばよい。

sub sb {
    ...;
}
my $f1 = \&sb;
$f1->();

R言語

f <- function(x){
    ...
    return(...)
}

JavaScript

var f = function(arg1, arg2){
    ...;
    return ...;
}

CoffeeScript

f = (arg1, arg2) ->
    ...
このサイトは筆者(hydrocul)の個人メモの集合です。すべてのページは永遠に未完成です。
スポンサーリンク