コールバックをわかりやすい英語で説明するにはどうすればよいですか?それらは、呼び出し元の関数からいくつかのコンテキストを取得して、ある関数を別の関数から呼び出すのとどう違うのですか?彼らの力を初心者プログラマーにどのように説明できますか
コールバックをわかりやすい英語で説明するにはどうすればよいですか?それらは、呼び出し元の関数からいくつかのコンテキストを取得して、ある関数を別の関数から呼び出すのとどう違うのですか?彼らの力を初心者プログラマーにどのように説明できますか
回答:
多くの場合、アプリケーションはそのコンテキスト/状態に基づいてさまざまな機能を実行する必要があります。このために、呼び出される関数に関する情報を格納する変数を使用します。itsアプリケーションは、必要に応じて、呼び出される関数に関する情報をこの変数に設定し、同じ変数を使用して関数を呼び出します。
JavaScriptでの例は次のとおりです。ここでは、関数に関する情報を格納する変数としてメソッド引数を使用します。
function processArray(arr, callback) {
var resultArr = new Array();
for (var i = arr.length-1; i >= 0; i--)
resultArr[i] = callback(arr[i]);
return resultArr;
}
var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]
function(arg)
に)processArray(arr,callback)
機能
私はこの死者をシンプルに保つようにしようとしています。「コールバック」は、最初の関数をパラメーターとして取る別の関数によって呼び出される関数です。多くの場合、「コールバック」は何かが発生したときに呼び出される関数です。その何かは、プログラマーの言葉では「イベント」と呼ぶことができます。
このシナリオを想像してみてください:数日でパッケージを期待しています。パッケージはあなたの隣人への贈り物です。したがって、パッケージを取得したら、それを隣人に持ち帰る必要があります。あなたは町の外にいるので、配偶者に指示を残します。
あなたは彼らにパッケージを手に入れて隣人に持って行くように言うことができます。あなたの配偶者がコンピューターのように愚かだった場合、彼らはドアに座って荷物が来るまで待って(それ以外は何もしません)、それが来たら隣人に持ち込みます。しかし、もっと良い方法があります。配偶者に、一度パッケージを受け取ったら、隣人に持っていくように伝えます。その後、パッケージを受け取るまで、通常どおりに生活できます。
この例では、パッケージの受信が「イベント」であり、ネイバーにパッケージをもたらすことが「コールバック」です。配偶者は、パッケージが到着したときにのみパッケージを持ち込むように指示を「実行」します。ずっといい!
このような考え方は日常生活の中で当たり前のことですが、コンピューターには同じような常識はありません。プログラマーが通常ファイルに書き込む方法を検討してください。
fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does
ここでは、ファイルに書き込む前に、ファイルが開くのを待ちます。これは実行の流れを「ブロック」し、プログラムは実行する必要があるかもしれない他のことはできません!代わりにこれを行うことができた場合はどうなりますか?
# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!
いくつかの言語とフレームワークでこれを行うことがわかりました。超カッコイイ!Node.jsをチェックして、この種の考え方を実際に実践してください。
open
機能するかについて仮定をしているようです。open
OSがブラックマジックを実行するのを待っている間に内部的にブロックし、コールバックが実行されるのはもっともらしいことです。そのような場合でも結果に違いはありません。
コールバックをわかりやすい英語で説明するにはどうすればよいですか?
わかりやすい英語では、コールバック関数は、タスクを完了したときにマネージャーに「コールバック」するワーカーのようなものです。
それらは、呼び出し元の関数からいくつかのコンテキストを取得して、ある関数を別の関数から呼び出すのとどう違うのですか?
別の関数から関数を呼び出すことは事実ですが、重要なのは、コールバックがオブジェクトのように扱われるため、システムの状態(戦略デザインパターンなど)に基づいて呼び出す関数を変更できることです。
彼らの力を初心者プログラマーにどのように説明できますか
コールバックの力は、サーバーからデータをプルする必要があるAJAXスタイルのWebサイトで簡単に確認できます。新しいデータのダウンロードには時間がかかる場合があります。コールバックがないと、新しいデータのダウンロード中にユーザーインターフェイス全体が「フリーズ」するか、ページの一部ではなくページ全体を更新する必要があります。コールバックを使用すると、「現在ロード中」のイメージを挿入し、ロードされたら新しいデータで置き換えることができます。
function grabAndFreeze() {
showNowLoading(true);
var jsondata = getData('http://yourserver.com/data/messages.json');
/* User Interface 'freezes' while getting data */
processData(jsondata);
showNowLoading(false);
do_other_stuff(); // not called until data fully downloaded
}
function processData(jsondata) { // do something with the data
var count = jsondata.results ? jsondata.results.length : 0;
$('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
$('#results_messages').html(jsondata.results || '(no new messages)');
}
以下は、jQueryのgetJSONを使用したコールバックの例です。
function processDataCB(jsondata) { // callback: update UI with results
showNowLoading(false);
var count = jsondata.results ? jsondata.results.length : 0;
$('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
$('#results_messages').html(jsondata.results || '(no new messages)');
}
function grabAndGo() { // and don't freeze
showNowLoading(true);
$('#results_messages').html(now_loading_image);
$.getJSON("http://yourserver.com/data/messages.json", processDataCB);
/* Call processDataCB when data is downloaded, no frozen User Interface! */
do_other_stuff(); // called immediately
}
多くの場合、コールバックはを使用state
して呼び出し元の関数からアクセスする必要がありますclosure
。これは、ワーカーがタスクを完了する前にマネージャーから情報を取得する必要があるようなものです。を作成するには、関数をインライン化して、呼び出し側のコンテキストでデータが見えるようにします。closure
/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) {
if (null == dtable) { dtable = "messages"; }
var uiElem = "_" + dtable;
showNowLoading(true, dtable);
$('#results' + uiElem).html(now_loading_image);
$.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
// Using a closure: can "see" dtable argument and uiElem variables above.
var count = jsondata.results ? jsondata.results.length : 0,
counterMsg = ['Fetched', count, 'new', dtable].join(' '),
// no new chatters/messages/etc
defaultResultsMsg = ['(no new ', dtable, ')'].join('');
showNowLoading(false, dtable);
$('#counter' + uiElem).text(counterMsg);
$('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
});
/* User Interface calls cb when data is downloaded */
do_other_stuff(); // called immediately
}
// update results_chatters when chatters.json data is downloaded:
grab("chatters");
// update results_messages when messages.json data is downloaded
grab("messages");
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback);
最後に、ここでの定義であるclosure
から、ダグラス・クロックフォードは:
関数は他の関数の内部で定義できます。内部関数は、外部関数の変数とパラメーターにアクセスできます。内部関数への参照が存続する場合(たとえば、コールバック関数として)、外部関数の変数も存続します。
以下も参照してください。
「コールバック」という言葉が2つの矛盾した方法で使用されるようになったという現実を強調することに失敗している非常に多くのインテリジェントな人々を見ると、私は驚きます。
どちらの方法でも、既存の関数に追加機能(匿名または名前付きの関数定義)を渡すことにより、関数をカスタマイズできます。すなわち。
customizableFunc(customFunctionality)
カスタム機能がコードブロックに単純にプラグインされている場合は、そのように関数をカスタマイズしています。
customizableFucn(customFunctionality) {
var data = doSomthing();
customFunctionality(data);
...
}
この種の挿入された機能は、しばしば「コールバック」と呼ばれますが、それについて偶然なことは何もありません。非常に明白な例は、配列を変更するために配列の各要素に適用される引数としてカスタム関数が提供されるforEachメソッドです。
ただし、これは、AJAXやnode.jsのように、または単にユーザーインタラクションイベント(マウスクリックなど)に機能を割り当てる場合のように、非同期プログラミングの「コールバック」関数の使用とは根本的に異なります。この場合、全体的なアイデアは、カスタム機能を実行する前に、偶発的なイベントが発生するのを待つことです。これは、ユーザーの操作の場合は明らかですが、ディスクからファイルを読み取るなど、時間がかかる可能性があるI / O(入力/出力)プロセスでも重要です。これが「コールバック」という用語が最も明白な意味を持つ場所です。I / Oプロセスが開始されると(ディスクまたはサーバーからファイルを読み取ってHTTPリクエストからデータを返すように要求するなど)、非同期プログラムはそれが終了するのを待ちません。次にスケジュールされているタスクを続行し、ファイルの読み取りまたはhttp要求が完了した(または失敗した)こと、およびカスタム機能がデータを利用できることが通知された後にのみ、カスタム機能で応答できます。これは、電話でお店に電話をかけて「コールバック」番号を残しておくようなもので、誰かがあなたに連絡できるようになったときに電話をかけることができます。それは、誰がどれだけ他の事柄に出席できないかを知っているために電話を切るよりもましです。
非同期使用には、本来、必要なイベント(たとえば、I / Oプロセスの完了)をリッスンするいくつかの手段が含まれるため、それが発生すると(発生した場合のみ)カスタムの「コールバック」機能が実行されます。明らかなAJAXの例では、データがサーバーから実際に到着すると、「コールバック」関数がトリガーされ、そのデータを使用してDOMを変更するため、ブラウザーウィンドウをその範囲まで再描画します。
要点をまとめると。一部の人々は、「コールバック」という単語を使用して、既存の関数に引数として注入できるあらゆる種類のカスタム機能を指します。しかし、少なくとも私にとって、この単語の最も適切な使用法は、注入された「コールバック」関数が非同期で使用される場合です。通知を待機しているイベントの発生時にのみ実行されます。
Array.prototype.forEach()
として渡される関数と引数として渡される関数の違いsetTimeout()
、およびプログラムについての推論の範囲では、色の異なる馬です。 。
プログラマー以外の用語では、コールバックはプログラムの空欄を埋めます。
多くの紙のフォームに共通する項目は、「緊急時に連絡する人」です。そこに空白行があります。あなたは誰かの名前と電話番号を書きます。緊急事態が発生した場合、その人は呼び出されます。
これが鍵です。フォーム(コード、通常は誰か他の人)は変更しません。ただし、不足している情報(自分の番号)を入力できます。
例1:
コールバックはカスタマイズされたメソッドとして使用され、プログラムの動作を追加または変更するために使用される場合があります。たとえば、関数を実行するが出力を印刷する方法がわからないCコードがあるとします。文字列を作るだけです。文字列の処理方法を理解しようとすると、空白行が表示されます。しかし、プログラマーはあなたにコールバックを書き込むための空白を与えました!
この例では、鉛筆を使用して用紙の空白を埋めるのではなく、関数を使用しますset_print_callback(the_callback)
。
set_print_callback
鉛筆ですthe_callback
あなたが記入しているあなたの情報です。これで、プログラムのこの空白行が埋められました。出力を印刷する必要があるときはいつでも、その空白行を見て、そこにある指示に従います(つまり、そこに置いた関数を呼び出します)。実際には、これにより、画面、ログファイル、プリンターに印刷することができます。ネットワーク接続、またはそれらの任意の組み合わせを介して。あなたはあなたがしたいことで空白を埋めました。
例2:
緊急番号に電話する必要があると言われたら、紙のフォームに書かれていることを読んでから、読んだ番号に電話します。その行が空白の場合、何も行われません。
Guiプログラミングもほとんど同じように機能します。ボタンがクリックされると、プログラムは次に何をすべきかを理解する必要があります。コールバックを探しに行きます。このコールバックは、「Button1がクリックされたときに何をするか」というラベルの付いた空白にあります。
ほとんどのIDEは、要求されたときに(たとえば、button1_clicked
)、空白を自動的に入力します(基本メソッドを記述します)。ただし、その空白には、適切な方法があります。メソッドを呼び出すrun_computations
かbutter_the_biscuits
、そのコールバックの名前を適切な空白に入れる限り、呼び出すことができます。緊急番号の空欄に「555-555-1212」と入力できます。それはあまり意味がありませんが、許容されます。
最後のメモ:コールバックで埋める空白行ですか?自由に消したり書き換えたりできます。(あなたがすべきかどうかは別の問題ですが、それは彼らの力の一部です)
例から始めるのが常に良い:)。
2つのモジュールAとBがあるとします。
モジュールBで何らかのイベント/条件が発生したときにモジュールAに通知する必要があります。ただし、モジュールBはモジュールAについて何も知りません。次のような関数ポインターを介して、(モジュールAの)特定の関数へのアドレスしか知りません。モジュールAによって提供されます。
したがって、すべてのBは、関数ポインターを使用して特定のイベント/条件が発生したときに、モジュールAに「コールバック」するだけで済みます。は、コールバック関数内でさらに処理を行うことができます。
*)ここでの明らかな利点は、モジュールBからモジュールAに関するすべてを抽象化していることです。モジュールBは、モジュールAが誰であるか/何であるかを気にする必要はありません。
プログラマーのジョニーはホッチキスを必要とするので、彼は事務用品部門に行き、それを要求します。要求フォームに記入した後、彼はそこに立って店員が倉庫を見てホッチキスを探すのを待つことができます(ブロッキング関数呼び出しのように) )または、その間に何か他のことを行ってください。
これには通常時間がかかるため、Johnyはリクエストフォームにメモを付けて、ホッチキスのピックアップの準備ができたときに電話をかけるように依頼します。その間、彼は机の上で昼寝するなど、他のことを行うことができます。
10の2乗を返す関数が必要なので、関数を作成するとします。
function tenSquared() {return 10*10;}
後で9乗する必要があるため、別の関数を記述します。
function nineSquared() {return 9*9;}
最終的には、これらすべてをジェネリック関数に置き換えます。
function square(x) {return x*x;}
まったく同じ考え方がコールバックにも当てはまります。何かを行う関数があり、完了時にdoAを呼び出します。
function computeA(){
...
doA(result);
}
後で、まったく同じ関数がdoBを呼び出すようにしたい場合は、関数全体を複製できます。
function computeB(){
...
doB(result);
}
または、コールバック関数を変数として渡し、関数を1回だけ持つ必要があります。
function compute(callback){
...
callback(result);
}
次に、compute(doA)とcompute(doB)を呼び出すだけです。
コードを単純化するだけでなく、非同期コードは、電話で誰かに電話をかけてコールバック番号を残した場合と同様に、完了時に任意の関数を呼び出すことでコードが完了したことを知らせます。
説明するポイントは2つあります。1つはコールバックがどのように機能するか(コンテキストを知らなくても呼び出すことができる関数を渡す)、もう1つは何に使用されるか(イベントの非同期処理)です。
他の回答で使用された小包が到着するのを待つことの類推は、両方を説明するのに適しています。コンピュータプログラムでは、小包を期待するようにコンピュータに伝えます。通常、小包は到着し、到着しない場合は無期限に到着するまで待機します(他には何もしません)。人間にとってこれはばかげているように聞こえますが、それ以上の対策がなければ、これはコンピュータにとって完全に自然なことです。
これで、コールバックは玄関のベルになります。宅配サービスを提供すると、家のどこにいる場合でも、ベルがどのように動作するかを知らなくても、荷物の到着を通知できます。(たとえば、一部の「ベル」は実際に電話を発信します。)コンテキスト外でいつでも「呼び出す」ことができる「コールバック関数」を提供したため、フロントポーチに座って停止し、いつでも(小包到着の)イベント。
友人があなたの家を出て、あなたが彼女に「家に着いたら電話して、あなたが無事に到着したことを知っている」と言ったと想像してください。(文字通り)コールバックです。言語に関係なく、それがコールバック関数です。いくつかのタスクが完了したときに、いくつかのプロシージャが制御をあなたに戻したいので、あなたにコールバックするために使用する関数を与えます。
たとえば、Pythonでは
grabDBValue( (lambda x: passValueToGUIWindow(x) ))
grabDBValue
データベースから値を取得するだけで、実際に値をどう処理するかを指定して、関数を受け入れるように記述できます。いつgrabDBValue
戻るかはわかりませんが、いつ戻るかはわかります。ここでは、GUIウィンドウに値を送信する無名関数(またはlambda)を渡します。これを行うことで、プログラムの動作を簡単に変更できます。
grabDBValue( (lambda x: passToLogger(x) ))
コールバックは、通常の整数、文字列、ブール値などと同様に、関数がファーストクラスの値である言語でうまく機能します。Cでは、ポインターを関数に渡すことで関数を「渡す」ことができ、呼び出し元はそれを使用できます。Javaでは、クラスの外側に関数(「メソッド」)がないため、呼び出し元は特定のメソッド名を持つ特定の型の静的クラスを要求します。他のほとんどの動的言語では、単純な構文で関数を渡すことができます。
語彙スコープ(SchemeやPerlなど)を備えた言語では、次のようなトリックを実行できます。
my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration
$val
この場合6
、コールバックはそれが定義された字句環境で宣言された変数にアクセスできるためです。字句スコープと匿名のコールバックは、初心者のプログラマにさらなる研究を保証する強力な組み合わせです。
実行したいコードがいくつかあります。通常、それを呼び出すと、続行する前に終了するのを待ちます(これにより、アプリが灰色になり、カーソルの回転時間が発生する可能性があります)。
別の方法は、このコードを並行して実行し、独自の作業を続行することです。しかし、元のコードが、それが呼び出したコードからの応答に応じて異なる処理を実行する必要がある場合はどうでしょうか。まあ、その場合は、完了時に呼び出すコードの名前/場所を渡すことができます。これは「コールバック」です。
通常のコード:情報の要求->情報の処理->処理の結果の処理->他の処理を続行します。
コールバックの場合:情報の要求->プロセス情報->他の処理を続行します。そして、後の時点で->処理の結果を扱う。
コールバックがなければ、他の特別なプログラミングリソース(スレッドなど)もありません。プログラムは、次々に順次実行される一連の命令であり、特定の条件によって決定される一種の「動的動作」でも、すべての可能なシナリオです事前にプログラムする必要があります。
したがって、プログラムに実際の動的動作を提供する必要がある場合は、コールバックを使用できます。コールバックを使用すると、パラメーター、以前に定義されたパラメーターを提供する別のプログラムを呼び出すプログラム、およびいくつかの結果(これはコントラクトまたは操作のシグネチャです)を期待できるため、これらの結果はサードパーティのプログラムで生成/処理できます以前は知られていない。
この手法は、プログラム、関数、オブジェクト、およびコンピューターによって実行されるコードの他のすべての単位に適用される多態性の基礎です。
コールバックの例として使用される人間の世界は、あなたが何かの仕事をしているときにわかりやすく説明されています。あなたが画家(ここではあなたがペイントするメインプログラムです)で、クライアントに電話して、仕事の結果を承認するよう依頼する場合があります。 、それで、彼は絵が良いかどうかを決定します(あなたのクライアントはサードパーティプログラムです)。
上記の例では、あなたは画家であり、結果を承認する仕事を他の人に「委任」します。画像はパラメーターであり、新しいクライアント(コールバックされた「関数」)はそれぞれ、彼が望むものを決定する仕事の結果を変更します。画像について(クライアントによる決定は、「コールバック関数」から返された結果です)。
この説明がお役に立てば幸いです。
長時間かかる可能性のあるタスクを私に与えたとしましょう:出会った最初の5人のユニークな人の名前を取得します。人口が少ないエリアにいる場合、これには数日かかる場合があります。私が走り回っている間、あなたの手に座っていることにあまり興味がないので、「リストを入手したら、私の携帯に電話して、私に読み返してください。これが番号です。」と言います。
あなたは私にコールバック参照を与えました-私はさらなる処理を引き渡すために実行することになっている関数です。
JavaScriptでは、次のようになります。
var lottoNumbers = [];
var callback = function(theNames) {
for (var i=0; i<theNames.length; i++) {
lottoNumbers.push(theNames[i].length);
}
};
db.executeQuery("SELECT name " +
"FROM tblEveryOneInTheWholeWorld " +
"ORDER BY proximity DESC " +
"LIMIT 5", callback);
while (lottoNumbers.length < 5) {
playGolf();
}
playLotto(lottoNumbers);
これはおそらく多くの方法で改善できます。たとえば、2番目のコールバックを提供することができます。1時間以上かかる場合は、赤い電話を呼び出して、タイムアウトしたことを回答した人に伝えます。
コールバックは、電話システムの観点から最も簡単に説明できます。関数呼び出しは、電話で誰かに電話をかけ、彼女に質問をし、答えを得て、電話を切るのに似ています。コールバックを追加すると、アナロジーが変更され、彼女に質問をした後、名前と番号を伝えて、彼女があなたに電話で返答できるようにします。-Paul Jakubik、「C ++でのコールバック実装」
コールバックは、2番目の関数によって呼び出される関数です。この2番目の関数は、どの関数を呼び出すかを前もって知りません。そのため、コールバック関数のIDはどこかに保存されるか、パラメーターとして2番目の関数に渡されます。この「アイデンティティー」は、プログラミング言語に応じて、コールバックのアドレスや他の種類のポインター、または関数の名前の場合があります。プリンシパルは同じで、関数を明確に識別するいくつかの情報を保存または渡します。
時間が来ると、2番目の関数がコールバックを呼び出し、そのときの状況に応じてパラメーターを提供できます。可能なコールバックのセットからコールバックを選択することもできます。プログラミング言語は、2番目の関数がその「アイデンティティ」を認識してコールバックを呼び出すことができるように、ある種の構文を提供する必要があります。
このメカニズムには、非常に多くの用途があります。コールバックを使用すると、関数の設計者は、提供されるコールバックを呼び出して、関数をカスタマイズできます。たとえば、並べ替え関数はコールバックをパラメーターとして受け取り、このコールバックは2つの要素を比較してどちらが最初に来るかを決定するための関数になる場合があります。
ちなみに、プログラミング言語によっては、上記の説明の「関数」が「ブロック」、「クロージャ」、「ラムダ」などに置き換えられる場合があります。
コールバックを教えるには、まずポインタを教える必要があります。生徒が変数へのポインタの考え方を理解すると、コールバックの考え方が簡単になります。C / C ++を使用している場合、これらの手順に従うことができます。
さらに多くのことがあるかもしれません。生徒を巻き込むと、生徒は発見します。お役に立てれば。
わかりやすい英語では、コールバックは約束です。ジョー、ジェーン、デイビッド、サマンサは、相乗り通勤を共有しています。ジョーは今日運転しています。ジェーン、デイビッド、サマンサにはいくつかのオプションがあります。
オプション1:これは、ジョーが外にいるかどうかをチェックする「ループ」でジェーンがスタックするポーリングの例に似ています。その間、ジェーンは他に何もできません。
オプション2:これはコールバックの例です。ジェーンはジョーに外にいるときにドアベルを鳴らすように言います。彼女は彼にドアベルを鳴らす「機能」を与えます。ジョーはドアベルがどのように機能するか、またはそれがどこにあるかを知る必要はありません。彼はその関数を呼び出すだけです。
コールバックは「イベント」によって駆動されます。この例では、「イベント」はジョーの到着です。たとえばAjaxでは、イベントは非同期要求の「成功」または「失敗」であり、それぞれが同じまたは異なるコールバックを持つことができます。
JavaScriptアプリケーションとコールバックの観点から。また、「クロージャー」とアプリケーションのコンテキストを理解する必要があります。「これ」とは、JavaScript開発者を簡単に混乱させる可能性があります。この例では、各人の「ring_the_door_bell()」メソッド/コールバック内に、朝のルーチンexに基づいて各人が行う必要のある他のいくつかのメソッドがある場合があります。"テレビを消す()"。"this"が "Jane"オブジェクトまたは "David"オブジェクトを参照するようにして、Joeがピックアップする前にそれぞれが必要なことをセットアップできるようにします。ここで、Joeを使用してコールバックを設定するには、「this」が正しいオブジェクトを参照するようにメソッドをパロディ化する必要があります。
お役に立てば幸いです。
コールバック関数とは何ですか?
この最初の質問に対する簡単な答えは、コールバック関数は関数ポインターを通じて呼び出される関数であるということです。関数のポインター(アドレス)を引数として別の関数に渡す場合、そのポインターを使用して関数を呼び出したときに、そのポインターが指すポインターは、コールバックが行われたと見なされます。
コールバック関数は追跡が困難ですが、非常に役立つ場合もあります。特に、ライブラリを設計している場合。コールバック関数は、ユーザーに関数名を要求するようなもので、特定の条件下でその関数を呼び出します。
たとえば、コールバックタイマーを記述します。これにより、期間と呼び出す関数を指定でき、それに応じて関数がコールバックされます。「myfunction()を10秒ごとに5回実行します」
または、関数名のリストを渡して関数ディレクトリを作成し、それに応じてコールバックするようライブラリに要求することもできます。「成功した場合はコールバックsuccess()、失敗した場合はコールバックfail()。」
簡単な関数ポインタの例を見てみましょう
void cbfunc()
{
printf("called");
}
int main ()
{
/* function pointer */
void (*callback)(void);
/* point to your callback function */
callback=(void *)cbfunc;
/* perform callback */
callback();
return 0;
}
コールバック関数に引数を渡す方法は?
コールバックを実装するための関数ポインターがvoid *を受け取ることが観察されました。これは、構造を含む任意のタイプの変数を取り込むことができることを示しています。したがって、構造によって複数の引数を渡すことができます。
typedef struct myst
{
int a;
char b[10];
}myst;
void cbfunc(myst *mt)
{
fprintf(stdout,"called %d %s.",mt->a,mt->b);
}
int main()
{
/* func pointer */
void (*callback)(void *); //param
myst m;
m.a=10;
strcpy(m.b,"123");
callback = (void*)cbfunc; /* point to callback function */
callback(&m); /* perform callback and pass in the param */
return 0;
}
コールバックは、条件が満たされたときに実行されるようにスケジュールされているメソッドです。
「現実の世界」の例は、地元のビデオゲーム店です。あなたはHalf-Life 3を待っています。毎日店に行ってそれが入っているかどうかを確認する代わりに、リストにメールを登録して、ゲームが利用可能になったときに通知を受けるようにします。メールが「コールバック」となり、満たされる条件はゲームの可用性です。
「プログラマー」の例は、ボタンがクリックされたときにアクションを実行するWebページです。ボタンのコールバックメソッドを登録し、他のタスクを続行します。ユーザーがボタンをクリックすると、ブラウザはそのイベントのコールバックのリストを確認し、メソッドを呼び出します。
コールバックは、イベントを非同期的に処理する方法です。コールバックがいつ実行されるか、まったく実行されるかどうかは決してわかりません。利点は、応答を待つ間、プログラムとCPUサイクルを解放して他のタスクを実行できることです。
単純明快:コールバックは、別の関数に渡して呼び出すことができる関数です。
通常、これは何らかの操作が完了したときに呼び出されます。他の関数に渡す前にコールバックを作成するので、呼び出しサイトからのコンテキスト情報でそれを初期化できます。これが、call * back *という名前が付けられている理由です。最初の関数は、呼び出された場所からコンテキストにコールバックします。
「コンピュータープログラミングでは、コールバックは実行可能コードまたは実行可能コードの一部への参照であり、他のコードに引数として渡されます。これにより、下位レベルのソフトウェアレイヤーは、上位レベルのレイヤーで定義されたサブルーチン(または関数)を呼び出すことができます。」-ウィキペディア
関数ポインターを使用したCでのコールバック
Cでは、コールバックは関数ポインターを使用して実装されます。関数ポインタ-名前が示すように、関数へのポインタです。
たとえば、int(* ptrFunc)();
ここで、ptrFuncは、引数を取らず整数を返す関数へのポインターです。括弧を入れることを忘れないでください。そうしないと、コンパイラーはptrFuncが通常の関数名であると想定します。これは、何も取らず、整数へのポインターを返します。
以下は、関数ポインタを示すコードです。
#include<stdio.h>
int func(int, int);
int main(void)
{
int result1,result2;
/* declaring a pointer to a function which takes
two int arguments and returns an integer as result */
int (*ptrFunc)(int,int);
/* assigning ptrFunc to func's address */
ptrFunc=func;
/* calling func() through explicit dereference */
result1 = (*ptrFunc)(10,20);
/* calling func() through implicit dereference */
result2 = ptrFunc(10,20);
printf("result1 = %d result2 = %d\n",result1,result2);
return 0;
}
int func(int x, int y)
{
return x+y;
}
ここで、関数ポインターを使用してCでのコールバックの概念を理解してみましょう。
完全なプログラムには、callback.c、reg_callback.h、およびreg_callback.cの3つのファイルがあります。
/* callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* callback function definition goes here */
void my_callback(void)
{
printf("inside my_callback\n");
}
int main(void)
{
/* initialize function pointer to
my_callback */
callback ptr_my_callback=my_callback;
printf("This is a program demonstrating function callback\n");
/* register our callback function */
register_callback(ptr_my_callback);
printf("back inside main program\n");
return 0;
}
/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);
/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"
/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
printf("inside register_callback\n");
/* calling our callback function my_callback */
(*ptr_reg_callback)();
}
このプログラムを実行すると、出力は次のようになります
これは、my_callback内のregister_callback内の関数コールバックをメインプログラム内に示すプログラムです。
上位層関数は下位層関数を通常の呼び出しとして呼び出し、コールバックメカニズムにより、下位層関数はコールバック関数へのポインターを介して上位層関数を呼び出すことができます。
インターフェースを使用したJavaでのコールバック
Javaには関数ポインターの概念がありません。これは、インターフェイスメカニズムを介してコールバックメカニズムを実装します。ここでは、関数ポインターの代わりに、呼び出し先がタスクを完了したときに呼び出されるメソッドを持つインターフェイスを宣言します。
例を挙げて説明します。
コールバックインターフェイス
public interface Callback
{
public void notify(Result result);
}
呼び出し元または上位クラス
public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee
//Other functionality
//Call the Asynctask
ce.doAsynctask();
public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}
呼び出し先または下位層関数
public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}
doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}
EventListenerパターンを使用したコールバック
このパターンは、特定のタスクが終了したことを0〜n個のオブザーバー/リスナーに通知するために使用されます
CallbackメカニズムとEventListener / Observerメカニズムの違いは、コールバックでは呼び出し先が単一の呼び出し元に通知するのに対し、Eventlisener / Observerでは呼び出し先はそのイベントに関心のある誰にでも通知できることです(通知は、タスクをトリガーしていないアプリケーション)
例を挙げて説明します。
イベントインターフェース
public interface Events {
public void clickEvent();
public void longClickEvent();
}
クラスウィジェット
package com.som_itsolutions.training.java.exampleeventlistener;
import java.util.ArrayList;
import java.util.Iterator;
public class Widget implements Events{
ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>();
ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();
@Override
public void clickEvent() {
// TODO Auto-generated method stub
Iterator<OnClickEventListener> it = mClickEventListener.iterator();
while(it.hasNext()){
OnClickEventListener li = it.next();
li.onClick(this);
}
}
@Override
public void longClickEvent() {
// TODO Auto-generated method stub
Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
while(it.hasNext()){
OnLongClickEventListener li = it.next();
li.onLongClick(this);
}
}
public interface OnClickEventListener
{
public void onClick (Widget source);
}
public interface OnLongClickEventListener
{
public void onLongClick (Widget source);
}
public void setOnClickEventListner(OnClickEventListener li){
mClickEventListener.add(li);
}
public void setOnLongClickEventListner(OnLongClickEventListener li){
mLongClickEventListener.add(li);
}
}
クラスボタン
public class Button extends Widget{
private String mButtonText;
public Button (){
}
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}
クラスチェックボックス
public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}
活動クラス
パッケージcom.som_itsolutions.training.java.exampleeventlistener;
public class Activity implements Widget.OnClickEventListener
{
public Button mButton;
public CheckBox mCheckBox;
private static Activity mActivityHandler;
public static Activity getActivityHandle(){
return mActivityHandler;
}
public Activity ()
{
mActivityHandler = this;
mButton = new Button();
mButton.setOnClickEventListner(this);
mCheckBox = new CheckBox();
mCheckBox.setOnClickEventListner(this);
}
public void onClick (Widget source)
{
if(source == mButton){
mButton.setButtonText("Thank you for clicking me...");
System.out.println(((Button) mButton).getButtonText());
}
if(source == mCheckBox){
if(mCheckBox.isChecked()==false){
mCheckBox.setCheck(true);
System.out.println("The checkbox is checked...");
}
else{
mCheckBox.setCheck(false);
System.out.println("The checkbox is not checked...");
}
}
}
public void doSomeWork(Widget source){
source.clickEvent();
}
}
他のクラス
public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}
メインクラス
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}
上記のコードからわかるように、アプリケーションで発生する可能性のあるすべてのイベントを基本的にリストする、eventsというインターフェイスがあります。Widgetクラスは、Button、CheckboxなどのすべてのUIコンポーネントの基本クラスです。これらのUIコンポーネントは、フレームワークコードから実際にイベントを受け取るオブジェクトです。Widgetクラスは、Eventsインターフェイスを実装し、OnClickEventListenerとOnLongClickEventListenerの2つのネストされたインターフェイスも持っています
これら2つのインターフェースは、ButtonやCheckboxなどのウィジェット派生UIコンポーネントで発生する可能性があるイベントをリッスンします。したがって、この例をJavaインターフェースを使用した以前のコールバックの例と比較すると、これら2つのインターフェースはコールバックインターフェースとして機能します。したがって、上位レベルのコード(ここでのアクティビティ)は、これら2つのインターフェースを実装しています。また、ウィジェットに対してイベントが発生すると、上位レベルのコード(または上位レベルのコードに実装されたこれらのインターフェースのメソッド、ここではActivity)が呼び出されます。
次に、コールバックパターンとイベントリスナーパターンの基本的な違いについて説明します。コールバックを使用すると説明したように、呼び出し先は単一の呼び出し元にのみ通知できます。ただし、EventListenerパターンの場合、アプリケーションの他の部分またはクラスは、ボタンまたはチェックボックスで発生する可能性のあるイベントに登録できます。この種のクラスの例は、OtherClassです。OtherClassのコードが表示されている場合、アクティビティで定義されたボタンで発生する可能性のあるClickEventのリスナーとして登録されていることがわかります。興味深い点は、アクティビティ(呼び出し元)の他に、ボタンでクリックイベントが発生するたびに、このOtherClassにも通知が送られることです。
コールバックを使用すると、独自のコードを別のコードブロックに挿入して、後で実行することができます。これにより、ニーズに合わせて、他のコードブロックの動作が変更または追加されます。保守性の高いコードを使用できる一方で、柔軟性とカスタマイズ性を得ることができます。
ハードコードが少ない=保守と変更が容易=時間の短縮=ビジネス価値の向上=素晴らしい。
たとえば、JavaScriptでUnderscore.jsを使用すると、次のように配列内のすべての偶数要素を見つけることができます。
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
Underscore.jsの好意による例:http : //documentcloud.github.com/underscore/#filter
[編集] functionAとfunctionBの 2つの関数がある場合、functionAがfunctionBに依存している場合。
次に、コールバック関数としてfunctionBを呼び出します。これは、Springフレームワークで広く使用されています。
方法を同僚にタスクを与えると考えてください。簡単なタスクは次のとおりです。
Solve these equations:
x + 2 = y
2 * x = 3 * y
同僚は熱心に計算を行い、次の結果を返します。
x = -6
y = -4
しかし、あなたの同僚は問題を抱えています、彼はのような表記法を常に理解しているわけではありません^
が、彼らは彼らの説明によってそれらを理解しています。などexponent
。彼がこれらの1つを見つけるたびに、次の情報が返されます。
I don't understand "^"
これは、あなたの同僚にそのキャラクターが何を意味するのかを説明した後で、命令セット全体を再度書き直す必要があり、彼は常に質問の合間に覚えているわけではありません。そして、私に尋ねるなど、あなたのヒントも思い出せません。しかし、彼は常にあなたの書いた指示に従います。
あなたは解決策を考え、あなたはすべての指示に以下を追加するだけです:
If you have any questions about symbols, call me at extension 1234 and I will tell you its name.
今、彼は問題があるときはいつでも、あなたに悪い反応を与えてプロセスを再開させるのではなく、あなたに電話して尋ねます。
これは、ウェブページのダウンロードに関しては次のとおりです。
プログラムが携帯電話で実行され、ウェブページhttp://www.google.comをリクエストしています。プログラムを同期的に作成する場合、データをダウンロードするために作成する関数は、すべてのデータがダウンロードされるまで継続的に実行されます。つまり、UIは更新されず、基本的にフリーズしたように見えます。コールバックを使用してプログラムを作成する場合、データを要求し、「完了したらこの関数を実行する」と言います。これにより、UIはファイルのダウンロード中にユーザーの操作を許可できます。Webページのダウンロードが完了すると、結果関数(コールバック)が呼び出され、データを処理できるようになります。
基本的に、それは何かをリクエストし、結果を待つ間も実行を続けることを可能にします。コールバック関数を介して結果が返されたら、中断したところから操作を再開できます。