
コンピュータプログラミングにおいて、コールバックとは、関数参照をあるコンテキスト(コンシューマー)から別のコンテキスト(プロバイダ)に渡して、プロバイダがその関数を呼び出すプログラミングパターンです。関数がコンシューマーの状態や機能にアクセスする場合、呼び出しはコンシューマーに戻ります。これは、コンシューマーがプロバイダを呼び出す通常の制御フローとは逆方向です。
コールバックパラメータを受け取る関数は、呼び出し元に処理を戻す前にコールバックを実行するように設計されている場合があります。しかし、より一般的なのは、プロバイダが後で関数を呼び出すことができるように、コールバック参照を保存することです(遅延呼び出し)。プロバイダがコンシューマーと同じスレッドでコールバックを呼び出す場合、その呼び出しはブロッキング、つまり同期呼び出しとなります。一方、プロバイダが別のスレッドでコールバックを呼び出す場合、その呼び出しはノンブロッキング、つまり非同期呼び出しとなります。
コールバックは、仕立て屋にスーツの仕立て完了時に何をすべきか指示するのと似ています。例えば、特定の電話番号に電話をかける、指定された住所に届けるなどです。これらの指示がコールバックです。コールバックとは、事前に提供され、後で実行される関数であり、多くの場合、指示を受け取ったシステムとは別のシステムによって実行されます。
一般的な関数参照とコールバックの違いは微妙な場合があり、これらの用語を区別なく使用する人もいますが、一般的にはプログラミングの意図によって区別されます。電話のコールバックのように、最初に呼び出された側が最初に呼び出した側に応答するという意図であれば、それはコールバックです。
ブロッキングコールバックは、コールバックを渡す関数の実行コンテキストで実行されます。遅延コールバックは、割り込み時やスレッド内など、異なるコンテキストで実行できます。そのため、遅延コールバックは同期処理や、別のスレッドへの処理委譲に利用できます。
コールバックはイベント処理に使用できます。多くの場合、処理対象のコードは特定の種類のイベントに対してコールバックを登録します。そのイベントが発生すると、コールバックが呼び出されます。コールバックは、ウィンドウシステムで実行されるプログラムのグラフィカルユーザーインターフェイス(GUI)をプログラミングする際によく使用されます。アプリケーションは、ウィンドウシステムが呼び出すためのカスタムコールバック関数への参照を提供します。ウィンドウシステムはこの関数を呼び出して、マウスのクリックやキーの押下などのイベントをアプリケーションに通知します。
コールバック関数は、非同期処理を実装するために使用できます。
呼び出し元は何らかのアクションを要求し、そのアクションが完了したときに呼び出されるコールバックを指定します。このコールバックは、要求が行われてからかなり時間が経過した後に呼び出される場合もあります。
コールバックを使用してポリモーフィズムを実装できます。次の擬似コードでは、またはのsay_hiいずれかを受け取ることができます。write_statuswrite_error
from typing import Callabledef write_status ( message : str ) -> None : write ( stdout , message )def write_error ( message : str ) -> None : write ( stderr , message )def say_hi ( write : Callable [[ str ], None ]) -> None : write ( "Hello world" )コールバック技術の実装方法は、プログラミング言語によって異なります。
アセンブリ言語、C、C++、Pascal、Modula-2、その他の言語では、コールバック関数は関数ポインタとして内部的に格納されます[ 1 ]。同じストレージを使用することで、異なる言語間で設計時または実行時の相互運用性レイヤーなしでコールバックを直接共有できます。たとえば、Windows API は複数の言語、コンパイラ、アセンブラからアクセスできます。C++ では、オブジェクトが関数呼び出し操作の実装を提供することも可能です。標準テンプレートライブラリは、これらのオブジェクト (ファンクタと呼ばれる) をパラメータとして受け入れます。JavaScript 、Lua、Python、Perl [ 2 ] [ 3 ]、PHPなどの多くの動的言語では、関数オブジェクトを渡すことができます。C #やVisual Basic (.NET) (VB.NET)などのCLI 言語では、デリゲートと呼ばれる型安全なカプセル化関数参照が提供されます。.NET言語で使用されるイベントとイベントハンドラは、コールバックを提供します。関数型プログラミング言語は一般的に第一級関数をサポートしており、これらの関数は他の関数へのコールバックとして渡したり、データとして保存したり、関数から返したりすることができる。
Perl、Python、Ruby、Smalltalk、C++ (11+)、C#、VB.NET (新バージョン) など多くの言語、およびほとんどの関数型言語は、一般的にコールバックとして機能するインライン構文を持つ無名関数であるラムダ式をサポートしています。Scheme、ML、JavaScript、Perl、Python、Smalltalk、PHP (5.3.0 以降)、[ 4 ] C++ (11+)、Java (8 以降)、[ 5 ]など多くの言語では、ラムダはクロージャ、つまりラムダが定義されているコンテキストでローカルに定義された変数にアクセスできます。関数値引数より前のJavaバージョンなどのオブジェクト指向プログラミング言語では、インターフェースを実装するオブジェクトを渡すことでコールバックの動作を実現できます。このオブジェクトのメソッドがコールバックです。PL/IおよびALGOL 60では、コールバックプロシージャは包含ブロック内のローカル変数にアクセスする必要がある場合があるため、エントリポイントとコンテキスト情報の両方を含むエントリ変数を介して呼び出されます。 [ 6 ]
コールバックにはさまざまな用途があります。例えば、エラー通知の場合、UnixプログラムはSIGTERMシグナルを受け取ったときにすぐに終了したくない場合があるため、終了処理が適切に行われるように、クリーンアップ関数をコールバックとして登録します。
#include <signal.h> #include <stdlib.h>#include <fcntl.h> #include <sys/types>struct Data { // ... };static struct Data data = { // ここで内容を初期化します}; static volatile sigatomic_t flag = 0 ;void cleanupHandler ( int signum ) { flag = 1 ; int saveFile = open ( "savefile.dat" , O_WRONLY | O_CREATO , S_IWUSR ); write ( saveFile , data , sizeof data ); close ( saveFile ); }int main ( void ) { signal ( cleanupHandler , SIGTERM ); doStuff (); }C言語におけるコールバックのもう1つの一般的な用途は、ソート()や検索(、 )に使用されるC標準ライブラリ関数で、比較関数がルーチンへの引数として渡され、照合順序が決定される場合である。[ 7 ]qsort()lsearch()bsearch()
#include <stdlib.h>struct Person { char name [ 20 ]; int age ; };人[ 1000 ] ;// 年齢別に人々を分類するint compareAges ( struct Person * p1 , struct Person * p2 ) { return p2 -> age - p1 -> age ; }int main ( void ) { // ... int numberOfPeople = /* ここに何らかの数値 */ ;qsort ( numberOfPeople , sizeof ( struct Person ), people , compareAges ); // ... }コールバックは、関数が動作するかどうかを制御するためにも使用できます。Xlibでは、プログラムがイベントを処理するかどうかを決定するカスタム述語を指定できます。次のCコードでは、関数はブロッキングコールバックとしてprintNumber()パラメータを使用します。はコールバック関数として動作するで呼び出されます。実行すると、質問に対する答えは「値: 42」となります。getNumberprintNumber()getAnswerToMostImportantQuestion()
#include <stdio.h> #include <stdlib.h>void printNumber ( int ( * getNumber )( void )) { int val = getNumber (); printf ( "値: %d \n " , val ); }int getAnswerToMostImportantQuestion ( void ) { return 42 ; }int main ( void ) { printNumber ( getAnswerToMostImportantQuestion ); return 0 ; }C++では、関数ポインタに加えてファンクタも使用できます。ファンクタは、operator()定義されたオブジェクトです。たとえば、のオブジェクトはstd::viewsファンクタです。以下は、C++でファンクタを使用する例です。
import std ;class MyCallback { public : void operator ()( int x ) { std :: println ( "コールバックが値 {} で呼び出されました" , x ); } };template < typename Callback > void performOperation ( int a , Callback callback ) { std :: println ( "Performing operation on: {}" , a ); callback ( a ); }int main () { MyCallback callback ; int value = 10 ; performOperation ( value , callback ); return 0 ; }std::function<R(Args...)>は、 C++11で導入された、呼び出し可能なオブジェクトのための型消去されたラッパーです。
import std ;std :: functionを使用します。void performOperation ( int a , function < void ( int ) > callback ) { std :: println ( "Performing operation on: {}" , a ); callback ( a ); }int main () { int value = 10 ; performOperation ( value , []( int x ) -> void { std :: println ( "コールバックが値 {} で呼び出されました" , x ); });return 0 ; }以下のC#コードでは、メソッドはブロッキングコールバックとしてHelper.PerformActionパラメーターを使用します。 はコールバック関数として機能して呼び出されます。実行すると、コンソールに「Callback was: Hello world」と表示されます。callbackHelper.PerformActionLog
名前空間Wikipedia.Examples ;Systemを使用します。class Helper { public void PerformAction ( Action < string > callback ) { callback ( "Hello world" ); } }public class Main { static void Log ( string str ) { Console . WriteLine ( $"Callback was: {str}" ); }static void Main ( string [] args ) { Helper helper = new () ; helper.PerformAction ( Log ) ; } }以下のJavaScriptコードでは、関数はブロッキング コールバックとしてcalculateパラメータを使用します。は で呼び出され、次に で呼び出されます。これらはコールバック関数として機能します。operatecalculatemultiplysum
function calculate ( a , b , operate ) { return operate ( a , b ); } function multiply ( a , b ) { return a * b ; } function sum ( a , b ) { return a + b ; } // 出力は 20 alert ( calculate ( 10 , 2 , multiply )); // 出力は 12 alert ( calculate ( 10 , 2 , sum ));jQueryライブラリ.each()のcollectionメソッドは、渡された関数をブロッキングコールバックとして使用します。コレクションの各項目に対してコールバックを呼び出します。例:
$ ( "li" ). each ( function ( index ) { console.log ( index + ": " + $ ( this ) .text ( )); } );遅延コールバックは、ユーザー、クライアント、タイマーからのイベントを処理するためによく使用されます。例はaddEventListener、、Ajax、およびにありますXMLHttpRequest。 [ 8 ]
JavaScript ソースコードでコールバックを使用することに加えて、関数を受け取る C 関数は js-ctypes を介してサポートされています。[ 9 ]
以下のJuliaコードでは、関数はブロッキング コールバックとして使用されるcalculateパラメータを受け取ります。 はコールバック関数として機能するとともに呼び出されます。operatecalculatesquare
julia> square ( val ) = val ^ 2 square (1 つのメソッドを持つ汎用関数) julia> calculate ( operate , val ) = operate ( val ) calculate (1 つのメソッドを持つ汎用関数) julia> calculate ( square , 5 ) 25以下のKotlinコードでは、関数はブロッキングコールバックとしてaskAndAnswerパラメータを使用します。 はコールバック関数として機能して呼び出されます。実行すると、質問に対する答えは「42」になります。getAnsweraskAndAnswergetAnswerToMostImportantQuestion
fun main () { print ( "最も重要な質問を入力してください: " ) val question = readLine () askAndAnswer ( question , :: getAnswerToMostImportantQuestion ) }fun getAnswerToMostImportantQuestion (): Int { return 42 }fun askAndAnswer ( question : String?, getAnswer : ( ) -> Int ) { println ( "質問: $ question " ) println ( "回答: ${ getAnswer () } " ) }このLuaコードでは、関数はブロッキング コールバックとして使用されるパラメータcalculateを受け取ります。 はと の両方で呼び出され、その後匿名関数を使用して分割します。operationcalculateaddmultiply
function calculate ( a , b , operation ) return operation ( a , b ) endfunction multiply ( a , b ) return a * b endfunction add ( a , b ) return a + b endprint ( calculate ( 10 , 20 , multiply )) -- 出力は 200 print ( calculate ( 10 , 20 , add )) -- 出力は 30 -- 匿名関数を使用したコールバックの例print ( calculate ( 10 , 20 , function ( a , b ) return a / b -- 出力は 0.5 end ))以下のPythonコードでは、関数はブロッキング コールバックとして使用されるcalculateパラメータを受け取ります。 はコールバック関数として機能するとともに呼び出されます。operatecalculatesquare
def square ( val : int ) -> int : return val ** 2def calculate ( operate : Callable [[ int ], int ], val : int ) -> int : return operate ( val )# 出力: 25 print ( calculate ( square , 5 ))以下のRebolとRedのコードは、コールバックの使用方法を示しています。
赤[タイトル: "コールバックの例" ]calculate: func [ num1 [ number! ] num2 [ number! ] callback-function [ function! ] ][ callback-function num1 num2 ]calc-product: func [ num1 [ number ! ] num2 [ number! ] ][ num1 * num2 ]calc-sum: func [ num1 [ number! ] num2 [ number! ] ][ num1 + num2 ]; アラート 75、5 と 15 の積アラートフォームcalculate 5 15 :calc-product; アラート 20、5 と 15 の合計アラートフォーム計算5 15 :calc-sumRust はFn、、FnMutおよび特性を持っていますFnOnce。[ 10 ]
fn call_with_one < F > ( func : F ) -> usize where F : Fn ( usize ) -> usize { func ( 1 ) }let double = | x | x * 2 ; assert_eq! ( call_with_one ( double ), 2 );