関数をパラメーターとして他の関数に渡しますか?


40

AS3アプリケーションとバックエンドとの通信方法を変更するプロセスを進めており、RESTシステムを実装して古いシステムを置き換えるプロセスを進めています。

悲しいことに、作業を開始した開発者は現在、長期の病気休暇中であり、私に引き継がれています。私は過去1週間かそこらでこれを使って作業しており、システムを理解していますが、心配していることが1つあります。関数の関数への受け渡しが多いようです。たとえば、サーバーへの呼び出しを行うクラスは、プロセスが完了してエラーが処理されたときにオブジェクトを呼び出して渡す関数を受け取ります。

それは恐ろしい練習であると感じる「悪い気持ち」を与えてくれます。そして、いくつかの理由を考えることができますが、システムにやり直しを提案する前に確認が必要です。この問題の経験がある人はいないだろうか?


22
どうしてそんな感じ?関数型プログラミングの経験はありますか?(私はそうではないと思いますが、あなたは見てみる必要があります)
フォシ14

8
次に、これをレッスンと考えてください!アカデミアが教えるものと実際に役立つものは、しばしば驚くほどほとんど重複しません。その種のドグマに適合しないプログラミング技術の世界はそこにあります。
Phoshi

32
関数を他の関数に渡すことは、言語がサポートしていない場合、問題の関数を一時的なものとして含めることを唯一の目的とするささいな「オブジェクト」を作成するための方法です。
ドーバル14

36
戻る私の一日で、私たちは、「コールバック」機能を介してこれらのパスと呼ばれる
マイク・

回答:


84

それは問題ではありません。

これは既知の手法です。これらは高階関数(関数をパラメーターとして受け取る関数)です。

この種の関数は、関数型プログラミングの基本的な構成要素でもあり、Haskellなどの関数型言語で広く使用されています。

そのような機能は悪くも良いこともありません-概念や技術に一度も遭遇したことがない場合、最初は把握するのが難しいかもしれませんが、非常に強力であり、ツールベルトに含めるのに適したツールです。


そのおかげで、私は関数型プログラミングの実際の経験がないので、それを調べていきます。私の小さな知識から、プライベート関数として何かを宣言すると、そのクラスだけがそれにアクセスでき、パブリックのものはオブジェクトを参照して呼び出す必要があるため、うまくいきませんでした。私はそれを適切に調べて、もう少し感謝することを期待しています!
エリオットブラックバーン14


8
あなたはそれがどのように使われるかを制御したい場合は、その使用は結構です、特定の機能のためのコールバックとしてある場合は、プライベートにするために何かを宣言@BlueHat
ラチェットフリーク

5
まさに。プライベートとマークすることは、他の誰かがアクセスして、好きなときに名前でアクセスしたくないことを意味します。プライベート関数を他の誰かに渡すのは、そのパラメーターについて文書化する方法で呼び出してほしいからです。プライベートフィールドの値をパラメーターとして他の関数に渡すことと何の違いもありません。おそらくあなたと一緒にひどく座っているわけではありません:-)
Steve Jessop 14

2
コンストラクターパラメーターとして関数を使用して具体的なクラスを作成していることに気付いた場合、おそらく間違った tmを実行していることに注意してください。
ガスドール14

30

関数型プログラミングに使用されるだけではありません。それらはコールバックとしても知られています

コールバックは、他のコードに引数として渡される実行可能コードの一部であり、都合のよいときに引数をコールバック(実行)することが期待されます。呼び出しは、同期コールバックのように即座に行われる場合と、非同期コールバックのように後で発生する場合があります。

非同期コードについて少し考えてみましょう。たとえば、データをユーザーに送信する関数を渡します。コードが完了した場合にのみ、応答の結果でこの関数を呼び出します。関数はこの関数を使用して、データをユーザーに送り返します。それは考え方の変化です。

シードボックスからトレントデータを取得するライブラリを作成しました。非ブロッキングイベントループを使用してこのライブラリを実行し、データを取得してから、ユーザーに返します(たとえば、websocketコンテキストで)。このイベントループには5人のユーザーが接続しており、誰かの急流データを失速させる要求の1つを想像してください。それはループ全体をブロックします。そのため、非同期に考えてコールバックを使用する必要があります。ループが実行され続け、「ユーザーにデータを返す」ことは、関数の実行が終了したときにのみ実行されるため、待機しません。発射して忘れてください。


1
+1コールバックの用語を使用します。「高階関数」よりも頻繁にこの用語に出会います。
ロドニーシューラー14

OPがコールバック、具体的にはコールバックを記述しているように見えたため、この回答が好きです。たとえば、サーバーへの呼び出しを行うクラスは、オブジェクトを呼び出して渡す関数を受け取りますプロセスが完了してエラーが処理されたときなどに」
Ajedi32 14

11

これは悪いことではありません。実際、それは非常に良いことです。

関数に関数を渡すことはプログラミングにとって非常に重要であるため、短縮形としてラムダ関数を発明しました。たとえば、C ++アルゴリズムでラムダを使用して、非常にコンパクトでありながら表現力のあるコードを記述し、汎用アルゴリズムがローカル変数やその他の状態を使用して検索や並べ替えなどを行えるようにします。

また、オブジェクト指向ライブラリには、本質的に少数の関数(理想的には1つですが、常にではない)を指定するインターフェイスであるコールバックがあります。次に、そのインターフェイスを実装する単純なクラスを作成し、そのクラスのオブジェクトを関数に渡すことができます。これは、イベント駆動型プログラミングの基礎であり、フレームワークレベルのコード(おそらく別のスレッドでも)は、ユーザーアクションに応答して状態を変更するためにオブジェクトを呼び出す必要があります。JavaのActionListenerインターフェイスは、この良い例です。

技術的には、C ++ファンクターはoperator()()、同じことを行うために構文シュガーを活用する一種のコールバックオブジェクトでもあります。

最後に、Cスタイルの関数ポインターがありますが、これはCでのみ使用する必要があります。詳細については説明しませんが、完全を期すために言及しています。上記の他の抽象化ははるかに優れており、それらを含む言語で使用する必要があります。

他の人は、関数型プログラミングと、それらの言語で関数を渡すことが非常に自然であることに言及しています。ラムダとコールバックは、手続き型言語とOOP言語がそれを模倣する方法であり、非常に強力で便利です。


また、C#デリゲートとラムダは両方とも広範囲に使用されます。
悪犬パイ14

6

すでに述べたように、それは悪い習慣ではありません。これは、責任を切り離して分離する方法にすぎません。たとえば、OOPでは次のようなことを行います。

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

ジェネリックメソッドは、インターフェイスを実装する別のオブジェクトに、特定のタスク(何も知らない)を委任します。ジェネリックメソッドはこのインターフェイスのみを知っています。あなたの場合、このインターフェースは呼び出される関数になります。


1
このイディオムは、単純に生の機能を渡すのと非常に似た何かを達成、インタフェースの機能をラップしている。

はい、そうです、これは珍しい習慣ではないことを示したかっただけです。
フィリップマリー14

3

一般に、関数を他の関数に渡すことには何の問題もありません。非同期呼び出しを行っており、その結果で何かをしたい場合は、何らかのコールバックメカニズムが必要になります。

ただし、単純なコールバックにはいくつかの潜在的な欠点があります。

  • 一連の呼び出しを行うには、コールバックの深いネストが必要になる場合があります。
  • エラー処理では、一連の呼び出しで呼び出しごとに繰り返しが必要になる場合があります。
  • 複数の呼び出しを同時に調整し、それらがすべて終了した後に何かをするように、複数の呼び出しを調整するのは厄介です。
  • 一連の呼び出しをキャンセルする一般的な方法はありません。

シンプルなWebサービスでは、その方法で問題なく動作しますが、より複雑な呼び出しのシーケンスが必要な場合は扱いにくくなります。ただし、いくつかの選択肢があります。たとえばJavaScriptでは、promiseの使用への移行がありました(javascript promiseのすごいところ)。

それらはまだ他の関数への関数の受け渡しを伴いますが、非同期呼び出しは、コールバックを直接取得するのではなく、コールバックを取得する値を返します。これにより、これらの呼び出しをより柔軟に構成できます。このようなものはActionScriptでかなり簡単に実装できます。


また、コールバックを不必要に使用すると、コードを読んでいる人の実行フローを追跡するのが難しくなることも追加します。明示的な呼び出しは、コードの遠い部分に設定されたコールバックよりも理解しやすいです。(救助へのブレークポイント!)それにもかかわらず、これはブラウザや他のイベントベースのシステムでイベントが発生する方法です。次に、イベントハンドラーが呼び出されるタイミングと順序を知るために、イベントエミッターの戦略を理解する必要があります。
joeytwiddle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.