2017-05-28 3 views
1

私は実際の倍数(f64)を基礎とするタイプとして機能するRustプログラムを持っており、複雑な値(num::complex::Complex64)も処理できるようにシステムを拡張したいと考えています。f64またはComplex64の戻り値の型の処理。ジェネリック?どちらか?

A(例削減)関数は、いくつかのコンフィギュレーション構造体configを受け取り、その入力に依存するインデックスidx電位値を生成する:

fn potential(config: &Config, idx: &Index3) -> Result<f64, Error> { 
    let num = &config.grid.size; 
    match config.potential { 
     PotentialType::NoPotential => Ok(0.0), 
     PotentialType::Cube => { 
      if (idx.x > num.x/4 && idx.x <= 3 * num.x/4) && 
       (idx.y > num.y/4 && idx.y <= 3 * num.y/4) && 
       (idx.z > num.z/4 && idx.z <= 3 * num.z/4) { 
       Ok(-10.0) 
      } else { 
       Ok(0.0) 
      } 
     } 
     PotentialType::Coulomb => { 
      let r = config.grid.dn * (calculate_r2(idx, &config.grid)).sqrt(); 
      if r < config.grid.dn { 
       Ok(-1./config.grid.dn) 
      } else { 
       Ok(-1./r) 
      } 
     } 
    } 
} 

Complex64を返すComplexCoulomb一致を追加します値:

PotentialType::ComplexCoulomb => { 
    let r = config.grid.dn * (calculate_r2(idx, &config.grid)).sqrt(); 
    if r < config.grid.dn { 
     Ok(Complex64::new(-1./config.grid.dn, 1.)) 
    } else { 
     Ok(Complex64::new(-1./r, 1.)) 
    } 
} 

この関数は、プログラムの初期エントリポイントで、ndarray::Array3を埋め込みます。現在、私はタイプndarray::Array3<f64>の変数の数を操作しています - だから、この関数だけでなく、プログラム全体を一般化する必要があります。

configの入力に基づいて両方のタイプを使用するにはどうすればこのプログラムを拡張できますか?この構造体は、ディスク上の構成ファイルの解析に由来し、PotentialType::Complex*値の数と一致します。

私は2つの可能なオプションを認識していますが、いずれかの条件に合致するかどうかはわかりません。

  1. 使用Eitherに似たものと実数と複素数のためのRightためLeftを返します。追加ロジックを使用して、他の機能で値を個別に処理します。
  2. ジェネリックタイプを使用してください。これは私があまり前にやったことではないし、generalisation over many typesは私の現在のコードベースの複雑な変更の公正な塊のように思える。複雑さを減らす方法はありますか?

他にも提案がありましたら、聞いてみてください!

+2

この質問は、その「提案」の性質(私の意見では「あまりにも広い」境界線に当たっている)にとっては難しいことです。特に、コードに変換しようとする試みを示さずに、2つのオプションをリストしました。たとえ欲しかったとしても、「複雑さを減らす方法」を示すことはできませんでした。いくつかのコードを表示すると、質問の品質が向上する可能性があります。たとえ関数が 'Result '(ここで 'T'は' f64'、 'Complex64'、...)を返すとしても、実行時フィールドを持つことはできません'config'で' T'を選択します。最悪の場合、2つの関数インスタンスが必要になることがあります。 –

+0

_weは 'config'にランタイムフィールドを持つことができません' T'を選択... 2つの関数インスタンスが必要かもしれません_ < - これは実際には私の実際の答えかもしれません。他のオプションがまだ適合していれば、私の質問をすぐに編集して編集します。ありがとう! – Geodesic

答えて

4

多くのコード変更がありますが、汎用パラメータを使用するのがおそらく最も柔軟なアプローチであり、パフォーマンスには影響しません。 enumを渡すことは、列挙型が大きくなる(大型バリアントのサイズとそれらを区別するタグのサイズ)ことと、列挙型バリアントが頻繁にチェックされる必要があることがあるため、パフォーマンスが低下します。

厄介なものは、型パラメータを制約する潜在的に長いリストです。これは、反復を節約するために、各機能ではなく、implレベルで行うことができます。現在、これを人間工学に優れたものにする特性セットのエイリアスはありませんが、RFCが承認されています。

私は非常にsimilar change in the Euclid libraryを作った。それは1年以上前です。それ以来、Rustとその図書館の両方で多くの変更がありましたが、そのコミットをすばやく見ても、必要な変更の量を把握することができます。

これは同じ(改称)の実装のcurrent stateです:彼らは標準ライブラリに含まれていないとして、これらの形質の

impl <T, Src, Dst> TypedTransform3D<T, Src, Dst> 
where T: Copy + Clone + 
     Add<T, Output=T> + 
     Sub<T, Output=T> + 
     Mul<T, Output=T> + 
     Div<T, Output=T> + 
     Neg<Output=T> + 
     ApproxEq<T> + 
     PartialOrd + 
     Trig + 
     One + Zero { 

    // methods of TypedTransform3D defined here... 

} 

一部(TrigOneZero)は、実際にdefined inside the crateです。

+0

Peterに感謝します。あなたのコードを見て、MWEを同様の方法で取得しようとします。何かを確認したら、あなたの答えを正しいものとしてマークします。 – Geodesic

関連する問題