1

次の(疑似)コードが与えられた場合:

typedef std::map<const unsigned int, unsigned long int> ModelVector;
typedef std::vector<unsigned long int> EncryptedVector;

int test1 (const EncryptedVector &x)
{
    //compute ModelVector y
    data = kenel1(x, y);
    //compute output
}
int test2 (const EncryptedVector &xx)
{
    //compute ModelVector y
    data = kenel2(xx, y);
    //compute output
}
int test3 (const EncryptedVector &x, const EncryptedVector &xx)
{
    //compute ModelVector y
    data = kenel3(x, xx, y);
    //compute output
}
int test4 (const EncryptedVector &x, const EncryptedVector &xSquared)
{
    //compute ModelVector y
    data = kenel4(x, xSquared, y);
    //compute output
}

変数 y と output は 4 つの関数すべてで同じように計算され、switch ステートメントを介して適切なカーネル関数を選択できる「グローバル」オブジェクトがあるため、それらを記述するよりエレガントな方法があるかどうか疑問に思っていました。できればどういうわけかそれらをマージします...

たとえば、次のようなもの (疑似コード) が適しています。

int test (const EncryptedVector &x, const EncryptedVector &xx, const EncryptedVector &xSquared)
{
    //compute ModelVector y
    //switch (kernel) -> select the appropriate one
    //compute output
}

test (x, NULL, NULL);//test1
test (NULL, xx, NULL);//test2
test (x, xx, NULL);//test3
test (x, NULL, xSquared);//test4

または、さらに良いことに、さまざまなパラメーターの組み合わせで test を複数回宣言し、すべて上記のものにフォールバックすることもできます (x と xx のセマンティックな区別は失われますが)。

上記のアプローチの問題は、C++ では std::vector の代わりに NULL を渡すことが許可されていないことです。変数を参照渡しする代わりにポインターで渡し始めるよりも、コードを 4 回繰り返す方がよいと思います。 ..

これを行う他の方法はありますか?


編集: カーネル関数のプロトタイプは次のとおりです。

int kernel1 (const EncryptedVector &x, const ModelVector &y);
int kernel2 (const EncryptedVector &xx, const ModelVector &y);
int kernel3 (const EncryptedVector &x, const EncryptedVector &xx, const ModelVector &y);
int kernel4 (const EncryptedVector &x, const EncryptedVector &xSquared, const ModelVector &y);
4

2 に答える 2

4

元の 4 つのメソッドは、渡された引数の型ではなく、渡された引数のセマンティクスに基づいてtest、異なる動作 (たとえば、別の関数が呼び出される) を選択します。言うまでもなく、それらはまったく異なる名前を持っています。kernel

このパラメータ化をセマンティックなものから型のものに何らかの方法で変更できる場合は、4 つのメソッドすべてを 1 つに結合する提案された方法を使用できます。

これは、3 つのパラメーターすべてを持つ 1 つの関数を持つことを意味します。これは、まさにあなたが提案したものです。

int test (const EncryptedVector &x, const EncryptedVector &xx, const EncryptedVector &xSquared)

NULL...もちろん問題は、参照渡しができないことです。実際に渡す必要がありますvector。次のように、空の一時的なベクトルを渡すことができます。

test (x, EncryptedVector(), EncryptedVector());  //test1

empty()...そして、渡されたの状態に基づいて内部的にメソッドを選択しますvector:

int test (const EncryptedVector &x, const EncryptedVector &xx, const EncryptedVector &xSquared)
{
    //compute ModelVector y
    //switch (kernel) -> select the appropriate one
      if( xx.empty() && xSquared.empty() )
      {
        // kenel1 method
      }
      else if( x.empty() && xSquared.empty() )
      {
        // kenel2 method
      }
      else if( ... etc ... )

    //compute output
}

そして、おそらくあなたの用途にはこれで十分ですが、上記は少なくともいくつかの新しい問題を引き起こします.

1 つは一連のifステートメントです。これにより、実行時間の要因と、おそらくより重要な保守性の面の両方で複雑さが生じます。if4ページの声明を維持したくないことはわかっています。

その他の問題には、空の s を使用したメソッドの不器用な呼び出し、および sの無効な組み合わせでvector呼び出すことができるという事実が含まれます。testvector

したがって、セマンティックなものではなく、タイプベースの動作の選択を使用することに戻ります。test()のパラメータに具象型を指定せず、template代わりに a を指定した場合はどうなるでしょうか。

template<class VectorX, class VectorXX, class VectorXSquared>
int test(const VectorX& x, const VectorXX& xx, const VectorXSquared& xSquared);

NullVector特別なタイプも提供できるようになりました。

class NullVector {}; // Empty Class

...そして、test有効なユースケースごとに関数を明示的に特化します(上記では、4つの有効なユースケースをリストしました)。

nowの呼び出しは次のtestようになります。

test(x,NullVector(),NullVector());  // First Use Case

これには追加の利点もあります。の非特殊化バージョンの実装を提供しない場合、無効なパラメータtestで呼び出しtestを試みると、コンパイルおよびリンクに失敗します。たとえば、リストされているユースケースでは、いずれも 3 つの有効なEncryptedVectorオブジェクトを使用しないため、コンパイルに失敗するはずです。

test(x, xx, xSquared);

...実際、専門化されていない実装を提供しない場合はそうなりますtest

OK、それは多くの話であり、すべてが十分に説明されていない可能性があります. したがって、ここに私が話していることを説明するのに役立つことを願っている完全なサンプルがあります:

#include <vector>
#include <string>
using namespace std;

typedef vector<string> EncryptedVector;

class NullVector{}; // Empty Class

int kernel1(const EncryptedVector& x)
{
    return 1;
}

int kernel2(const EncryptedVector& xx)
{
    return 2;
}

int kernel3(const EncryptedVector& x, const EncryptedVector& xx)
{
    return 3;
}

int kernel4(const EncryptedVector& x, const EncryptedVector& xSquared)
{
    return 4;
}

template<class VectorX, class VectorXX, class VectorXSquared> 
int test(const VectorX& x, const VectorXX& xx, const VectorXSquared& xSquared);

template<> int test<>(const EncryptedVector& x, const NullVector&, const NullVector&)
{
    return kernel1(x);
}

template<> int test<>(const NullVector&, const EncryptedVector& xx, const NullVector&)
{
    return kernel2(xx);
}

template<> int test<>(const EncryptedVector& x, const EncryptedVector& xx, const NullVector&)
{
    return kernel3(x, xx);
}

template<> int test<>(const EncryptedVector &x, const NullVector&, const EncryptedVector &xSquared)
{
    return kernel4(x,xSquared);
}

int main()
{
    EncryptedVector x, xx, xSquared;  // each somehow populated

    ///*** VALID USE-CASES ***///
    test(x,NullVector(),NullVector());
    test(NullVector(),xx,NullVector());
    test(x,xx,NullVector());
    test(x,NullVector(),xSquared);

    ///*** INVALID USE CASES WILL FAIL TO COMPILE&LINK ***///
    test(x,xx,xSquared);

}
于 2012-06-12T16:29:28.157 に答える
0

追加のパラメーターを含み、カーネル (kenel?) を計算する関数を持つオブジェクト (私が間違っていなければ、ファンクターと呼ばれます) を送信できます。

// Main function, looks more generic now
template <class F>
int test1234(F functor)
{
    //compute ModelVector y
    data = functor(y);
    //compute output
}

...

// Functors involve some tedious coding, but at least your main calculation is clean
struct functor_for_kernel1
{
    const EncryptedVector &x;
    functor_for_kernel1(const EncryptedVector &x): x(x) {}
    int operator(const ModelVector &y) {return kernel1(x, y);
};

struct functor_for_kernel2 // easy
...

struct functor_for_kernel3
{
    const EncryptedVector &x, const EncryptedVector &xx;
    functor_for_kernel3(const EncryptedVector &x, const EncryptedVector &xx): x(x), xx(xx) {}
    int operator()(const ModelVector &y) {return kernel3(x, xx, y);
};

struct functor_for_kernel4 // easy
...

// Usage of the unified function
test1234(functor_for_kernel1(x));
test1234(functor_for_kernel2(xx));
test1234(functor_for_kernel3(x, xx));
test1234(functor_for_kernel4(x, xx));
于 2012-06-12T16:38:38.287 に答える