コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

無名関数

出典: フリー百科事典『ウィキペディア(Wikipedia)』

プログラミング言語における無名関数英語: anonymous functionあるいはnameless function[1]とは、名前付けされずに定義された関数のことである。無名関数を表現するための方法には様々なものがあるが、近年[2]主流となっているのはラムダ式による記法である。無名関数を表現するリテラル式は、関数リテラル (function literal) とも呼ばれる。値がある場合は関数オブジェクトであるものが多い。

ラムダ式

[編集]

ラムダ式 (lambda expression) はラムダ計算と関係が深く、関数型言語で特によく採用されている。

Haskellにおける例を示す。バックスラッシュ '\' がギリシャ文字のラムダ 'λ' のように見えることから、Haskellではラムダ式を表す構文に採用されている[3]

--let add x y = x + y -- 通常の名前付き関数addの定義。
let add = \x y -> x + y -- 2つの引数を取ってその和を返す無名関数を定義し、変数addにバインドする。
print $ add 2 3

Haskellでは変数にバインドせずにラムダ式を直接引数に作用させることもできる。

print $ (\x y -> x + y) 2 3

以下のように高階関数の引数にラムダ式を渡すこともできる。

let calc op x y = op x y
print $ calc (\x y -> x + y) 2 3

関数型ではないプログラミング言語においても、ラムダ式を言語機能として取り入れる動きが活発である。

C#ではC# 3.0にて導入された。以下に例を示す。

Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(2, 3));

C++ではC++11にて導入された。以下に例を示す。

auto add = [](int x, int y) { return x + y; };
std::cout << add(2, 3) << std::endl;

JavaではJava 8にて導入された。以下に例を示す。

import java.util.function.*;
...
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
System.out.println(add.apply(2, 3));

C#の無名関数

[編集]

C#には大きく分けて2種類の無名関数(匿名関数)の記法が存在する[4]。一つは、C# 2.0で導入された匿名メソッド (anonymous method) である[5]。もう一つは、C# 3.0で導入されたラムダ式である。通例、ラムダ式のほうが簡潔であり、またラムダ式は式木として扱うこともできるため、基本的にC# 3.0以降ではラムダ式を用いる。

// 匿名メソッドの例
Func<int, int, int> add = delegate(int x, int y) { return x + y; };
Console.WriteLine(add(2, 3));

C#のラムダ式と匿名メソッドを比較した場合に唯一匿名メソッドが勝っている点は、パラメーターリストを省略できることである。

// public delegate void EventHandler(object sender, EventArgs e);
EventHandler click;
// 匿名関数:引数を使用しない場合、引数リストを省略できる
click = delegate { Console.WriteLine("Clicked"); };
// ラムダ式:推論可能な場合、引数の型名を省略できる
click = (sender, e) => Console.WriteLine("Clicked");
// ラムダ式:引数を使用しない場合も、引数リストの省略はできない
// click = () => Console.WriteLine("Clicked");

なお、ラムダ式や匿名メソッドがデリゲートパラメータとして渡される場合、内部的にはコンパイラによって自動的に命名された(C#開発環境では通常命名できない名前の)クラスが生成され、ラムダ式・メソッドはそのメンバーメソッドとして自動的に命名された後、コンパイルが実行される。つまり、IL化された時点で定義済みの名前付きメソッドのデリゲートを生成して渡す操作へと置換されるため、コンパイル後は通常の(名前付きメソッドの)デリゲート呼び出しと挙動的にはほぼ変わらない扱いとなる。

例えば(実用的ではないものの)リフレクションを介して無名関数の「名前」を見つけ出せば、再度呼び出すことも可能である。

JavaScriptの無名関数

[編集]

JavaScriptではfunctionというキーワード(予約語)を用いて記述する。

var add = function(a, b){ return a + b; }; // 2つの引数を取ってその和を返す無名関数を定義し、変数addにバインドする。
alert(add(2, 3));

ECMAScript 2015を用いる場合は、「アロー関数」という記法を用いることもできる。

var add = (a, b) => { return a + b; }; //アロー関数
    add = (a, b) => a + b; // 値を返すだけの関数の場合、returnと{}を同時に省略可能
alert(add(2, 3));

Luaの無名関数

[編集]

Luaにおける関数は第一級オブジェクトであり、すべての関数が本質的に無名関数である。名前付きの関数とは、関数オブジェクトへの参照を保持する変数にすぎない。

function add(x, y) return x + y end

というコードは、次のコードに対する糖衣構文である。

add = function(x, y) return x + y end

Pythonの無名関数

[編集]

Pythonではlambdaというキーワードを使う。Python特有の注意点として、lambdaの内容には式のみが書け、文は書けない点が挙げられる。

add = lambda a, b: a + b # 2つの引数を取ってその和を返す無名関数を定義し、変数addにバインドする。
print(add(2, 3))

無名関数の特徴

[編集]

メリット

[編集]
  • 一度しか使わない関数の名前を付けなくて済む。名前の衝突を考えなくて済む。
  • 関数の引数などに直接渡せる。

特にC++ではアルゴリズム関数テンプレートにおける述語 (predicate) としての関数オブジェクトの明示的な定義が不要になり、簡潔なコードを記述しやすくなる。

C++14での例を示す。

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
  const std::vector<int> ary { 1, 5, -1, 3, 0, -2, };
  std::cout << "Count of negative numbers = " << std::count_if(ary.begin(), ary.end(), [](auto x) { return x < 0; }) << std::endl;
  return 0;
}

デメリット

[編集]
  • 名前で参照できないため、再帰のためには何らかのテクニックがほぼ必要になる(無名再帰を参照)。

言語にもよるが、ラムダ式には通常の関数あるいはメソッドよりも機能制限がある。C# 7.0ではラムダ式の欠点を補うことのできるローカル関数が導入された。

脚注

[編集]
  1. ^ 「anonymous」は「匿名の」という意味も持つため、「anonymous function」は日本語で「匿名関数」と訳されることも多いが、「匿名」は(本当の)名前を匿(かく)すことを意味する。anonymous functionの「anonymous」は「名前がない」ということを意味しているので、ニュアンス的には「無名関数」のほうが適切な訳である。
  2. ^ “Introduction to Data Science in Python”, University of Michigan, https://www.coursera.org/learn/python-data-analysis 2018年6月13日閲覧。 
  3. ^ Anonymous function - HaskellWiki
  4. ^ 匿名関数 (C# プログラミング ガイド) | Microsoft Docs
  5. ^ マイクロソフトによるMSDN日本語版ドキュメントでは「匿名関数」「匿名メソッド」としているので、この節ではそれに従っている。「無名関数」と「匿名関数」とで技術的な違いがあるわけではない。匿名メソッド (C# プログラミング ガイド) | Microsoft Docs

参考文献

[編集]

関連項目

[編集]