コールバック関数とは何ですか?
コールバック関数とは何ですか?
回答:
のろわれたものの名前が原因で、開発者はコールバックが何であるか混乱することがよくあります。
コールバック関数は、次のような関数です。
コールバック関数がどのように機能するかを想像する良い方法は、それが渡される関数の「バックで呼び出される」関数であるということです。
たぶん、より良い名前は「後の呼び出し」関数でしょう。
この構成は、前のイベントが完了するたびにアクティビティが発生する非同期動作に非常に役立ちます。
疑似コード:
// A function which accepts another function as an argument
// (and will automatically invoke that function when it completes - note that there is no explicit call to callbackFunction)
funct printANumber(int number, funct callbackFunction) {
printout("The number you provided is: " + number);
}
// a function which we will use in a driver function as a callback function
funct printFinishMessage() {
printout("I have finished printing numbers.");
}
// Driver method
funct event() {
printANumber(6, printFinishMessage);
}
event()を呼び出した場合の結果:
The number you provided is: 6
I have finished printing numbers.
ここでの出力の順序は重要です。後でコールバック関数が呼び出されるため、「数字の出力が完了しました」が最初ではなく最後に出力されます。
コールバックは、ポインター言語での使用により、いわゆるコールバックです。これらのいずれかを使用しない場合は、「コールバック」という名前に労力を費やさないでください。親メソッドが呼び出されたとき(ボタンクリック、タイマーティックなどの条件)、そのメソッド本体が完了したときなど、別のメソッドへの引数として提供されるメソッドを説明するための単なる名前であることを理解してください。次に、コールバック関数が呼び出されます。
一部の言語は、複数のコールバック関数引数がサポートされている構造をサポートし、親関数の完了方法に基づいて呼び出されます(つまり、1つのコールバックは、親関数が正常に完了した場合に呼び出され、別のコールバックは、親関数がスローした場合に呼び出されます。特定のエラーなど)。
once its parent method completes, the function which this argument represents is then called
。関数が引数として別の関数に渡されたが、親関数のランタイムの途中から呼び出されたparent(cb) {dostuff1(); cb(); dostuff2()}
場合、callback
関数とは見なされませんか?
コールバック関数は、別のコードに提供する関数で、そのコードから呼び出すことができます。
なぜこれをしたいのですか?呼び出す必要のあるサービスがあるとしましょう。サービスがすぐに戻る場合は、次のようにします。
たとえば、サービスがfactorial
関数だったとします。の値が必要な場合は5!
、を呼び出してfactorial(5)
、次の手順を実行します。
現在の実行場所が保存されます(スタックに保存されますが、それは重要ではありません)
実行はに渡されます factorial
ときにfactorial
完了するが、それは結果を置くどこかで、あなたはそれを得ることができます
実行は[1]にあった場所に戻ります
ここでfactorial
、非常に長い時間がかかったとしましょう。膨大な数を与えているため、スーパーコンピューティングクラスタで実行する必要があるためです。結果が返されるまでに5分かかると想定しているとします。あなたは出来る:
デザインを維持し、眠っている夜にプログラムを実行して、半分の時間だけ画面を見つめないようにします。
一方、他のことを行うには、あなたのプログラムを設計しfactorial
、そのことをやっています
2番目のオプションを選択すると、コールバックが機能する場合があります。
コールバックパターンを利用するにfactorial
は、次の方法で呼び出すことができる必要があります。
factorial(really_big_number, what_to_do_with_the_result)
2番目のパラメーターはwhat_to_do_with_the_result
、戻り値の前にその結果を呼び出すことをfactorial
期待して、に送信する関数ですfactorial
。
はい、これは、factorial
コールバックをサポートするように記述されている必要があることを意味します。
次に、コールバックにパラメーターを渡せるようにしたいとします。あなたはそれを呼ぶつもりはないので、今はできませんfactorial
。したがってfactorial
、パラメーターを渡せるように記述する必要があります。パラメーターを呼び出すと、コールバックに渡されます。次のようになります。
factorial (number, callback, params)
{
result = number! // i can make up operators in my pseudocode
callback (result, params)
}
factorial
このパターンを許可すると、コールバックは次のようになります。
logIt (number, logger)
{
logger.log(number)
}
そしてあなたの呼び出しfactorial
は
factorial(42, logIt, logger)
何かを返却したい場合はどうなりlogIt
ますか?まあ、それはできません。なぜならfactorial
、それに注意を払っていないからです。
さて、なぜfactorial
コールバックが返すものを返すことができないのですか?
実行はfactorial
終了時にコールバックに渡されることを意図しているため、呼び出し側には何も返しません。そして理想的には、別のスレッド/プロセス/マシンで何らかの方法でその作業を開始し、すぐに戻って続行できるようにするでしょう。
factorial(param_1, param_2, ...)
{
new factorial_worker_task(param_1, param_2, ...);
return;
}
これは「非同期呼び出し」になりました。つまり、呼び出すとすぐに戻りますが、まだ実際には機能していません。したがって、それをチェックし、終了時に結果を取得するメカニズムが必要であり、プログラムはプロセスがより複雑になっています。
ちなみに、このパターンを使用factorial_worker_task
すると、コールバックを非同期で起動してすぐに戻ることができます。
答えは、コールバックパターン内に留まることです。いつでも書きたい
a = f()
g(a)
そしてf
、あなたが代わりに書きます、非同期的に呼び出されます
f(g)
どこg
コールバックとして渡されます。
これにより、プログラムのフロートポロジが根本的に変化し、慣れが必要になります。
プログラミング言語は、オンザフライで関数を作成する方法を提供することで、多くのことを支援することができます。すぐ上のコードでは、関数g
はほど小さい場合がありますprint (2*a+1)
。あなたの言語がこれを完全に不必要な名前とシグネチャを持つ別個の関数として定義することを要求するなら、あなたはこのパターンをたくさん使うとあなたの人生は不愉快になるでしょう。
一方、あなたの言語でラムダを作成できる場合は、はるかに良い状態です。その後、次のようなものを書くことになります
f( func(a) { print(2*a+1); })
それはとても良いです。
どのようにコールバック関数を渡しますfactorial
か?まあ、いくつかの方法でそれを行うことができます。
呼び出された関数が同じプロセスで実行されている場合、関数ポインタを渡すことができます
またはfn name --> fn ptr
、プログラムの辞書を維持したい場合は、名前を渡すことができます
多分あなたの言語はラムダとして可能な関数をその場で定義することを可能にします!内部的には、ある種のオブジェクトを作成し、ポインターを渡しますが、それについて心配する必要はありません。
おそらく、呼び出す関数は完全に別のマシンで実行されており、HTTPなどのネットワークプロトコルを使用して呼び出します。コールバックをHTTP呼び出し可能な関数として公開し、そのURLを渡すことができます。
あなたはアイデアを得ます。
私たちが参入したこのウェブ時代では、私たちが呼び出すサービスは、しばしばネットワーク上にあります。多くの場合、これらのサービスを制御することはできません。つまり、これらのサービスを記述しなかった、維持しなかった、サービスが稼働していること、またはサービスのパフォーマンスを確認できません。
しかし、これらのサービスが応答するのを待っている間、プログラムがブロックされることは期待できません。これを認識して、サービスプロバイダーはコールバックパターンを使用してAPIを設計することがよくあります。
JavaScriptは、ラムダやクロージャなどのコールバックを非常にうまくサポートしています。また、JavaScriptの世界には、ブラウザーとサーバーの両方で多くのアクティビティがあります。モバイル用に開発されているJavaScriptプラットフォームさえあります。
前進するにつれて、ますます多くの人が非同期コードを書くようになり、そのためにはこの理解が不可欠です。
コールバックは1つの単語であることに注意してください。
ウィキペディアのコールバックページは、それを非常によく説明しています。
ウィキペディアのページからの引用:
コンピュータープログラミングでは、コールバックは実行可能コードまたは実行可能コードの一部への参照であり、引数として他のコードに渡されます。これにより、下位レベルのソフトウェアレイヤーが上位レベルのレイヤーで定義されたサブルーチン(または関数)を呼び出すことができます。
コールバック関数は、特定の条件が満たされたときに呼び出される関数です。コールバック関数はすぐに呼び出されるのではなく、将来のある時点で呼び出されます。
通常、非同期で終了する(つまり、呼び出し元の関数が戻った後しばらくすると終了する)タスクが開始されるときに使用されます。
たとえば、ウェブページをリクエストする関数では、ウェブページのダウンロードが完了したときに呼び出されるコールバック関数を呼び出し元に提供する必要があります。
"...when a condition is met"
が、親関数の実行が終了し、条件(?)に依存しないときにコールバックが呼び出されると思いました。
コールバックは、電話システムの観点から最も簡単に説明できます。関数呼び出しは、電話で誰かに電話をかけ、質問をし、回答を得て、電話を切るのに似ています。コールバックを追加すると、アナロジーが変更され、彼女に質問した後、彼女に名前と番号を与えて、彼女があなたに電話で返答できるようにします。
-Paul Jakubik、「C ++でのコールバック実装」
この「コールバック」専門用語は、多くの場所で誤って使用されていると思います。私の定義は次のようになります:
コールバック関数は、誰かに渡し、ある時点で呼び出せるようにする関数です。
私は人々がウィキ定義の最初の文を読んだだけだと思います:
コールバックは、他のコードへの引数として渡される実行可能コードまたは実行可能コードの一部への参照です。
私は多くのAPIを扱ってきました。さまざまな悪い例を参照してください。多くの人は、関数ポインター(実行可能コードへの参照)または匿名関数(実行可能コードの一部)に「コールバック」という名前を付ける傾向があります。これらが単なる関数であれば、なぜ別の名前が必要なのですか?
実際には、wiki定義の2番目の文だけが、コールバック関数と通常の関数の違いを明らかにしています。
これにより、下位レベルのソフトウェアレイヤーが上位レベルのレイヤーで定義されたサブルーチン(または関数)を呼び出すことができます。
そのため、違いは、関数を渡すのはだれであり、渡された関数がどのように呼び出されるかです。関数を定義して別の関数に渡し、その関数本体で直接呼び出す場合は、コールバックと呼ばないでください。定義によると、渡された関数は「下位レベル」の関数から呼び出されます。
あいまいな状況でこの単語の使用をやめてほしいと思います。理解を深めるのに役立ちません。
シンプルにしましょう。コールバック機能とは?
たとえ話と類推による例
秘書がいます。毎日私は彼女に次のことを求めます:(i)会社の郵便を郵便局に送り、それが終わったら次のようにします:(ii)付箋の 1つに彼女のために書いたタスク。
さて、付箋のタスクは何ですか?タスクは日々異なります。
この特定の日に、私は彼女にいくつかの文書を印刷するように要求するとします。それを付箋に書いて、彼女が投稿する必要がある送信メールと一緒に彼女の机にそれをピンで留めます。
要約すれば:
コールバック関数は、その2番目のタスクである、それらのドキュメントの印刷です。これは、メールがドロップされた後に行われるため、また、ドキュメントを印刷するように彼女に通知する付箋が、彼女が投稿する必要のあるメールと共に彼女に与えられるからです。
これをプログラミング語彙と結び付けましょう
それだけです。これ以上何もない。私はそれがあなたのためにクリアされたことを願っています-そうでない場合は、コメントを投稿してください。明確にするために最善を尽くします。
これにより、コールバックはメソッドの最後のreturnステートメントのように聞こえます。
それが何であるかはわかりません。
コールバックは、別の関数が呼び出されて完了した結果として、実際には関数の呼び出しであると思います。
私はまた、コールバックは、「ちょっと!あなたが求めたものですか?私はそれをやった-ちょうどあなたに知らせたいと思った-あなたに戻って」というような形で、元の呼び出しに対処することを意図しているとも思います。
コールバックとは何ですか?
コールバック関数とは何ですか?
otherFunction
パラメータとして)、およびコールバック関数が呼び出される(または実行)される内部otherFunction
。 function action(x, y, callback) {
return callback(x, y);
}
function multiplication(x, y) {
return x * y;
}
function addition(x, y) {
return x + y;
}
alert(action(10, 10, multiplication)); // output: 100
alert(action(10, 10, addition)); // output: 20
SOAでは、コールバックにより、プラグインモジュールはコンテナー/環境からサービスにアクセスできます。
Call Afterは、愚かな名前であるcallbackよりも良い名前です。関数内で条件が満たされた場合、または条件が満たされた場合は、引数として受け取った別の関数Call After関数を呼び出します。
関数内の内部関数をハードコードするのではなく、すでに記述されたCall After関数を引数として受け入れる関数を記述します。後の呼び出しは、引数を受け取る関数のコードによって検出された状態変化に基づいて呼び出される場合があります。
我々は機能があると仮定しsort(int *arraytobesorted,void (*algorithmchosen)(void))
、それが中にいくつかの点で使用することができ、引数として関数ポインタを受け付けることができるsort()
の実装を。次に、関数ポインタによってアドレス指定されているコードがコールバック関数algorithmchosen
として呼び出されます。
また、次のようなアルゴリズムを選択できるという利点もあります。
1. algorithmchosen = bubblesort
2. algorithmchosen = heapsort
3. algorithmchosen = mergesort ...
これは、たとえば、プロトタイプで実装されています。
1. `void bubblesort(void)`
2. `void heapsort(void)`
3. `void mergesort(void)` ...
これは、オブジェクト指向プログラミングでポリモーフィズムを実現するために使用される概念です。
「コンピュータープログラミングでは、コールバックは実行可能コードまたは実行可能コードの一部への参照であり、引数として他のコードに渡されます。これにより、下位レベルのソフトウェアレイヤーは、上位レベルのレイヤーで定義されたサブルーチン(または関数)を呼び出すことができます。」-ウィキペディア
関数ポインターを使用した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つのインターフェースは、ボタンやチェックボックスなどのウィジェット派生UIコンポーネントで発生する可能性のあるイベントをリッスンします。したがって、この例をJavaインターフェースを使用した以前のコールバックの例と比較すると、これら2つのインターフェースはコールバックインターフェースとして機能します。したがって、上位レベルのコード(Here Activity)はこれら2つのインターフェースを実装します。また、ウィジェットに対してイベントが発生すると、上位レベルのコード(または上位レベルのコードに実装されたこれらのインターフェースのメソッド、ここではActivity)が呼び出されます。
次に、コールバックパターンとイベントリスナーパターンの基本的な違いについて説明します。コールバックを使用すると説明したように、呼び出し先は単一の呼び出し元にのみ通知できます。ただし、EventListenerパターンの場合、アプリケーションの他の部分またはクラスは、ボタンまたはチェックボックスで発生する可能性のあるイベントに登録できます。この種のクラスの例は、OtherClassです。OtherClassのコードが表示されている場合、アクティビティで定義されたボタンで発生する可能性のあるClickEventのリスナーとして登録されていることがわかります。興味深い点は、アクティビティ(呼び出し元)の他に、ボタンでクリックイベントが発生するたびに、このOtherClassにも通知が送られることです。
高次関数とも呼ばれるコールバック関数は、パラメーターとして別の関数に渡される関数であり、コールバック関数は親関数内で呼び出されます(または実行されます)。
$("#button_1").click(function() {
alert("button 1 Clicked");
});
ここでは、関数をパラメーターとしてクリックメソッドに渡します。そして、クリックメソッドは、渡されたコールバック関数を呼び出し(または実行)します。
コールバック関数 引数として別の関数に渡された関数。
function test_function(){
alert("Hello world");
}
setTimeout(test_function, 2000);
注:上記の例では、setTimeout関数の引数としてtest_functionを使用しています。