ステート トランスフォーマーを使用して、2D 再帰ウォークのすべてのポイントでデータセットをランダムにサンプリングします。これにより、一緒に条件を満たしたサンプルの 2D グリッドのリストが出力されます。結果から遅延して取得したいのですが、私のアプローチでは、最初の結果を取得する前に、すべてのポイントでデータセット全体を使い果たします。
具体的には、次のプログラムを検討してください。
import Control.Monad ( sequence, liftM2 )
import Data.Functor.Identity
import Control.Monad.State.Lazy ( StateT(..), State(..), runState )
walk :: Int -> Int -> [State Int [Int]]
walk _ 0 = [return [0]]
walk 0 _ = [return [0]]
walk x y =
let st :: [State Int Int]
st = [StateT (\s -> Identity (s, s + 1)), undefined]
unst :: [State Int Int] -- degenerate state tf
unst = [return 1, undefined]
in map (\m_z -> do
z <- m_z
fmap concat $ sequence [
liftM2 (zipWith (\x y -> x + y + z)) a b -- for 1D: map (+z) <$> a
| a <- walk x (y - 1) -- depth
, b <- walk (x - 1) y -- breadth -- comment out for 1D
]
) st -- vs. unst
main :: IO ()
main = do
std <- getStdGen
putStrLn $ show $ head $ fst $ (`runState` 0) $ head $ walk 2 2
(x, y)
プログラムは からまで長方形のグリッドをたどり、State モナドのリストの 1 つの値を含むすべての結果を合計します:状態を読み取って進める(0, 0)
非自明なトランスフォーマー、または自明なトランスフォーマーのいずれかです。興味深いのは、アルゴリズムがとの先頭を超えて探索するかどうかです。st
unst
st
unst
提示されたコードでは、 がスローされundefined
ます。これは、変換を連鎖させる順序の設計ミス、特に状態処理の問題であると考えましたunst
。しかし、1D 再帰は、状態変換器を使用しても遅延を維持することがわかりました (幅ステップを削除し、ブロックを にb <- walk...
交換します)。liftM2
fmap
の場合、トリガーする前にグリッド全体をウォークするtrace (show (x, y))
こともわかります。
$ cabal run
Build profile: -w ghc-8.6.5 -O1
...
(2,2)
(2,1)
(1,2)
(1,1)
(1,1)
sandbox: Prelude.undefined
ここでは私の使用に問題があると思いますが、モナドの選択とウォークの次元がその成功に影響するため、変換を行うことがそれ自体が厳密性の原因であるとはsequence
広く言えません。sequence
ここで 1D 再帰と 2D 再帰の厳密さの違いの原因は何ですか? また、どうすれば必要な遅延を実現できますか?