2016-09-14 5 views
4

ハスケルのような機能指向言語では、パラメータ定義のいくつかの軸に関数定義をオーバーロードすることができます。 C++は、引数の数と型をサポートしています。階乗の定義は、引数は階乗の定義から0異なりですパラメータ値に基づいて関数シグネチャのマッチングを実行する方法はありますか?

factorial :: (Integral a) => a -> a 
factorial 0 = 1 
factorial n = n * factorial (n - 1) 

:他の言語には、例えば、引数の値とさえガード条項(。条件の引数をテストコード)Haskellでは、階乗の実装をサポート引数が他の整数の場合

私はC++でこの機能を見つけられず、最初はその言語で実装するのが難しいと考えました。私はそれが実際にはかなり簡単で言語に素敵なものになると思ったので、私はそれを見逃さなければなりません。

ネイティブ構文またはテンプレートでこれを行う方法はありますか?

+0

C++テンプレートは、整数定数に特化することができます。これは、Haskellの例とほぼ同じです。 [C++で特別なテンプレートで書かれた階乗関数です](http://stackoverflow.com/q/3082113/464709)。 –

+0

はい、コンパイル時にパラメータ値がわかっている場合にのみ、テンプレートのメタプログラミングで行うことができます。 C++では、仮想メソッドディスパッチ以外の実行時にパラメータ値をディスパッチできません。 – antlersoft

+0

これは2つのオーバーロードされた関数ではなく、 'factorial x = case x of 0 => 1の関数です。 n => n *階乗(n - 1) 'のようになります。 – molbdnilo

答えて

2

で行われなければならない私はここで本当の答えは、完全に同等ではありませんということだと思います。それでも。テンプレートの特殊化は近いですが、コンパイル時にのみ機能します。コンパイル時には、その使い勝手がいくつか制限されています。もちろん分岐はありますが、それは他の関数型プログラミング言語でパターンマッチングができるものに比べて電力が限られています。

C++でパターンマッチングのための現在の提案があります:P0095r1階乗の次の定義を可能にする概念を想定し、:

template <Integral I> 
I factorial(I n) { 
    return inspect(n) { 
     0 => 1 
     n => n * factorial(n-1) 
    }; 
} 

私は構文上全くわからないんだけど、その後、再び、それはですこれまでのやり方では、構文そのものが変わる可能性があります。

2

このようなことがあり、テンプレート特化と呼ばれています。基本的には、一般的なテンプレート定義とは別に、特定のタイプのテンプレートを定義することができます。

template<std::size_t N> 
struct factorial { 
    static constexpr unsigned long long value = N * factorial<N - 1>::value; 
}; 

template<> 
struct factorial<0> { 
    static constexpr unsigned long long value = 1; 
} 

auto foo = factorial<10>::value; 
:あなたは階乗テンプレート「機能」もテンプレートの特殊化を使用していますが、原因テンプレートの性質のために、それが唯一のコンパイル時の値( テンプレートメタプログラミング)を計算することができます

//Main template definition 
template<typename T> 
void foo(T) { std::cout << "Some T\n"; } 

//Specialization for int 
template<> 
void foo(int) { std::cout << "Called with an int!\n"; } 

hereそれについて読むことができます

私が知る限り、実行時には(スイッチ/ ifブランチを除いて)与えられた関数内にそのようなものはありません。

0

短い答え:いいえC++にはHaskellスタイルのパターンマッチングはありません。また、Haskellの例では、2つではなく1つの関数を持っていますが、入力の値をチェックするためのより良い構文があります。オーバーロードでは、実際には名前は同じですが、引数の数や型が異なる2つ以上の関数があります。

回答者:あなたが提案しているものに似ているものはtemplate-metaprogrammingで可能です。 C++テンプレートでは型だけでなく、テンプレート引数として値を使用できるので、実際にそのような関数を構築できます。テンプレート言語は明らかにチューリングが完了しているので、実際には計算可能なすべてを計算することができます。 もちろん、それはひどいように見え、大量のコンパイル時間と最初の書き込み後のコードを理解するのが難しくなります。

0

ランタイム分岐は、ifまたは3進演算子 <condition> ? <if-true> : <if-false>を使用して行われます。

コンパイル時にコンパイル時にオーバーロードが行われるため、値に基づいて関数のオーバーロードを選択する場合は、コンパイル時にその値を厳密に知っている必要があります。

ここsfinaeを用いて分岐コンパイル時間の例は次のとおり

ここ
template<int n, std::enable_if_t<(n > 1), short> = 0> 
constexpr int factorial(std::integral_constant<int, n>) { 
    return n * factorial(std::integral_constant<n - 1>{}); 
} 

template<int n, std::enable_if_t<(n == 0), short> = 0> 
constexpr int factorial(std::integral_constant<int, n>) { return 1; } 

enable_if_tに使用される条件に気づきます。条件が満たされない場合、関数を無効にしてコンパイラに代替関数を試すように強制します。

もちろん、構文はそれほど優れていません。最高のは、両方のランタイムのための単一の実装を持っていると時間をコンパイルするだろうが、そのためにあなたは、伝統的な分岐を使用する必要があります。

constexpr factorial(int n) { 
    return n == 0 ? 1 : n * factorial(n - 1); 
} 
1

値はコンパイル時に知られている場合、それはテンプレート

で行うことができます
//recursively calls itself until N is 1 
template<int N> 
struct factorial<N>{enum{value = N * factorial<N-1>::value};}; 

//at which point, this will be called (stopping the recursion) 
template<> 
struct factorial<1>{enum{value = 1};}; 

値は実行時にのみ知られている場合は、決定がランタイム

int factorial_recursion(int n){ 
    if(n == 1) 
    return 1; 
    else 
    return n * factorial_recursion(n - 1); 
} 
//or 
int factorial_loop(int n){ 
    int answer = 1; 
    for(int count = n; count > 1; --count) 
    answer *= count; 

    return answer; 
} 
0
int factorial(int n) 
{ 
    switch(n) 
    { 
     case 0: return 1; 
     default: return n * factorial(n - 1); 
    } 
} 
+0

私の考えは、コンパイラがこの「アンダーザカバー」を作成する/できた/できたということでした。 –

+0

@JasonDoege私はC++はすべてexplicitnessについてだと思います。コンパイラを人の背中の背後にあるトリックにすることは、言語の精神に反していると思います。私はハスケルのような人を知っていますが、私には構文が非常に謎めいていて、(この些細な事例以外の)非常に難しいのです。私はおそらくそれは私が心からの古いマシンコードハッカーだということを知らないでしょう:) –

関連する問題