私はハスケルの背後にある数学の基礎を読んでいます。関数の状態を保存するためにクロージャを使う方法について学びました。ハスケルでクロージャーはどのように機能しますか?
ハスケルがクロージャを許可するのかどうか、そして純粋な関数ではないので、どのように動作するのだろうと思いましたか?
関数がクローズオーバー状態を変更すると、同じ入力に異なる出力を与えることができます。
ハスケルでこれはどのように問題ありませんか?最初に値を割り当てた後に変数を再割り当てすることはできないからですか?
私はハスケルの背後にある数学の基礎を読んでいます。関数の状態を保存するためにクロージャを使う方法について学びました。ハスケルでクロージャーはどのように機能しますか?
ハスケルがクロージャを許可するのかどうか、そして純粋な関数ではないので、どのように動作するのだろうと思いましたか?
関数がクローズオーバー状態を変更すると、同じ入力に異なる出力を与えることができます。
ハスケルでこれはどのように問題ありませんか?最初に値を割り当てた後に変数を再割り当てすることはできないからですか?
クロージャは、関数に追加の変数を追加するだけなので、「通常の」変数よりも何もできることはありません。つまり、確かに状態を変更しないでください。
はもっと読む:あなたが実際にあなたが考えるかもしれない方法をHaskellでクロージャをシミュレートすることはできませんが、 Closures (in Haskell)
初心者の直感的な言葉でこの答えを受け入れていただきありがとうございます – nidoran
haskellは閉じられた変数を値または参照と見なしますか? – CMCDragonkai
純粋なハスケルの世界であると考えると、混乱を招く可能性があるので、まず「リファレンス」と言ったときの意味を具体的に説明しなければならないと思います。 – Bartosz
。
data Closure i o = Respond (i -> (o, Closure i o))
これは各「ステップ」のタイプo
の応答を計算するために使用されるタイプi
の値をとる型を定義:まず、私は、閉鎖型を定義します。
それでは、すなわち、整数で空の入力と回答を受け入れ、「閉鎖」を定義してみましょう:
incrementer :: Closure() Int
このクロージャの動作が要求にリクエストごとに異なります。私はそれをシンプルに保つ、それが最初の応答に0で応答して、それぞれの連続した要求に対するその応答をインクリメントするようにそれを作ってあげる:
incrementer = go 0 where
go n = Respond $ \() -> (n, go (n + 1))
私たちは、その後、繰り返しの結果が得閉鎖、およびAを照会することができます新しい閉鎖:
newtype State s a = State { runState :: s -> (a, s) }
:上記のタイプの後半はState
モナドであるHaskellでは一般的なパターンを、似ていること
query :: i -> Closure i o -> (o, Closure i o)
query i (Respond f) = f i
お知らせControl.Monad.State
からインポートできます。さんはそれを私たちの閉鎖を渡し、見てみましょう
someQuery :: State (Closure() Int) (Int, Int)
someQuery = do
n1 <- query()
n2 <- query()
return (n1, n2)
:
query :: i -> State (Closure i o) o
query i = state $ \(Respond f) -> f i
を...そして今、私たちはState
モナドを使って任意の閉鎖を照会するための汎用的な方法があります。だから我々はこのState
モナドでquery
をラップすることができます何が起こる:
>>> evalState someQuery incrementer
(0, 1)
はのは、いくつかの任意のパターンを返す別のクロージャを書いてみましょう:
weirdClosure :: Closure() Int
weirdClosure = Respond (\() -> (42, Respond (\() -> (666, weirdClosure))))
...それをテストしてください:
>>> evalState someQuery weirdClosure
(42, 666)
今、手でクローズを書くのはかなり面倒です。クロージャを書くのにdo
の表記法を使うことができればいいのではないでしょうか?まあ、できます! Closure i o
ため
data Closure i o r = Done r | Respond (i -> (o, Closure i o r))
は、今、私たちは(Control.Monad
から)Monad
インスタンスを定義することができます:私たちは私たちの閉鎖型への1つの変更をしなければならない
instance Monad (Closure i o) where
return = Done
(Done r) >>= f = f r
(Respond k) >>= f = Respond $ \i -> let (o, c) = k i in (o, c >>= f)
をそして、我々はに対応して便利な機能を書くことができます
:我々はすべて私たちの古いクロージャを書き換えるために使用することができますanswer :: (i -> o) -> Closure i o()
answer f = Respond $ \i -> (f i, Done())
...:単一の要求にサービスを提供
incrementer :: Closure() Int()
incrementer = forM_ [1..] $ \n -> answer (\() -> n)
weirdClosure :: Closure() Int r
weirdClosure = forever $ do
answer (\() -> 42)
answer (\() -> 666)
今、私たちは私たちに問い合わせ機能を変更します。
query :: i -> StateT (Closure i o r) (Either r) o
query i = StateT $ \x -> case x of
Respond f -> Right (f i)
Done r -> Left r
...クエリを記述するためにそれを使用します。
someQuery :: StateT (Closure() Int()) (Either()) (Int, Int)
someQuery = do
n1 <- query()
n2 <- query()
return (n1, n2)
は今それをテスト!
>>> evalStateT someQuery incrementer
Right (1, 2)
>>> evalStateT someQuery weirdClosure
Right (42, 666)
>>> evalStateT someQuery (return())
Left()
しかし、私はまだ本当にエレガントなアプローチであることは考えていないので、私は臆面もなく書き込み閉鎖との一般的な多くの、より構造化された方法として私pipes
に私のProxyタイプを差し込んで締結するつもりだ彼ら消費者。 Server
タイプは一般化されたクロージャを表し、Client
はクロージャの一般化された消費者を表す。
お返事ありがとうございますが、これのほとんどは今のところ私の頭の上です。私がもっと学ぶときこれに戻ります – nidoran
他人が言っていたように、Haskellはクロージャの "状態"を変更することを許可していません。これにより、機能の純度を損なう可能性のあることを行うことができなくなります。
それは、関数によって閉じられた変数を宣言し、その関数の外でその変数を後で修正してその関数を実行すると、その関数は私の突然変異を無視します私は閉じた変数を変更しなかったときと同じ出力を返すのですか? – CMCDragonkai
Haskellでは、後で変数を "変更"することはできません。 (またはこれまで。) – MathematicalOrchid
単純に、閉鎖状態やその他の変更はできません。 – is7s
類似の質問:http://stackoverflow.com/questions/9419175/are-closures-a-violation-of-the-functional- programming-paradigm/ – amindfv
@ is7sですが、アトミックでないデータの場合は、コール間でさらにインスタンス化される可能性があります。 –