0

genEdges次の関数を末尾再帰関数に変換したかったのです。

genEdges :: Int -> Node -> IO [Edge]
genEdges n origin | n == 0 = return []
                  | otherwise =  do
                      edge <- genRandEdge origin
                      edges <- genEdges (n-1) (snd edge)
                      return $ edge : edges

つまり、最後の行は次のようになります

return $ edge : genEdges (n-1) (snd edge)

edgeとの型が異なることはわかっているgenEdges (n-1) (snd edge)ため、この例の行は正しくありません。

それは可能ですか?もしそうなら、機能はどうあるべきですか?そうでない場合、なぜですか?

4

3 に答える 3

1

IOまたは などの他のモナド/モナド変換子を使用することになった場合RandT(他のオプションは、ジェネレーターおよび/または事前に生成された無限リストを渡します)、以下は、状態モナド変換子を使用して のスレッド化を支援する例ですorigin:

import Control.Monad.State

data Node = Node
type Edge = ((), Node)

genRandEdge :: Node -> IO Edge
genRandEdge = undefined

genEdges :: Int -> Node -> IO [Edge]
genEdges n = evalStateT $ replicateM n $ do
        origin <- get
        edge <- liftIO $ genRandEdge origin
        put $ snd edge
        return edge

IO をより適切genRandEdgeに回避するために、より多くのコンテキストが必要です。生成アプローチをどのように改善できるかについて、別の質問をしてください。

于 2013-09-27T17:14:08.763 に答える
1

編集

彼が正しい Ingo の答えを見ると、おそらくジェネレーターを関数に渡す必要があり、純粋な関数を使用して次の乱数を取得し、それが生成する gen を次の再帰関数呼び出しに渡すことができます。

編集終了

このように、アキュムレータを持つヘルパー関数を作成できます。私はコンパイラを持っていないので、正確に正しいとは保証できません。

genEdges :: Int -> Node -> IO [Edge]
genEdges n o = genEdgesTR n o []
    where genEdgesRec n origin acc
          | n == 0  = return acc
          | otherwise   = do
            edge <- get RandEdge orgin
            genEdgesRec (n-1) (snd edge) (edge : acc)

これはおそらくfoldMまたは で記述できますが、それを理解するのはあなた次第です。

于 2013-09-27T14:31:01.423 に答える