1)
以下はPython関数summation
です。立方体/正方形の合計/..、同様の操作を実行できます。
def identity(k): return k def cube(k): return pow(k, 3) def square(k): return pow(k,2) def summation(n, term): if n == 0: return 0 else: return term(n) + summation(n-1, term) def sum_cubes(n): return summation(n, cube) if __name__ == "__main__": sum = sum_cubes(4) print(sum) """ In C, We can implement the same using function pointers. Goal is, to perform similar operations(Sum of ..) using single function summation()"""
2)
以下で、APIをCから並べ替えることを検討してください。
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
ここで、qsort
は任意のタイプのデータを並べ替えることができます、ディレクトリ/文字列/内のfloat /ファイル名の配列…
質問:
汎用関数を定義する方法は?
summation
は汎用関数ですか?
または
qsort
は汎用関数ですか?
または
2つの例を挙げますが、汎用関数は無効な用語ですか?
注:動機-用語 div id = “4ce7c96277”>
または私が設計した任意の並べ替え関数
コメント
回答
「一般的」にはいくつかの意味があります。
非公式の定義
“generic” 日常の言語では、共通のプロパティを共有しますが、いくつかの点であまり具体的ではありません。
この観点から、 qsort()
を一般的なものと見なすことができます:この関数のコードQSORTアルゴリズムを使用して比較関数を定義できる固定サイズのデータ構造を並べ替えることができます。
同じことがsummation()
関数にも当てはまります。この関数は、1つのパラメーターを持つ関数を使用して取得された用語を要約します。
正式な定義
C ++やJavaなどのプログラミング言語では、ジェネリックプログラミングが可能ですテンプレートまたはジェネリックを使用する場合:
C ++ 14標準からの定義: テンプレートは、クラスまたは関数のファミリー、またはタイプのファミリーのエイリアスを定義します。
原則として、クラスまたは関数の実装は型によってパラメーター化できます。
このより正式な観点によれば、qsort()
はジェネリック関数ではありません。その実装はコンパイル時に型を決定する必要がなく、その動作は型に依存しません。必要なのは、ソートされる要素のサイズだけです。このサイズは、実行時に処理される通常の引数です。
Pythonなどの静的に型指定されていない言語の場合、。実装と動作が型に依存しないため、一般的ではないと思います。この関数は高階関数であり、引数term
関数。タイプに基づいてこの関数の動作を変更する機能は使用しません。
一般的な関数の説明については、C ++標準関数 std::sort()
:その実装は、引数のタイプ(およびオプションで決定されたタイプの引数との比較関数)によって異なります。 C ++テンプレートの機能を使用することにより、汎用関数の実装に必要な演算子/メンバー関数/特性/イテレーターがあるという条件の下で、任意のタイプの任意のコンテナーをソートできます。
動的型付き言語にジェネリック関数を含めることはできますか
動的型付き言語には静的に型付けされた言語よりも一般的でないコード。
たとえば、動的タイプのオブジェクトのコンテナがある場合、コンテナ内の2つの要素の任意の組み合わせを比較できる限り、qsort関数はコンテナを一般的にソートできます。
しかし、このような柔軟な環境でも、ジェネリック型に依存するプログラミングが役立つ場合があります。典型的なユースケースはマルチメソッドであり、動作またはコードは引数のタイプまたはタイプの組み合わせ(2つの異なる形状間の交差を決定するためなど)に依存します。詳細については、以下を参照してください。
コメント
- わからない、なぜジェネリックス(主にJavaでの型キャストを回避するために使用&は、ジェネリック関数の定義でポリモーフィズムの実行に役立ちますか?
- @overexchange Javaは、ジェネリックメソッドを含むジェネリックプログラミングも提供すると思います(仕様またはチュートリアルを参照してください)。それでも、’定義部分を少し編集して、コメントに対応しました。
- Pythonのジェネリックパッケージは、ジェネリック関数とは何の関係もありません。同じ形容詞を共有することを除いて。
- @Killian if ジェネリックプログラミングは、具体的で効率的なアルゴリズムから抽象化するという考えです。さまざまなデータ表現と組み合わせることができる汎用アルゴリズムを取得するには、そのパッケージのマルチメソッドを含める必要があると思います。’そう思いませんか?
回答
ジェネリック関数は、コンパイル時に少なくとも1つの関数引数の型をジェネリックに取ります。つまり、コンパイラは特定の場所で使用されているタイプを見つけて、関数で使用されているこのタイプを正確に適用します。例えば。 +
演算子で使用される汎用引数が関数にある場合、型には適切なメソッドが必要です。文字列/配列の場合、これは多くの場合、連結であり、整数/浮動小数点の加算になります。コンパイラーは、が正しい操作を適用していることを検出できます。 Cルーチンは、その意味でジェネリックではありません。これは、コンパイラが型を検出して正しいサイズを使用するのではなく、サイズ情報を適用するプログラマであるためです。
架空の言語など
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
ここで、コンパイラは最初のケースで2つの文字列が適用されていることを検出し、内部で次のように展開します
func add(p1:string, p2:string)
+
を連結として扱い、2番目のケースでは拡張します
func add(p1:int, p2:int)
提供されたとおり整数パラメータ。ジェネリックとは、コンパイラがコンパイル時に個々のコードを生成することを意味します。たとえば、Pythonは型指定されておらず、実行時にそのような置換を行います。つまり、すべてがジェネリックであるため、Pythonにはジェネリック関数がありません。
コメント
- わかりませんでした。つまり、+はジェネリック関数であり、C ++の構文ですか?
- 関数の引数を取る関数は高次です。関数s Python / JavaScriptの世界。 Cでは、同じように関数ポインタが必要です。
- 上記の編集を参照してください。
- つまり、
summation
、高階関数? - ジェネリックとは何かについては、多くの定義があります。たとえば、Stroustrupは、型をパラメータとして使用する”プログラミング”として定義しています。ウィキペディアのリファレンスについては、’次のURLにアクセスしてください: en.wikipedia.org/wiki/Generic_programming
回答
これをC ++の観点から始めて、Cに進みます。
C、C ++、Javaなどの静的に型指定された言語では、「ジェネリック」関数を使用すると、さまざまな型のプレースホルダーを使用して、関数の操作を 1回指定できます。異なる呼び出し間(つまり、qsort
やbsearch
は間違いなく not ジェネリック関数)。理想的には、コンパイラがこのジェネリック関数の呼び出しを自動的に検出し、必要に応じて実際のコードを生成することも必要です。
C ++は、テンプレートを提供することでこれを簡単に 1 します:
template <typename T> T summation( T *values, size_t numValues ) { T result = 0; for ( size_t i = 0; i < numValues; i++ ) result += values[i]; return result; }
T
は任意のタイプ 2 のプレースホルダーであるため、
int ivals[] = {1,2,3,4,5,6,7,8,9}; double dvals[] = {1,2,3,4,5,6,7,8,9}; int sumi = summation( ivals, 10 ); double sumd = summation( dvals, 10 );
コードがコンパイルされると、コンパイラはsummation
への2つの呼び出しを確認し、引数のタイプを推定します。異なるタイプごとに、関数の新しいインスタンスを生成し、一意の名前を付けます。
int summation_i( int *values, size_t numValues ) // actual compilers will generate { // more complex "mangled" names int result = 0; // than this ... } double summation_d( double *values, size_t numValues ) { double result = 0; ... }
次に、コードを生成します。 summation_i
の結果がsumi
に割り当てられ、summation_d
が
。
Cは、テンプレート機能に似たものを提供しません。従来、私たちは、マクロを使用するか、void *
どこでも、型認識操作を他の関数に委任します。
ここに「マクロベースのソリューションの悪い例:
#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(t,x,s) summation_##t(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(int, ivals, 5); double sumd = SUMMATION(double, dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; }
SUMMATION_DEF
は、マクロパラメータt
を型プレースホルダーとして使用して関数操作を指定するという点で、テンプレートとほぼ同じです。t
–##
はトークン貼り付け演算子であり、プリプロセッサはt
そしてその値を関数の名前に追加します 3 。
C ++と異なるのは、マクロが単なるダムテキスト置換であるという事実です。トリガーされません。コンパイラ側の特別な操作。実際の関数インスタンスは、SUMMATION
マクロの呼び出しに基づいて自動的に生成されるわけではありません。必要な関数を明示的に生成する必要があります(したがって、SUMMATION_DEF(int)
およびSUMMATION_DEF(double)
の前にmain
)。これは、summation_xxx
を呼び出す場合も意味します。 SUMMATION
マクロを介して、適切な関数が呼び出されるように、マクロ引数リストの一部として型を渡す必要があります。なんて面倒なことでしょう。
C 2011標準では、_Generic
キーワードが追加されました。これにより、その点で生活が少し楽になります。
#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(x,s) _Generic((x), \ int * : summation_int, \ double * : summation_double \ )(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(ivals, 5); double sumd = SUMMATION(dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; }
_Generic
キーワードを使用すると、タイプに基づいて式を評価できます。したがって、iv id =の最初の引数のタイプが”ab6b2923ab”>
はint *
であり、summation_int
と呼びます。それは、summation_double
と呼びます。このように、マクロ引数で型名を指定する必要はありません。
もう1つのアプローチは、「ご覧のとおり、void *
を使用して、型認識操作を他の関数に委任することです。前述のように、それは」型ごとに各比較関数を手動で実装する必要があるため、実際には「ジェネリック」プログラミングではありません。 「一度コーディングして完了することはできません。void *
を使用することで、基本的に型安全性をウィンドウの外に投げ出し、対向するトラフィックに投入します。
そして、誰かが文句を言う前に-いいえ、これらの合計関数はどれも算術オーバーフローをチェックしたり処理したりしません。それは別の日の主題です。
- 「簡単」の定義が十分に緩い場合。テンプレートをサポートするために使用されるメタプログラミング言語はチューリング完全であるため、*本当に驚くべき*ことができ、それを使って物事を理解することは不可能です。
- 「任意のタイプ」の定義が十分に緩い場合。使用するタイプが何であれ、
+=
演算子をサポートする必要があることに注意してください。サポートしないと、コンパイラが怒鳴ります。 - このコードは、
unsigned int
やlong double
などのタイプでは、名前に空白が含まれているため、 壊れます。私はその問題の解決策をすぐには知りません、そして私はこの答えに十分な時間を費やしました。
一般的な関数”理解できない’を読んだことがありますか?たくさんのコードを書く代わりに、それを投稿すると役に立ちます。
パラメトリック多型 。恒等関数はこのようにジェネリックです。