2016-04-22 17 views
1

を反復処理するたびに、私は機能を持っていることを言う:この質問の目的のためにハスケル - 別の値を返す関数は、それが

doesAThing :: Int -> ChangeState 

それはdoesAThingを取る必要がある唯一のこと、何であるかChangeState特に重要ではありませんIntをパラメータとして使用し、その反復は無限に繰り返されます。

addNum :: [Int] -> Int 
addNum [n] = foldl (+) 0 ([n] ++ [n + 1]) 

とdoesAThingのためにそれを使用します。私は何をしたいか

は、このような機能を取るです。現在のところ、関数はうまく動作しますが、それは私がやりたいことをしません - 常に同じ番号を返します。そのアイデアは、itATを繰り返すたびにaddNumは以前の出力が何であってもaddNumのパラメータとしてそれを使用するということです。私。 :

最初の繰り返し:nが0に設定されているとします。addNumは1(0 + 0 + 1)を返し、doesAThingはそれを使用してChangeStateを変更します。

2回目の反復:addNumはパラメータとして1をとり、3(0 + 1 + 2)を返し、それを使用してChangeStateを変更します。

3回目の繰り返し:addNumはパラメータとして3をとり、7(0 + 3 + 4)を返し、7は、ChangeStateを変更するために7を使用します。

謝罪これは本当にnoobish質問であれば、自己教育自分Haskellは時々難しいことができます。

+2

次に、モナドについての章を読みます。 – arrowd

+1

「自分自身でHaskellを教えることは時折困難です」 - それは難しいことではありません。適切なチュートリアルを参照してください!あなたは[推奨書籍](http://stackoverflow.com/tags/haskell/info)をチェックしましたか? – leftaroundabout

答えて

1

ハスケルでは、正当な理由で、あなたが望むものは不可能です、以下を参照してください。

ただし、次の繰り返しで独自の出力を与えることで、関数を反復する方法があります。ここで

は一例です:

iterate (\c -> c + 2) 0 

これは無限リスト

[0,2,4,6,....] 

Haskellは純粋な言語であり、この関数は、引数、定数、他の機能をのみアクセスできることを意味を作成します何もありません。つまり、関数がアクセスできる隠れた状態はありません。したがって、同じ入力では、Haskell関数は常に同じ出力を計算します。

+0

より可能性が高い 'iterate(\ c - > 2 * c + 1)0' – wowofbob

+0

@wowofbobもしあなたが' [0,1,3,7,15,31,63、...] 'を望むなら、あなたは正しいです。 – Ingo

+0

説明したOPにちょうど近いです – wowofbob

2

何かを変更したい場合は、変更可能な状態が必要です。 2つのオプションがあります。まず、現在の状態を関数の引数の1つとし、新しい状態を結果の一部にすることができます。 addNum :: [Int] -> ([Int], Int)のように。 Stateモナドがそれを助け、実際にはいくつかの可変状態があるという錯覚を与えます。

第2に、IORefにあなたの状態を保存して、addNum :: IORef [Int] -> IO IntのようにIOのモナドを使用するようにすることができます。そうすれば、実際にIORefの状態を変更することができます。変更可能な状態がローカルでのみ使用される場合は、STのような同じものを許可するモナドがあります。STMは、アプリケーションの並行性が高い場合に役立ちます。

最初のオプションを強くお勧めします。IO(またはその他の不可欠なもの)を絶対に必要とするまで使用しないでください。

1

ハスケルに慣れているプログラマがモナドになる可能性が高い状況に遭遇しました。特に、状態モナドは、そのためのいくつかの合理的なチュートリアルがオンラインにあります

は、私はかなり "addNum" の動作を理解していませんでしたあなたは説明しようとしています、そして私はそれを定義しようとするあなたの試みにいくつかの欠陥があると思います。たとえば、コードでは常に1つの要素のリストが必要であるということは、リストまたはfoldlがないことを示唆しています。引数としてnを追加して追加します。

しかし、あなたの説明から、私は次のようにできるだけ近づけると思います。 (これはモナドと状態モナドを少し勉強しなくては理解できないだろうが、うまくいけば、それは他の材料と一緒にで動作するようにあなたに例を示します。)

import Control.Monad.State 

-- Add the previous `addNum` result to the argument. 
addNum :: Int -> State Int Int 
addNum n = do 
    -- Precondition: the previous call to `addNum` used `put` 
    -- (a few lines below here) to record its result as the 
    -- implicit state for the `State` monad. 
    previous <- get 
    let newResult = previous + n 

    -- Here we fulfill the precondition above. 
    put newResult 
    return newResult 

Stateモナドの考えはということですgetputのアクションがあり、getを実行すると、最後に引数として与えられた値がputに取得されます。 addNumを実際に使用するには、evalState :: State s a -> s -> aのような関数の呼び出しのコンテキスト内で行う必要があります。最初のgetが最初に使用する最初の状態をaddNumで指定する必要があります。

たとえば、traverse関数を使用して、リスト[1..10]の連続した要素の呼び出しをaddNumに連鎖させます。各リスト要素のaddNumへの各呼び出しは、getnewResultの値は、前の要素の呼び出しによってputになります。これはHaskellは最初にどのように感じているのだ良くも悪くも、よく、圧倒的な感じ場合

>>> evalState (traverse addNum [1..10]) 0 
[1,3,6,10,15,21,28,36,45,55] 

:そしてevalStateから0引数は、非常に最初のaddNumコールgetが0だということを意味します。それを維持し、このような例をゆっくりと構築してください。

関連する問題