普通の英語でコールバックを説明する方法?呼び出し元の関数からコンテキストを取得して、ある関数を別の関数から呼び出すのとはどう違うのですか。初心者プログラマに彼らの力をどのように説明できるでしょうか?
多くの場合、アプリケーションはそのコンテキスト/状態に基づいてさまざまな機能を実行する必要があります。このために、呼び出される関数に関する情報を格納する場所に変数を使用します。必要に応じて、アプリケーションは呼び出される関数に関する情報でこの変数を設定し、同じ変数を使用して関数を呼び出します。
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]
私はこの死者をシンプルにしようとします。 「コールバック」とは、最初の関数をパラメーターとして受け取る別の関数によって呼び出される関数です。多くの場合、「コールバック」はsomethingが発生したときに呼び出される関数です。そのsomethingは、プログラマーの言葉で「イベント」と呼ぶことができます。
このシナリオを想像してみてください:あなたは数日でパッケージを期待しています。パッケージは隣人への贈り物です。したがって、パッケージを取得したら、それを隣人に持ち帰りたいと思います。あなたは町を出ているので、配偶者に指示を残します。
あなたは彼らに荷物を手に入れて隣人に持ってくるように言うことができます。あなたの配偶者がコンピューターのように愚かだった場合、彼らはドアに座って荷物が来るまで待っていて(何もしないで)、それが来ると隣人に持っていきます。しかし、より良い方法があります。配偶者にパッケージを受け取ったら、隣人に渡す必要があることを伝えます。その後、彼らはパッケージを受け取るまで、普通に生活を送ることができます。
この例では、パッケージの受信は「イベント」であり、近隣への持ち込みは「コールバック」です。あなたの配偶者は、あなたの指示を「実行」し、パッケージが到着したときのみwhenを引き渡します。ずっといい!
このような考え方は日常生活では明らかですが、コンピューターには同じような常識はありません。プログラマーが通常ファイルに書き込む方法を検討してください。
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 をチェックして、この種の考え方を実際に実践してください。
普通の英語でコールバックを説明する方法?
わかりやすい英語では、コールバック関数はTaskを完了したときにManagerにコールバックするWorkerのようなものです。
呼び出し元の関数からコンテキストを取得して、ある関数を別の関数から呼び出すのとはどう違うのですか。
あなたが他の関数から関数を呼んでいるのは事実ですが、重要なのはコールバックがオブジェクトのように扱われるので、あなたは(ストラテジーデザインパターンのように)システムの状態に基づいて呼び出す関数を変えることができます。
初心者プログラマに彼らの力をどのように説明できるでしょうか?
コールバックの力は、サーバーからデータを取得する必要がある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
にアクセスする必要があります。これは、Taskを完了する前にWorkerがManagerから情報を取得する必要があるようです。 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);
最後に、 Douglas Crockford からのclosure
の定義です。
関数は他の関数の中で定義できます。内部関数は、外部関数のvarsとパラメータにアクセスできます。内部関数への参照が(たとえばコールバック関数として)存続する場合、外部関数のvarも存続します。
また見なさい:
私は、「コールバック」という言葉が2つの矛盾した方法で使用されるようになったため、多くの知的な人々が現実を強調するのに失敗しているのを見て驚きます。
どちらの方法でも、既存の関数に追加の機能(関数定義、匿名または名前付き)を渡すことによって関数をカスタマイズする必要があります。すなわち。
customizableFunc(customFunctionality)
カスタム機能が単純にコードブロックに接続されている場合は、そのように関数をカスタマイズしました。
customizableFucn(customFunctionality) {
var data = doSomthing();
customFunctionality(data);
...
}
この種の注入された機能はしばしば「コールバック」と呼ばれますが、それに関して偶然のものは何もありません。非常に明白な例は、配列を変更するために配列の各要素に適用される引数としてカスタム関数が提供されるforEachメソッドです。
しかしこれは、AJAXやnode.jsのように非同期プログラミングのための "コールバック"関数の使用とは根本的に異なります。マウスのクリックなどのユーザーインタラクションイベントに機能を割り当てることができます。この場合、全体的な考え方は、カスタム機能を実行する前に偶発的なイベントが発生するのを待つことです。これはユーザーとのやり取りの場合には明らかですが、ディスクからファイルを読み取る場合のように、時間がかかる入出力(入出力)プロセスでも重要です。これが、用語「コールバック」が最も明白な意味をなすところです。入出力プロセスが開始されると(http要求からデータを返すためにディスクまたはサーバーからファイルを読み取るように要求するなど)、非同期プログラムはそれを待ちません。終わる。次にスケジュールされているタスクはすべて続行でき、ファイルの読み取り要求またはhttp要求が完了したこと(または失敗したこと)とデータがカスタム機能で使用可能であることが通知された後にのみカスタム機能で応答します。それは電話で会社に電話をかけてあなたの「コールバック」番号を残すことのようなものです。それは、誰がどのくらいの期間知っていて他の業務に参加できないかを知っている人に電話をかけるよりも優れています。
非同期使用は、それが発生したときに(そしてそれが発生したときにだけ)カスタムの「コールバック」機能が実行されるように、所望のイベント(例えば、I / Oプロセスの完了)を聴取する何らかの手段を本質的に含む。明らかなAJAXの例では、データが実際にサーバーから届くと、 "callback"関数がそのデータを使ってDOMを修正し、それによってブラウザウィンドウをその範囲まで再描画します。
要点をまとめると。引数として既存の関数に挿入できる任意の種類のカスタム機能を指すためにWordの「コールバック」を使用する人もいます。しかし、少なくとも私にとっては、Wordの最も適切な使用法は、注入された「コールバック」関数が非同期に使用される場合です - 通知を待っているイベントの発生時にのみ実行されます。
プログラマー以外の言葉では、コールバックはプログラムの空白を埋めることです。
多くの紙のフォームに共通の項目は「緊急の場合に電話をかける人」です。そこに空白行があります。あなたは誰かの名前と電話番号を書きます。緊急事態が発生した場合、その人は呼ばれます。
これが鍵です。あなたはフォーム(コード、通常は他の誰かのもの)を変更しません。ただし、足りない情報を記入することはできます(あなたの番号)。
例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に通知してもらいたいのです。モジュールAによって提供される関数ポインターを介した(モジュールAの)特定の関数へのアドレスです。
そのため、Bがやらなければならないのは、関数ポインタを使用して特定のイベント/条件が発生したときに、モジュールAに「コールバック」することだけです。 Aはコールバック関数内でさらに処理を行うことができます。
*)ここでの明らかな利点は、モジュールBからモジュールAに関するすべてを抽象化していることです。
あなたが関数を書くようにあなたが10の二乗を返す関数が必要だと想像してみてください:
function tenSquared() {return 10*10;}
後で9の2乗が必要になるので、別の関数を書きます。
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つはその目的(非同期にイベントを処理する)です。
他の回答で使用されている小包が到着するのを待つことの例えは、両方を説明するのに良いものです。コンピュータプログラムでは、小包を受け取るようにコンピュータに指示します。通常は、小包が到着するまでそこに座って待機します(そして、到着しない場合は無期限に待機します)。人間にとって、これは愚かに聞こえますが、それ以上の対策がなければ、これはコンピュータにとって全く自然なことです。
今すぐコールバックはあなたの正面玄関のベルになります。あなたは、彼らがあなたが家のどこにいるか(たとえ)であっても、あるいはベルがどのように機能するかを知らなくても、小包の到着を知らせる方法を小包サービスに提供します。 (たとえば、「ベル」によっては実際に電話をかけることができます。)「コールバック関数」を提供しているので、コンテキストに関係なくいつでも「呼び出す」ことができます。時間があるときはいつでも(小包到着の)イベント.
コールバックは電話システムに関して最も簡単に説明されます。関数呼び出しは、電話で誰かに電話をかけ、質問をし、答えを得て、電話を切るのと似ています。コールバックを追加するとアナロジーが変わり、質問をした後で名前と番号を教えて返事ができるようになります。 - Paul Jakubik、 " C++ "
友人があなたの家を出ようとしていると想像してください、そしてあなたは彼女に「あなたが安全に到着したことを私が知っているように家に帰ったら電話してください」と言います。つまり、(文字通り)コールバックです。言語に関係なく、それがコールバック関数です。あるタスクがタスクを完了したときに制御をあなたに返すようにしたいので、あなたにそれをコールバックするために使用する関数を与えます。
たとえば、Pythonでは、
grabDBValue( (lambda x: passValueToGUIWindow(x) ))
grabDBValue
は、データベースから値を取得するだけで、実際にその値をどう処理するかを指定できるように書くことができるため、関数を受け入れます。 grabDBValue
がいつ返されるのか、またはいつ返されるのかはわかりませんが、返す場合はいつ行うのかがわかります。ここでは、値をGUIウィンドウに送信する無名関数(またはラムダ)を渡します。こうすることで、プログラムの動作を簡単に変更できます。
grabDBValue( (lambda x: passToLogger(x) ))
コールバックは、通常の整数、文字列、ブール値などのように、関数がファーストクラスの値である言語でうまく機能します。 msgstr "" "ポインタへのポインタの受け渡しによって関数は動き回り、呼び出し元はそれを使用することができます; 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時間以上かかる場合は、赤い電話に電話して、タイムアウトしたことを回答した人に伝えます。
コールバックがなければ、他の特別なプログラミングリソース(スレッドなどのもの)もプログラムは正確に連続して順番に実行される一連の命令です。そしてある種の「動的な振る舞い」が特定の条件によって決定される場合でも、すべての可能なシナリオは事前にプログラムされていなければなりません。
したがって、プログラムに実際の動的な振る舞いを提供する必要がある場合は、コールバックを使用できます。コールバックを使用すると、以前に定義されたパラメータを提供する別のプログラムを呼び出すようにパラメータで指示することができ、いくつかの結果を期待できます(これは契約または操作シグネチャです)。以前に知られていなかった第三者のプログラムによって生産/処理することができます。
このテクニックは、プログラム、関数、オブジェクト、その他すべてのコードの単位に適用される多態性の基盤であり、コンピュータによって実行されます。
コールバックの例として使用されている人間の世界は、仕事をしているときに説明されています。あなたがPainterであると仮定します(ここがメインプログラムで、を描画します)。クライアントは時々あなたの仕事の結果を承認するように彼に頼むようにします、それで、彼は絵が良いかどうかを決定します(あなたのクライアントはサードパーティプログラムです)。
上の例では、あなたはPainterであり、結果を承認する仕事を他の人に "委任"し、絵はパラメータであり、そしてそれぞれの新しいクライアント(コールバックされた "function")は彼が望むことを決める画像(についてのクライアントによる決定は、「コールバック関数」から返された結果です。
この説明が役に立つことを願っています。
実行したいコードがいくつかあります。通常、それを呼び出すと、次に進む前にそれが終了するのを待っています(これにより、アプリが灰色になり/カーソルの回転時間が発生する可能性があります)。
別の方法は、このコードを並行して実行し、あなた自身の作業を続けることです。しかし、元のコードが呼び出したコードからの応答に応じて異なる処理を実行する必要がある場合はどうなりますか?まあ、その場合、あなたはそれが終わったときにあなたがそれに呼んで欲しいコードの名前/場所を渡すことができます。これは「折り返し電話」です。
通常のコード:[情報] - > [プロセス情報] - > [処理結果の処理] - > [他の処理を続行する]を選択します。
コールバックを使って:情報を求める - >プロセス情報 - >他のことをし続ける。そしてしばらくしてから、処理結果を処理します。
コールバックは、2番目の関数によって呼び出される関数です。この2番目の関数は、どの関数を呼び出すのかを事前にはわかりません。そのため、コールバック関数の恒等式はどこかに格納されるか、または2番目の関数にパラメータとして渡されます。プログラミング言語に応じて、この「識別」は、コールバックのアドレス、または他の種類のポインタ、または関数の名前になります。原理は同じです、私たちは関数を明確に識別する情報をいくつか保存するか渡します。
時が来れば、2番目の関数はその瞬間の状況に応じてパラメータを渡してコールバックを呼び出すことができます。それは可能なコールバックのセットからコールバックを選択するかもしれません。プログラミング言語は、その「同一性」を知っていて、2番目の関数がコールバックを呼び出せるようにするためにある種の構文を提供しなければなりません。
このメカニズムには、非常に多くの用途があります。コールバックを使用すると、関数の設計者は、提供されているコールバックが何であれ、それをコールさせることによってそれをカスタマイズさせることができます。たとえば、ソート関数はパラメータとしてコールバックを取り、このコールバックは2つの要素を比較してどちらが先になるかを決定するための関数です。
ちなみに、プログラミング言語によっては、上記の説明の「関数」という単語は、「ブロック」、「クロージャ」、「ラムダ」などに置き換えられることがあります。
通常、変数を関数に送りました。引数として与えられる前に変数が処理される必要があるタスクがあるとしましょう - あなたはコールバックを使うことができます。
function1(var1, var2)
は通常の方法です。
var2
を処理してから引数として送信したい場合はどうすればいいですか? function1(var1, function2(var2))
これはコールバックの一種です - ここでfunction2
は何らかのコードを実行して変数を初期関数に返します。
コールバックを教えるためには、まずポインタを教える必要があります。生徒が変数へのポインタの概念を理解すると、コールバックの概念が簡単になります。 C/C++を使用していると仮定すると、これらのステップに従うことができます。
もっとたくさんのことがあるかもしれません。生徒を巻き込むと、彼らは発見するでしょう。お役に立てれば。
比喩的な説明:
私は私が友人に配達したい小包を持っています、そしてまた私の友人がいつそれを受け取るかについて知りたいです。
そこで私は小包を郵便局に持って行き、配達を依頼します。友達が小包を受け取ったときに知りたい場合は、2つの選択肢があります。
(a)郵便局で配達されるまで待つことができます。
(b)配送されたらEメールを受け取ります。
オプション(b)はコールバックに似ています。
平易な英語ではコールバックは約束です。ジョー、ジェーン、デビッド、サマンサは仕事のためにカープールを共有しています。ジョーは今日運転しています。 Jane、David、Samanthaにはいくつかの選択肢があります。
オプション1:これは、Janeが外部にいるかどうかを確認する「ループ」にジェーンが立ち往生するポーリングの例に似ています。ジェーンはその間に他に何もすることができない。
オプション2:これはコールバックの例です。ジェーンは彼が外にいるときジョーに彼女のドアベルを鳴らすように言います。彼女は彼にドアベルを鳴らすための「機能」を与える。ジョーはドアベルがどのように機能するのか、あるいはそれがどこにあるのかを知る必要はありません。
コールバックは「イベント」によって駆動されます。この例では、「イベント」はJoeの到着です。たとえばAjaxでは、イベントは非同期要求の「成功」または「失敗」であり、それぞれが同じまたは異なるコールバックを持つことができます。
JavaScriptアプリケーションとコールバックに関して。また、「クロージャ」とアプリケーションのコンテキストを理解する必要があります。 「これ」が指すものは、JavaScript開発者を容易に混乱させる可能性があります。この例では、各人の "ring_the_door_bell()"メソッド/コールバック内に、各人が朝のルーチンexに基づいて実行する必要がある他のメソッドがいくつかあるかもしれません。 "テレビを消す()"。 "this"が "Jane"オブジェクトまたは "David"オブジェクトを指すようにして、Joeがそれらを拾う前に、それぞれが他の必要なことを設定できるようにします。これは、 "this"が正しいオブジェクトを指すように、Joeでコールバックを設定するためにメソッドをパロディ化する必要がある場合です。
それが役立つことを願っています!
説明するのはかなり簡単な作業だと思います。
最初のコールバックは普通の関数です。
さらに、この関数を別の関数(Bと呼びましょう)の内部から呼びます(Aと呼びましょう)。
これについての魔法は、私が決めることです、どの関数が外部からの関数によって呼ばれるべきですB.
関数Bを書いている時点では、どのコールバック関数を呼び出すべきかわかりません。私が関数Bを呼び出すときに、関数Aを呼び出すようにこの関数に指示します。それがすべてです。
コールバック関数とは何ですか?
この最初の質問に対する簡単な答えは、コールバック関数は関数ポインタを通して呼び出される関数であるということです。関数のポインタ(アドレス)を別の引数として渡すと、そのポインタがその関数を呼び出すのに使用されるときにそれが指すと、コールバックが行われたと言われます。
コールバック関数は追跡が困難ですが、時には非常に便利です。特にあなたが図書館を設計しているとき。コールバック関数は、あなたのユーザにあなたに関数名を与えるように頼むのに似ています、そしてあなたは特定の条件下でその関数を呼ぶでしょう。
たとえば、コールバックタイマーを書きます。それはあなたが持続時間とどの関数を呼び出すべきかを指定することを可能にし、そしてその関数はそれに応じてコールバックになります。 “ myfunction()を10秒毎に5回実行します”
または、関数名のリストを渡して関数ディレクトリを作成し、それに応じてライブラリにコールバックするように依頼することもできます。 「成功した場合はコールバックsuccess()、失敗した場合はcallback 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;
}
コールバックは、自己宛名付きの封筒です。あなたが関数を呼び出すとき、それは手紙を送るようなものです。その関数から別の関数を呼び出したい場合は、その情報を参照またはアドレスの形式で提供します。
コンピュータプログラミングでは、コールバックは実行可能コード、または他のコードへの引数として渡される実行可能コードへの参照です。これにより、下位レベルのソフトウェア層が、上位レベルの層で定義されているサブルーチン(または関数)を呼び出すことができます。 - Wikipedia
関数ポインタを使った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などのWidget派生UIコンポーネントで発生する可能性があるイベントを監視する役割を果たします。したがって、この例とJavaインタフェースを使用した以前のコールバックの例を比較すると、これら2つのインタフェースはコールバックインタフェースとして機能します。そのため、上位レベルのコード(Here Activity)はこれら2つのインタフェースを実装しています。そして、ウィジェットにイベントが発生するたびに、上位レベルのコード(または上位レベルのコードに実装されているこれらのインターフェースのメソッド、ここではActivity)が呼び出されます。
それでは、コールバックとEventlistenerのパターンの基本的な違いについて説明しましょう。 Callbackを使うと、Calleeは1人のCallerにしか通知できません。しかし、EventListenerパターンの場合、アプリケーションの他の部分またはクラスは、ButtonまたはCheckboxで発生する可能性のあるイベントを登録できます。この種のクラスの例はOtherClassです。 OtherClassのコードを見ると、Activityで定義されたButtonで発生する可能性があるClickEventのリスナーとして登録されていることがわかります。興味深いのは、アクティビティ(呼び出し側)のほかに、このOtherClassにもButtonでクリックイベントが発生するたびに通知されることです。
わかりやすく単純:コールバックとは別の関数に渡す関数なので、呼び出しできます。
通常、何らかの操作が完了したときに呼び出されます。他の関数に渡す前にコールバックを作成するので、コールサイトからのコンテキスト情報でそれを初期化することができます。それがcall * back *と呼ばれる理由です - 最初の関数はそれが呼ばれた場所からコンテキストにコールバックします。
コールバックは、条件が満たされたときに実行されるようにスケジュールされたメソッドです。
「現実世界」の例は、地元のビデオゲーム店です。あなたはHalf-Life 3を待っています。それが入っているかどうか見るために毎日店に行くのではなく、あなたはゲームが利用可能になったときに通知されるリストにあなたの電子メールを登録します。電子メールはあなたの「コールバック」になり、満たされるべき条件はゲームの可用性です。
「プログラマ」の例は、ボタンがクリックされたときにアクションを実行したいWebページです。ボタンのコールバックメソッドを登録し、他のタスクを続けます。ユーザーがボタンをクリックすると、ブラウザはそのイベントのコールバックのリストを見て、メソッドを呼び出します。
コールバックはイベントを非同期的に処理する方法です。コールバックがいつ実行されるのか、またはまったく実行されるのかを知ることはできません。利点は、応答を待っている間に他のタスクを実行するためにプログラムとCPUサイクルを解放することです。
functionAがfunctionBに依存している場合、functionAとfunctionBの2つの関数があるとき[編集]。
それから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.
今、彼が問題を抱えているときはいつでも、彼はあなたに電話してあなたに尋ねます、あなたに悪い応答を与えて、そしてプロセスを再開させるよりはむしろ。
コールバックを使用すると、別のコードブロックに独自のコードを挿入して別の時点で実行することができます。これにより、他のコードブロックの動作がニーズに合わせて変更または追加されます。より保守しやすいコードを持つことができる一方で、あなたは柔軟性とカスタマイズ性を得ます。
ハードコードが少ない=保守や変更が簡単=時間が少ない=ビジネス価値が高い=素晴らしい。
たとえば、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
コールバック関数:
callback
という名前のcallback function
をそれにパラメータotherFunction
を与えて定義し、それを関数本体の中で呼び出します。
function callback(otherFunction){
otherFunction();
}
callback
関数を呼び出すときには、function
型の引数が必要であるため、無名関数で呼び出します。ただし、引数がfunction
型ではない場合はエラーになります。
callback(function(){console.log('SUCCESS!')});
callback(1); // error
ピザの例を焼きます。食材をトッピングしたピザのベースを焼くオーブン今ここで、oven
はcallback function
です。 pizza base with ingredients
はotherFunction
です。
注意すべき点ピザの種類によってピザの種類が異なりますが、オーブンはそれは同じままです焼きます。これはややcallback function
の仕事で、さまざまなカスタム結果を生み出すためにさまざまな機能を備えた関数を期待し続けます。
これはウェブページをダウンロードするという点では:
プログラムは携帯電話で実行されており、ウェブページ http://www.google.com をリクエストしています。あなたがプログラムを同期的に書くならば、あなたがデータをダウンロードするために書く機能はすべてのデータがダウンロードされるまで継続的に実行されるでしょう。つまり、UIは更新されず、基本的にフリーズしたように見えます。あなたがコールバックを使ってあなたのプログラムを書くならば、あなたはデータを要求して、「あなたが終わったらこの関数を実行する」と言います。これにより、ファイルのダウンロード中にUIがユーザーの操作を許可できるようになります。 Webページのダウンロードが完了すると、結果関数(コールバック)が呼び出されてデータを処理できます。
基本的に、それはあなたが何かを要求し、結果を待っている間実行を続けることを可能にします。結果がコールバック関数を介してあなたに戻ってきたら、中断したところから操作を再開できます。