数学およびコンピュータ科学において、高階関数(HOF)とは、少なくとも以下のいずれかの条件を満たす関数を指します。
その他の関数はすべて一次関数です。数学では、高階関数は演算子または汎関数とも呼ばれます。微積分における微分演算子は、関数をその導関数(これも関数です)に写像するため、よく知られた例です。高階関数は、数学における「ファンクター」という言葉の他の用法と混同しないように注意してください。詳しくは「ファンクター(曖昧さ回避)」を参照してください。
型なしラムダ計算では、すべての関数は高階関数です。一方、ほとんどの関数型プログラミング言語の派生元である型付きラムダ計算では、1つの関数を引数として取る高階関数は、次の形式の型を持つ値です。。
qsortその一例です。これらの例はプログラミング言語を比較対照することを目的としたものではなく、高階関数構文の例として提示するものです。
以下の例では、高階関数はtwice関数を受け取り、その関数をある値に2回適用しています。twice同じ値に対して複数回適用する必要がある場合は、値ではなく関数を返す方が望ましいでしょう。これは「繰り返しを避けるf」原則に沿ったものです。
2回← { ⍺⍺ ⍺⍺ ⍵ }プラス3 ← { ⍵ + 3 }g ← {プラス3を2回⍵ } g 7 13あるいは暗黙のうちに:
2回← ⍣ 2プラス3 ← + ∘ 3g ←プラス3を2回g 7 13C++11std::functionでの使用例:
import std ;auto twice = []( const std :: function < int ( int ) >& f ) -> auto { return [ f ]( int x ) -> int { return f ( f ( x )); }; } ;auto plusThree = []( int i ) -> int { return i + 3 ; };int main () { auto g = twice ( plusThree );std :: println ( "{}" , g ( 7 )); // 13 }または、C++14で提供される汎用ラムダ式を使用する場合:
import std ;auto twice = []( const auto & f ) -> auto { return [ f ]( int x ) -> int { return f ( f ( x )); }; };auto plusThree = []( int i ) -> int { return i + 3 ; };int main () { auto g = twice ( plusThree );std :: println ( "{}" , g ( 7 )); // 13 }デリゲートのみを使用する:
Systemを使用します。public class Program { public static void Main ( string [] args ) { Func < Func < int , int > , Func < int , int >> twice = f => x => f ( f ( x ));Func < int , int > plusThree = i => i + 3 ;var g = twice ( plusThree );Console.WriteLine ( g ( 7 ) ); // 13 } }あるいは、静的メソッドを使えば、同様に次のようになります。
Systemを使用します。public class Program { private static Func < int , int > Twice ( Func < int , int > f ) { return x => f ( f ( x )); }private static int PlusThree ( int i ) => i + 3 ;public static void Main ( string [] args ) { var g = Twice ( PlusThree );Console.WriteLine ( g ( 7 ) ); // 13 } }( defn twice [ f ] ( fn [ x ] ( f ( f x ))))(定義プラス3 [ i ] ( + i 3 ))( def g ( 2 倍プラス 3 ))( println ( g7 ) ) ;13twice = function ( f ) { return function ( x ) { return f ( f ( x )); }; };plusThree = function ( i ) { return i + 3 ; };g = twice ( plusThree );writeOutput ( g ( 7 )); // 13( defun twice ( f ) ( lambda ( x ) ( funcall f ( funcall f x )))) ( defun plus-three ( i ) ( + i 3 )) ( defvar g ( twice #' plus-three )) ( print ( funcall g 7 ))import std.stdio : writeln ;alias twice = ( f ) => ( int x ) => f ( f ( x ));alias plusThree = ( int i ) => i + 3 ;void main () { auto g = twice ( plusThree );writeln ( g ( 7 )); // 13 }int Function ( int ) twice ( int Function ( int ) f ) { return ( x ) { return f ( f ( x )); }; }int plusThree ( int i ) { return i + 3 ; }void main () { final g = twice ( plusThree ); print ( g ( 7 )); // 13 }Elixirでは、モジュール定義と匿名関数を混在させることができます。
defmodule Hof do def twice ( f ) do fn ( x ) -> f . ( f . ( x )) end end endplus_three = fn ( i ) -> i + 3 endg = Hof.twice ( plus_three )IO.puts g . ( 7 ) # 13あるいは、純粋な匿名関数を用いて合成することも可能です。
twice = fn ( f ) -> fn ( x ) -> f . ( f . ( x )) end endplus_three = fn ( i ) -> i + 3 endg = 2回. (プラス3 )IO.puts g . ( 7 ) # 13or_else ([], _) -> false ; or_else ([ F | Fs ], X ) -> or_else ( Fs , X , F ( X )).or_else ( Fs , X , false ) -> or_else ( Fs , X ); or_else ( Fs , _, { false , Y }) -> or_else ( Fs , Y ); or_else (_, _, R ) -> R .or_else ([ fun erlang : is_integer / 1 , fun erlang : is_atom / 1 , fun erlang : is_list / 1 ], 3 . 23 ).この Erlang の例では、高階関数はor_else/2関数のリスト ( Fs) と引数 ( ) を受け取ります。引数を引数としてX関数を評価します。関数がfalse を返すと、 の次の関数が評価されます。関数がを返すと、引数を持つの次の関数が評価されます。関数がを返すと、高階関数は を返します。、、 は関数であることに注意してください。この例では を返します。FXFFsF{false, Y}FsYFRor_else/2RXYRfalse
f = f >> fを2回としますlet plus_three = (+) 3g = twice plus_threeとするg 7 |> printf "%A" // 13パッケージメインimport "fmt"func twice ( f func ( int ) int ) func ( int ) int { return func ( x int ) int { return f ( f ( x )) } }func main () { plusThree := func ( i int ) int { return i + 3 }g := twice ( plusThree )fmt.Println ( g ( 7 ) ) // 13 }関数リテラルは、識別子 ( twice) を使用して定義することも、匿名で定義することもできます (変数 に代入plusThree)。
def twice = { f , x -> f ( f ( x )) } def plusThree = { it + 3 } def g = twice . curry ( plusThree ) println g ( 7 ) // 13twice :: ( Int -> Int ) -> ( Int -> Int ) twice f = f . fplusThree :: Int -> Int plusThree = ( + 3 )main :: IO () main = print ( g 7 ) -- 13 where g = twice plusThree具体的に言うと、
2回=副詞: 'uu y'plusthree =.動詞: 'y + 3' g =. plusthree 2回g 7 13あるいは暗黙のうちに、
2回=. ^: 2プラス3 =. +& 3 g =.プラス3 2g 7 13機能的なインターフェースのみを使用する:
import java.util.function.* ;class Main { public static void main ( String [] args ) { Function < IntUnaryOperator , IntUnaryOperator > twice = f -> f . andThen ( f );IntUnaryOperator plusThree = i -> i + 3 ;var g = twice.apply ( plusThree ) ;System.out.println ( g.applyAsInt ( 7 ) ) ; // 13 } }あるいは、静的メソッドを使えば、同様に次のようになります。
import java.util.function.* ;class Main { private static IntUnaryOperator twice ( IntUnaryOperator f ) { return f . andThen ( f ); }private static int plusThree ( int i ) { return i + 3 ; }public static void main ( String [] args ) { var g = twice ( Main :: plusThree );System.out.println ( g.applyAsInt ( 7 ) ) ; // 13 } }矢印関数を使用する場合:
「use strict」;const twice = f => x => f ( f ( x ));const plusThree = i => i + 3 ;const g = twice ( plusThree );console.log ( g ( 7 ) ) ; // 13または、古典的な構文で:
「use strict」;function twice ( f ) { return function ( x ) { return f ( f ( x )); }; }function plusThree ( i ) { return i + 3 ; }const g = twice ( plusThree );console.log ( g ( 7 ) ) ; // 13julia> function twice ( f ) function result ( x ) return f ( f ( x )) end return result end twice (1 つのメソッドを持つ汎用関数)julia> plusthree ( i ) = i + 3 plusthree (1 つのメソッドを持つ汎用関数)julia> g = twice ( plusthree ) (::var"#result#3"{typeof(plusthree)}) (1 つのメソッドを持つ汎用関数)ジュリア> g ( 7 ) 13fun twice ( f : ( Int ) -> Int ): ( Int ) -> Int { return { f ( f ( it )) } }fun plusThree ( i : Int ) = i + 3fun main () { val g = twice ( :: plusThree )println ( g ( 7 )) // 13 }function twice ( f ) return function ( x ) return f ( f ( x )) end endfunction plusThree ( i ) return i + 3 endlocal g = twice ( plusThree )print ( g ( 7 )) -- 13function result = twice ( f ) result = @( x ) f ( f ( x )); endplusthree = @( i ) i + 3 ;g = 2倍(プラス3 )disp ( g ( 7 )); % 132回f x = f ( f x )とするlet plus_three = (+) 3let () = let g = twice plus_three inprint_int ( g 7 ); (* 13 *) print_newline ()<?phpdeclare ( strict_types = 1 );function twice ( callable $f ) : Closure { return function ( int $x ) use ( $f ) : int { return $f ( $f ( $x )); }; }function plusThree ( int $i ) : int { return $i + 3 ; }$g = twice ( 'plusThree' );echo $g ( 7 ), " \n " ; // 13または、すべての関数を変数に格納する場合:
<?phpdeclare ( strict_types = 1 );$twice = fn ( callable $f ) : Closure => fn ( int $x ) : int => $f ( $f ( $x ));$plusThree = fn ( int $i ) : int => $i + 3 ;$g = $twice ( $plusThree );echo $g ( 7 ), " \n " ; // 13アロー関数は親スコープから来る変数を暗黙的にキャプチャしますが、[ 1 ]匿名関数ではuse同じことを行うためにキーワードが必要です。
use strict ; use warnings ;sub twice { my ( $f ) = @_ ; sub { $f -> ( $f -> ( @_ )); }; }sub plusThree { my ( $i ) = @_ ; $i + 3 ; }my $g = twice ( \& plusThree );print $g -> ( 7 ), "\n" ; # 13または、すべての関数を変数に格納する場合:
use strict ; use warnings ;my $twice = sub { my ( $f ) = @_ ; sub { $f -> ( $f -> ( @_ )); }; };my $plusThree = sub { my ( $i ) = @_ ; $i + 3 ; };my $g = $twice -> ( $plusThree );print $g -> ( 7 ), "\n" ; # 13def twice ( f : Callable [ Any ]) -> Any : def result ( x : Any ) -> Any : return f ( f ( x )) return resultplus_three : Callable [ int ] = lambda i : i + 3g : int = twice ( plus_three )print ( g ( 7 )) # 13と出力されますPythonのデコレータ構文は、関数を、その関数を高階関数に渡した結果に置き換えるためによく使用されます。例えば、この関数はg以下のように同等に実装できます。
@twice def g ( i : int ) -> int : return i + 3print ( g ( 7 )) # 13と出力されますtwice <- \ ( f ) \ ( x ) f ( f ( x ))plusThree <- function ( i ) i + 3g <- twice ( plusThree )> g ( 7 ) [ 1 ] 13sub twice ( Callable:D $f ) { return sub { $f ( $f ( $^x )) }; } sub plusThree ( Int:D $i ) { return $i + 3 ; } my $g = twice ( &plusThree ); $ g ( 7 ); #13Rakuでは、すべてのコードオブジェクトはクロージャであるため、関数内ではレキシカル変数が「閉じられている」ため、外部スコープから内部の「レキシカル」変数を参照できます。また、Rakuはラムダ式のための「ポインティングブロック」構文をサポートしており、変数に代入したり、匿名で呼び出したりすることができます。
def twice ( f ) -> ( x ) { f . call ( f . call ( x )) } endplus_three = -> ( i ) { i + 3 }g = 2回(プラス3 )puts g.call ( 7 ) # 13fn twice ( f : impl Fn ( i32 ) -> i32 ) -> impl Fn ( i32 ) -> i32 { move | x | f ( f ( x )) }fn plus_three ( i : i32 ) -> i32 { i + 3 }fn main () { let g = twice ( plus_three );println! ( "{}" , g ( 7 )) // 13 }object Main { def twice ( f : Int => Int ): Int => Int = f compose fdef plusThree ( i : Int ): Int = i + 3def main ( args : Array [ String ]): Unit = { val g = twice ( plusThree )print ( g ( 7 )) // 13 } }(定義(合成f g ) (ラムダ( x ) ( f ( g x ))))(定義( 2回f ) (合成f f ))(定義(プラス3 i ) ( + i 3 ))( g ( 2倍プラス3 )を定義)(表示( g 7 )) ; 13 (表示" \n " )func twice ( _ f : @ escaping ( Int ) -> Int ) -> ( Int ) -> Int { return { f ( f ( $0 )) } }let plusThree = { $0 + 3 }let g = twice ( plusThree )print ( g ( 7 )) // 132回設定{{ f x } {apply $f [apply $f $x ] }} plusThree {{ i } {return [expr $i + 3 ]}}# 結果: 13 puts [apply $twice $plusThree 7 ]Tcl は apply コマンドを使用して匿名関数を適用します (バージョン 8.6 以降)。
XACML規格では、属性バッグの複数の値に関数を適用するための高階関数が定義されています。
ルールallowEntry {許可条件anyOfAny(function [ stringEqual ], citizenships , allowedCitizenships ) }XACMLの高階関数の一覧は、こちらで確認できます。
declare function local:twice ( $ f , $ x ) { $ f ( $ f ( $ x )) };declare function local:plusthree ( $ i ) { $ i + 3 };local:twice ( local:plusthree # 1 , 7 ) (: 13 :)C、C++、Fortran、Pascalなどの言語における関数ポインタは、プログラマが関数への参照をやり取りすることを可能にします。以下のCコードは、任意の関数の積分の近似値を計算します。
#include <stdio.h>double square ( double x ) { return x * x ; }double cube ( double x ) { return x * x * x ; }/* 区間 [a,b] 内で f() の積分を計算する */ double integral ( double f ( double x ), double a , double b , int n ) { int i ; double sum = 0 ; double dt = ( b - a ) / n ; for ( i = 0 ; i < n ; ++ i ) { sum += f ( a + ( i + 0.5 ) * dt ); } return sum * dt ; }int main () { printf ( "%g \n " , integral ( square , 0 , 1 , 100 )); printf ( "%g \n " , integral ( cube , 0 , 1 , 100 )); return 0 ; }C標準ライブラリのqsort関数は、関数ポインタを使用して高階関数の動作をエミュレートします。
マクロは、高階関数と同様の効果を実現するためにも使用できます。ただし、マクロでは変数キャプチャの問題を容易に回避することはできません。また、大量の重複コードが発生する可能性があり、コンパイラによる最適化が難しくなる場合があります。マクロは一般的に厳密な型付けはされませんが、厳密な型付けコードを生成することもあります。
他の命令型プログラミング言語では、評価スコープ内でコードを動的に実行すること( EvalまたはExecute操作と呼ばれることもあります)によって、高階関数で得られるアルゴリズム的な結果の一部を実現することが可能です。ただし、このアプローチには重大な欠点があります。
高階関数をサポートしないオブジェクト指向プログラミング言語では、オブジェクトが効果的な代替手段となります。オブジェクトのメソッドは本質的に関数のように動作し、メソッドはオブジェクトをパラメータとして受け取り、オブジェクトを戻り値として生成することができます。ただし、オブジェクトは純粋関数に比べて実行時のオーバーヘッドが増加することが多く、オブジェクトとそのメソッドの定義とインスタンス化のための定型コードも追加されます。スタックベース(ヒープベースではなく)のオブジェクトや構造体を許可する言語は、この方法においてより高い柔軟性を提供できます。
Free Pascalで、関数を返す関数とシンプルなスタックベースのレコードを使用する例を以下に示します。
プログラム例;type int = integer ; Txy = record x , y : int ; end ; Tf = function ( xy : Txy ) : int ; function f ( xy : Txy ) : int ; begin Result := xy . y + xy . x ; end ;function g ( func : Tf ) : Tf ; begin result := func ; end ;変数a : Tf ; xy : Txy = ( x : 3 ; y : 7 ) ;begin a := g ( @ f ) ; // "a" に関数を返すwriteln ( a ( xy )) ; // 10 を出力するend .この関数はレコードを入力としてa()受け取りTxy、レコードのフィールドの合計の整数値(3 + 7)を返しxますy。
関数非化は、第一級関数を持たない言語で高階関数を実装するために使用できます 。
// 非機能化された関数データ構造template < typename T > struct Add { T value ; }; template < typename T > struct DivBy { T value ; }; template < typename F , typename G > struct Composition { F f ; G g ; };// 非機能化された関数適用実装テンプレート< typename F , typename G , typename X > auto apply ( Composition < F , G > f , X arg ) { return apply ( f . f , apply ( f . g , arg )); }template < typename T , typename X > auto apply ( Add < T > f , X arg ) { return arg + f . value ; }template < typename T , typename X > auto apply ( DivBy < T > f , X arg ) { return arg / f . value ; }// 高階合成関数テンプレート< typename F , typename G > Composition < F , G > compose ( F f , G g ) { return Composition < F , G > { f , g }; }int main ( int argc , const char * argv []) { auto f = compose ( DivBy < float > { 2.0f }, Add < int > { 5 }); apply ( f , 3 ); // 4.0f apply ( f , 9 ); // 7.0f return 0 ; }この場合、関数オーバーロードによって異なる関数をトリガーするために異なる型が使用されます。この例のオーバーロードされた関数のシグネチャは ですauto apply。