4

foo関数と言ってbar noo、私のプログラムの基本です。さらに、これらの関数はさまざまなシナリオ( foo1, bar1foo2, bar2など) でさまざまな方法で実装できますが、入力foo1foo2出力の型は同じです。いくつかの入力または構成によると、プログラムfoo1, bar1はあるシナリオでは を使用し、別のシナリオでは を使用しfoo2, bar2ます。

接尾辞 (1,2,3..) を に追加して、上記のように定義することもできますfoo, bar, noo。ただし、サフィックスが長くなる可能性があるため、これはきれいではありません。foo1with bar1(vs. )の特別なバインドも許可しませんbar2

別の方法は、各シナリオを個別の として扱うことModuleです。これfoo, bar, nooで、各ケースが適切にまとめられ、醜い接尾辞が回避されました。ただし、これにより、1 つのファイルがModule. このアプローチのもう 1 つの欠点は、Modulesいくつかの類似点 (たとえば 3 つの機能) を共有しているにもかかわらず、これらが完全に分離されていることです。

さまざまなシナリオのさまざまなs が同じ入力と出力を持っているため、typeclass解決策は高く評価されますが、私の頭には浮かびませんでした。foo

これらのアプローチの前述の欠点を回避するために、問題に対するHaskellのベストプラクティスがあるのではないかと思っています。

foo1 :: Double -> Double
bar1 :: Int -> Int
noo1 :: [Int] -> [Int]

foo2 :: Double -> Double
bar2 :: Int -> Int
noo2 :: [Int] -> [Int]

...

foo9 :: Double -> Double
bar9 :: Int -> Int
noo9 :: [Int] -> [Int]

編集:私がそれにどのようにアプローチするかを説明することは、議論に関連していると思いますJava Interface(いくつかの素晴らしいが概念レベルの議論は、Java interfaceこの投稿ここにあります)。多くの場合、複雑になる可能性がありますが、ここオーバーロードは実際には簡潔です。Haskell typeclassJava interface and class

interface Scenario {
  double     foo(double d);
  int        bar(int i);
  Array<int> noo(Array<int> a);
}

class UseScenario {
  void use(Scenario ss) {
    ss.foo(...);
    ss.bar(...);
    ss.noo(...);
  }
}

class S1 implements Scenario {
  double     foo(double d) {...};
  int        bar(int i) {...};
  Array<int> noo(Array<int> a) {...};
}

class S2 implements Scenario {
  double     foo(double d) {...};
  int        bar(int i) {...};
  Array<int> noo(Array<int> a) {...};
}
4

1 に答える 1

6

良い方法の 1 つは、すべての関数を 1 つのデータ型に入れることです。次に、異なる戦略ごとにそのタイプの異なる値を持ちます。最後に、デフォルトの戦略を選択し、実際の関数をデフォルトの戦略にリンクします (使いやすくするため)。例えば:

module MyModule where


data Strategy  = Strategy {
    fooWithStrategy :: Double -> Double
  , barWithStrategy :: Int -> Int
  , nooWithStrategy :: [Int] -> [Int]
  }

defaultStrategy :: Strategy
defaultStrategy = Strategy { 
    fooWithStrategy = (*2)
  , barWithStrategy = (+2)
  , nooWithStrategy = id
  }

foo :: Double -> Double
foo = fooWithStrategy defaultStrategy

bar :: Int -> Int
bar = barWithStrategy defaultStrategy

noo :: [Int] -> [Int]
noo = nooWithStrategy defaultStrategy

tripleStrategy :: Strategy
tripleStrategy = Strategy {
    fooWithStrategy = (*3)
  , barWithStrategy = (*3)
  , nooWithStrategy = \x -> x ++ x ++ x
  }

customAddStrategy :: Int -> Strategy
customAddStrategy n = Strategy {
    fooWithStrategy = (+ (fromIntegral n))
  , barWithStrategy = (+ n)
  , nooWithStrategy = (n :)
  }

これにより、多くの便利な機能が可能になります。

  1. カスタマイズ可能な戦略 (例: customAddStrategy)。戦略を組み合わせて一致させることもできます。newStrat = defaultStrategy { nooWithStrategy = nooWithStrategy tripleStrategy, fooWithStrategy = (*4) }
  2. ユーザーは実行時に戦略を切り替えることができます
  3. ライブラリを初めて使用するユーザーは、デフォルト ( foobarおよび) を使用できます。noo
  4. あなたや他のユーザーによるより多くの戦略で簡単に拡張できます。
于 2013-07-31T00:45:52.230 に答える