web-dev-qa-db-ja.com

配列次元の入力/出力への期待と可変次元入力配列のサポートを実装するためのアプローチ

前文

カラーAPIをベクトル化しているため、ほとんどの関数で受け入れられる入力タイプを変更しています。既存のコードとの下位互換性を維持することに熱心です。

次の入力変数を定義しましょう。

数値

R

形:()

たとえば、RGBトリプレットのピクセルR値。

1d

[R, G, B]

形:(3)

たとえば、ピクセルRGB値はトリプレットです。

2d

[[R, G, B],
 [R, G, B],
 [R, G, B],
 [R, G, B],
 [R, G, B],
 [R, G, B]]

形:(6, 3)

たとえば、RGB値の行はトリプレットです。

d

[[[R, G, B],
  [R, G, B],
  [R, G, B]],

 [[R, G, B],
  [R, G, B],
  [R, G, B]]]

形:(2, 3, 3)

たとえば、RGB値のトリプレットの画像。

期待

以前はnumericを入力として受け入れていた関数が、numericまたは1d配列を受け入れるようになりました。以前は1d配列を入力として受け入れていた関数が、1d2d、またはd配列を受け入れるようになりました。

現在、コードベースのベクトル化のほとんどはプロトタイプ化されています。具体的には、1d2dまたはd配列をサポートするために、元々1d配列を入力として受け取る関数を使用します。入力配列を2d 1に変換します。内部アルゴリズムは2d配列で機能するようになったため、それらの実装は簡単で予測可能です。

特定の関数について、アルゴリズムの目的で入力配列の次元を変更すると、結果の出力配列の次元が変更されます(何もしなければ)。

私たちは以下を知りたいです:

配列の次元を維持しようとしているのは一般的に予想される動作ですか?

logicalと思われるパスは、入力配列の次元を維持することです。たとえば、誰かが画像を(d形状で)入力するとします。私たちのRGB_to_XYZ関数は、おそらくd形状の画像を取得することを期待し、同じ関数に1d RGBピクセルを入力すると、おそらく1d)を取得することを期待します。 XYZピクセルであり、2d配列ではありません。

alternativeパスは、各関数の予想される入力/出力配列の次元を強制することです(これにより下位互換性が失われます)。

実装

alternativeパスは、その実装に問題はありません。

ただし、logicalパスは注意が必要です。この点でコードのプロトタイプを作成したため、各関数の開始と終了に、取得するための冗長なボイラープレートコードが多数あります。入力配列の次元、その次元を変更し、出力配列の形状を変更します。これはあまりエレガントではなく、コードを読んでいる人にとっては混乱を招きますが、保守性はさらに難しくなります。

「論理的な」パス動作を実装するためのエレガントな方法はありますか?

すべてのディメンションの処理/ウィザードリィの再形成を担当できるラッパー関数(Pythonを使用しているデコレーター)について考えています。関数自体は、のように入力/出力に対して固定の期待を持つことができます。 )代替パス。

2
Kel Solaar

配列の寸法を維持しようとすると、一般的に予想される動作になりますか?

私には、これは「私のAPIが内部でこれらすべての型変換を行うことを期待するだろうか」と少し聞こえます。答えは「おそらくそうですが、実装について心配する必要がないので、どちらの方法でも気にする必要はありません」です。たぶん、Rコンポーネントしかない色をフロブニケーションする超最適化された方法があり、その「古い」オーバーロードがそれを呼び出す必要があります。

「古いタイプをすべてサポートすることで、下位互換性を維持することを期待できますか?」と質問しようとしている場合。次に答えは完全にユーザーベースに依存し、私たちはそれらについて何も知りません。たとえば、歴史的に Microsoft Windowsはバグを安全に修正することすらできません 、ましてや引数の種類を変更することはほとんどありません。一方、 Python3は最も基本的なI/Oプリミティブの1つを「壊した」 、それは問題なく機能したようです。たぶん、あなたのユーザーは古いAPIからの「クリーンブレイク」を持っていることを感謝するでしょう。それはすべて異なります。

「論理的な」パス動作を実装するためのエレガントな方法はありますか?

C++での私のソリューションは、おそらく次のようになります。

_class ColorFrobnicator {
  public:
    int frobnicate(const int R) {
        std::vector<int> colors = { R, 0, 0 };
        return actuallyFrobnicate(colors);
    }
    int frobnicate(const std::vector<int>& colors) {
        return actuallyFrobnicate(colors);
    }

  private:
    int actuallyFrobnicate(const std::vector<int>& colors) {
        /* the code that actually matters goes here */
        return finalValueAfterExtensiveFrobnication;
    }
}
_

これは、下位互換性が得られるのとほぼ同じくらい「エレガント」だと思います。すべての変換とラッパーをインターフェイスに配置し、すべての「実際のコード」を実装に移動します。これにより、実際のコードをif(oldAPI) { convertToArray(R); }に移動する必要がなくなり、実際のコードに触れることなく、古いインターフェイスまたは新しいインターフェイスに簡単に変更を加えることができます。

私はPythonの経験はそれほど多くありませんが、その言語でこれを処理するための同様に口当たりの良い方法があると確信しています。

2
Ixrec