1

チュートリアルを続けると、セクションA more complex side effect: Random Numbersで次のようになります。

bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (StdGen → (b,StdGen))

「ランダム化された関数」(著者が呼んでいるように)のタイプが次の場合:

a → StdGen -> (b,StdGen)

さらに、バインドは次のように定義されます。

bind f x seed = let (x',seed') = x seed in f x' seed'

質問StdGen: バインドの署名の最後に余分な文字があるのはなぜですか? そうではありませんか:

bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (b,StdGen)

私の推論は次のようになります。

  1. Bind は関数f:: a -> StdGen -> (b,StdGen)と「出力」を取りますStdGen -> (a,StdGen)
  2. をandに適用し、fの署名が示すものは何でも返します- これはちょうど:aStdGenf(b, StdGen)

    f::a -> StdGen -> (b,StdGen)
    
  3. 次の bind 実装でfも、 valuex'seed'typeの両方に適用されるStdGenため、結果はタプルでなければなりません!

     bind f x seed = let (x',seed') = x seed in f x' seed'
    

どこかで私が間違っていましたか?どんな助けでも大歓迎です!

注: 将来の読者のために、著者の の定義はbind、引数が反転していることを除いて、標準の定義と同等です。flip . >>=

4

1 に答える 1

3

あなたのタイプを見てみましょう:

bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (b,StdGen)

さて、私はあなたの最初のポイントに 100% 同意します。

Bind は関数f :: a -> StdGen -> (b,StdGen)と「出力」を取りますStdGen -> (a,StdGen)

しかし、あなたの2番目は私を心配しています:

とに を適用しfます。aStdGen

型の値はどこaから取得しましたか? 型の値はどこStdGenから取得しましたか?

これらの両方の質問に対する答えは、「嘘をついているわけではありません」です。ただし、嘘をついているのでStdGen -> (a,StdGen)、パラメーターがもう 1 つあれば両方を取得できStdGenます。そして、それが追加のパラメーターの由来です。

さて、少し高度な説明です。問題の一部 (私が思うに) は、これらの型シグネチャが少し雑然としていて快適に読めないことです。いくつかの抽象化が必要です。ここでモデル化しようとしているのは確率分布であり、サンプリング関数としてモデル化しています。aしたがって、分布オーバーは、分布からサンプリングして返す方法を知っている関数であると言えますa

type Dist a = StdGen -> (a, StdGen)

さて、すべての分布がこれほどフラットというわけではありません。たとえば、ベルヌーイ分布は a のようなものですがDist Bool、 を選択する確率についてもパラメーター化されていますFalse。その型を次のように書くことができます。

bernoulli :: Double -> Dist Bool

したがって、パラメータ化された分布を、分布を返す関数としてモデル化できます。同様に、分布をパラメータ化された分布として返す関数を考えることができます。

この高レベルの解釈を念頭に置くと、 の型がbindより読みやすくなります。

bind :: (a -> Dist b) -> (Dist a -> Dist b)

これは、最初に分布からサンプリングし、次に分布からサンプリングするときにそれをパラメーターとして使用するbind方法を指示する関数であると言います。それだけでなく、この型エイリアスを使用すると、「余分な」引数を持たない型を書くことはほとんど考えられなくなります。aabbind

于 2012-07-26T17:53:38.173 に答える