なぜ「コールバック関数」が必要なのですか?


16

私はその本を読んでいprogramming in Luaます。と言った

クロージャーは、多くの状況で価値のあるツールを提供します。これまで見てきたように、これらはsortなどの高階関数の引数として役立ちます。クロージャーは、newCounterの例のように、他の関数を構築する関数にも役立ちます。このメカニズムにより、Luaプログラムは機能的な世界からの洗練されたプログラミング手法を組み込むことができます。クロージャーはコールバック関数にも役立ちます。ここでの典型的な例は、従来のGUIツールキットでボタンを作成するときに発生します。各ボタンには、ユーザーがボタンを押したときに呼び出されるコールバック関数があります。ボタンを押したときに、ボタンごとにわずかに異なる処理を行う必要があります。たとえば、デジタル計算機には、各桁に1つずつ、10個の同様のボタンが必要です。次のような関数を使用して、それぞれを作成できます。

function digitButton (digit)
  return Button{label = tostring(digit),
                action = function ()
                  add_to_display(digit)
                  end}
end

を呼び出すdigitButtonとを返すように見えるのでaction(これによりクロージャが作成されます)、digit渡されたにアクセスできますdigitButton

私の質問は:

Why we need call back functions? what situations can I apply this to?


著者は言った:

この例では、Buttonは新しいボタンを作成するツールキット関数であると想定しています。labelはボタンのラベルです。actionは、ボタンが押されたときに呼び出されるコールバッククロージャーです。コールバックは、digitButtonがタスクを実行した後、ローカル変数digitがスコープから出た後、長時間呼び出すことができますが、それでもこの変数にアクセスできます。

著者によると、同様の例は次のようになります。

function Button(t)
  -- maybe you should set the button here
  return t.action -- so that you can call this later
end

function add_to_display(digit)
  print ("Display the button label: " .. tostring(digit))
end

function digitButton(digit)
  return Button{label = tostring(digit),
                action = function ()
                           add_to_display(digit)
                           end}
end

click_action = digitButton(10)
click_action()

したがって、 the callback can be called a long time after digitButton did its task and after the local variable digit went out of scope.


回答:


45

Guy 1からGuy 2:ユーザーがそこをクリックしたときに何かをしたいのですが、それがうまくいったら電話してください。

ユーザーがここをクリックすると、Guy 2はGuy 1にコールバックします。


1
私はこのコールバックジョークがとても好きです:-P
ルーカスリー

6
私だけでしょうか?コールバック関数が必要な理由をこれがどのように説明するのかわかりません。
phant0m

2
それは、Guy 1がGuy 2に「時が来たらこれを行う」と言っているようなものです。Guy 2は自分の一日を過ごして、時間がきたらそれを行います。Guy 1がGuy 2の発信者になることを意味する場合、Guy 2はGuy 1にコールバックしないため、これは間違っています。実際、Guy 1がGuy 2の呼び出し元である場合、このシナリオでは手にハードループが発生します。Guy 1がコールバックするつもりだった場合、コールバックはGuy 2が誰であるかわからず、確かに彼に電話をかけるように求めないため、これは間違っています。
リズム

これは恐ろしい説明です。
インサイド

21

いいえ、アクションを返すことはありません。ユーザーがクリックすると、ボタンが実行します。それが答えです。制御しないイベントに反応して別のコンポーネントで発生するアクションを定義する場合は、コールバック関数が必要です(システムの一部であるイベントループは、ボタンのメソッドを実行します。アクション)。


私はあなたに同意しません。投稿を編集し、いくつかのコードを追加しました。私はまだアクションが即座に実行されないと思います。
ルーカスリー

2
ティム、あなたは正しい-しかし、Janもそうだ。「ユーザーがクリックするとボタンが実行する」。即座にではない。
AlexanderBrevig

@AlexanderBrevigええ、まだJanの返信は私にとって非常に役立つと思います。コールバックとは何か、なぜコールバックが必要なのかを知ることができます。
ルーカスリー

コールバックは、誰かが最初の関数が起動されたときに機能し、おそらく基準に基づいて機能する2番目の関数に追加された単なる汚い名前です。関数well_done()を呼び出すと、違いはありません。コールバックは検証に似ており、検証は通常2つのメッセージのシーケンスであり、コールバックは通常2つの関数のシーケンスです。3つ以上のコールバックを連鎖させた場合、おめでとうございます。
m3nda

16

コールバックの使用のより良い例は、非同期関数にあると思います。Luaについて話すことはできませんが、JavaScriptで最も一般的な非同期アクションの1つは、AJAXを介してサーバーに接続することです。

AJAX呼び出しは非同期です。つまり、次のようなことをすると:

var ajax = ajaxCall();
if(ajax == true) {
  // Do something
}

ifブロックの内容が確実に正しく実行されるわけではありません。ajaxCall()実行されるのは、実行がifステートメントに到達する前に関数が終了したからです。

ただし、コールバックは、必要な関数を呼び出す前に非同期呼び出しが確実に終了するようにすることで、この問題を解消します。したがって、コードは次のように変更されます。

function ajaxCallback(response) {   
  if(response == true) {
    // Do something   
  }
}

ajaxCall(ajaxCallback);

これの目的は、インターフェイスの描画など、他のことを妨げることなく、データの収集などを実行できるようにすることです。データ収集が同期的である場合、アプリケーションがデータの取得を待機しているため、インターフェイスは応答しなくなります。ほとんどのユーザーはアプリケーションが「クラッシュ」したと考え、プロセスを終了しようとするため、応答のないインターフェイスはユーザーエクスペリエンスにとって非常に悪いです。

これを実際に見るには、ページ全体をリロードせずにあらゆる種類の更新を行うWebサイトを見るだけです。Twitter、LinkedIn、およびStackExchangeサイトは良い例です。Twitterのフィードの「無限スクロール」とSEの通知(ユーザー通知、および質問に新しいアクティビティがあるという通知の両方)は、非同期呼び出しによって強化されています。どこかにスピナーが表示されている場合、それはセクションが非同期呼び出しを行っており、その呼び出しが終了するのを待っていることを意味しますが、インターフェイス上の他のものと対話できます(さらに他の非同期呼び出しを行うこともできます)。呼び出しが終了すると、それに応じて反応し、インターフェイスを更新します。


12

あなたは質問をしました

「コールバック関数」が必要な理由

簡潔で明快な方法で回答しようとします(失敗した場合は、この回答の下部にあるwikiリンクを参照してください)。

コールバックは、何かの特定の実装を可能な限り最後まで延期するために使用されます。

ボタンの例は、これの良い例です。プリンターにページを印刷するボタンをアプリケーションPrinterButtonに配置するとします。これを行うには、新しいクラス全体をコーディングする必要がある世界を想像できます。

幸いなことに、コールバックの概念があります(継承とテンプレートパターンの使用も一種のコールバックセマンティックです)。その必要はありません。

ボタンのすべての側面を再実装する代わりに、ボタンの実装に追加します。Buttonやっての概念がある何かを、それが押されていますときに。私たちは単にそれが何かを伝えます。

フレームワークに動作を注入するメカニズムは、コールバックと呼ばれます。

例:

HTMLボタンにいくつかの動作を注入しましょう。

<button onclick="print()">Print page through a callback to the print() method</button>

onclickこの例では、コールバック(この例ではprint()メソッド)を指します。


http://en.wikipedia.org/wiki/Callback_(computer_programming)


おかげで、あなたのイラストを読んだ後、同期コールバックのみの例のウィキを読みに行きます。
ルーカスリー

関数に問題が発生するたびに答えを伝え、問題に耳を傾ける必要があります。自分自身で処理する問題を解決する関数を教えます。
ジョー

8

コールバック関数が必要な理由 これはどのような状況に適用できますか?

コールバック関数は、イベント駆動型プログラミングで非常に役立ちます。イベントが正しいコードをトリガーするようにプログラムをセットアップできます。これは、ユーザーがUI上の任意の要素(ボタンやメニュー項目など)をクリックできるGUIを備えたプログラムで非常に一般的であり、適切なコードが実行されます。


1
+それはコールバックが良いことですが、私はそれらを書く必要がないことを嫌います :-)
マイクダンラベイ

私が新入生だった頃、先生はMFCでのプログラミング方法を教えてくれましたが、コールバックとは何も教えてくれませんでした:
Lucas Li

2

コールバックなしで何が起こるか:

Guy 1:さて、私はそのことが起こるのを待っています。(口hist、親指をいじる)

ガイ2:くそ!Guy 1が私に物を見せないのはなぜですか?私は何かが起こっているのを見たいです!! 私のものはどこにあります?誰もこの辺りで何かをすることになっているのでしょうか?


1

まず、コールバックという用語は、何かに使用されているクロージャーを指します。

たとえば、クロージャ関数を作成し、変数に保存するだけだとします。何にも使用されていないため、コールバックではありません。

ただし、クロージャーを作成し、何かが発生したときに呼び出される場所に格納するとします。現在はコールバックと呼ばれています。

通常、コールバックは、プログラムを呼び出す部分とは異なる部分によって作成されます。したがって、プログラムの一部が他の部分を「コールバック」していると想像するのは簡単です。

簡単に言えば、コールバックを使用すると、プログラムのある部分が、何かが発生したときに別の部分に何か(何でも)を行うように指示できます。

クロージャーで使用されているために変数が生き続けるのは、アップバリューと呼ばれる完全に異なる機能です(別名、「Luaを話さない人の間でのローカル変数の寿命の延長」)。また、非常に便利です。


ええ、Extending the lifetime of local variables用語を言うことupvalueです。
ルーカスリー

うーん、面白い。Googleの簡単な検索では、他の言語を説明するために時折使用されていることが示されていますが、アップバリューという用語はLuaに固有のようです。回答に追加します。
ジョナサングレイフ

1

少なくとも、Fortranの初期からコールバックが行われています。たとえば、ルンゲクッタソルバーなどのODE(常微分方程式)ソルバーがある場合、次のようになります。

subroutine rungekutta(neq, t, tend, y, deriv)
  integer neq             ! number of differential equations
  double precision t      ! initial time
  double precision tend   ! final time
  double precision y(neq) ! state vector
  ! deriv is the callback function to evaluate the state derivative
  ...
  deriv(neq, t, y, yprime) ! call deriv to get the state derivative
  ...
  deriv(neq, t, y, yprime) ! call it several times
  ...
end subroutine

これにより、呼び出し元は、必要なときに呼び出される特別な目的の関数を渡すことにより、サブルーチンの動作をカスタマイズできます。これにより、ODEソルバーを使用してさまざまなシステムをシミュレートできます。


1

これにつまずいて、より最新の答えを提供したかった...

コールバック関数を使用すると、後でデータを処理して、コードの残りの部分を待機せずに実行できます。非同期コードにより、後で何かを実行するという贅沢が可能になります。私が遭遇したコールバック関数の最も読みやすい例は、JavaScriptの約束です。下の例では、function(result)または(newResult)または(finalResult) ... が表示されるたびに、これらはコールバック関数です。中括弧内のコードは、サーバーからデータが返されると実行されます。現時点で必要なデータが利用可能になったため、これらの機能を実行する意味があります。

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

コードは... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promisesから取得され ます

うまくいけば、これは誰かを助ける。これは私がコールバックを理解するのに役立ちました:)


1
これは、前の9つの回答で作成および説明されたポイントに対して実質的な何かを提供していないようです。
ブヨ

1
たぶんあなたのためかもしれませんが、これが本当にコールバックを明確にしてくれたので、共有したいと思いました。IMOの例の読みやすさは、価値があるものです。特に初心者にとっては、コードの可読性が大いに役立つと確信しています。
NRV

-2

私はluaを知りませんが、一般的にコールバックメソッドはマルチスレッドのようなものです。たとえば、モバイルアプリケーション開発プログラミングでは、ほとんどのアプリケーションは、サーバーに要求を送信し、サーバーからの応答として受信したデータでUIを操作するように機能します。ユーザーがサーバーにリクエストを送信すると、サーバーから応答を取得するのに時間がかかりますが、最高のUX UIの場合はスタックしないでください。

このため、複数のスレッドを使用して並列操作を行います。サーバーから応答を取得したら、UIを更新する必要があります。更新するには、そのスレッドから通知する必要があります。機能的な世界から。このタイプの関数呼び出しは、コールバックメソッドと呼ばれます。これらのメソッドを呼び出すと、コントロールはメインスレッドに戻るはずです。たとえば、コールバックメソッドはObjective-Cのブロックです。


ええ、あなたが言ったことは、本を読んだ後の私の理解と一致しています。
ルーカスリー

2
コールバックメソッドは、マルチスレッドのようなものではありません。コールバックメソッドは、単なる関数ポインター(デリゲート)です。スレッドは、CPUコンテキストの切り替え/利用に関するものです。非常に単純化された対照は、スレッド化はUXのCPU最適化に関するものであり、コールバックはポリモーフィズムのメモリ切り替えに関するものであるということです。スレッドがコールバックを実行する可能性があるからといって、スレッドとコールバックが類似していることを意味するわけではありません。スレッドは、静的クラスで静的メソッドも実行しますが、スレッド化と静的タイプは似ていません。
リズム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.