7

次の値を照会する必要がある Python チャレンジで、リンクリストの問題で遊んでいます (Int だと思います)。

次の値を取得する関数を次のように作成します

url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=" 

getNext :: Int -> IO Int
getNext x = do
    rsp <- simpleHTTP (getRequest $ url ++ show x)
    bdy <- getResponseBody rsp
    let num = last $ splitWhen (==' ') bdy
    return (read num::Int)

そしてそれはうまくいきます(ghciで)

> getNext 12345
44827
> getNext 44827
45439

答えが見つかるまで繰り返し getNext を呼び出すと思いますが、何かが失敗した場合に最後の値から続行できるように、非モナドの世界でできるように履歴を保持する必要があると思います。

> let nX x = x + 3
> :t nX
nX :: Num a => a -> a
> take 10 $ iterate nX 1
[1,4,7,10,13,16,19,22,25,28]

それは iterate のモナドリフトバージョンであり、 Control.Monad.Loops から見つけられるべきだと思いますが、iterateM_期待どおりに機能しませんでした。何も表示されていません (_ サフィックスは結果を破棄することを意味すると思いますが、iterateMはありません)

> :t iterate
iterate :: (a -> a) -> a -> [a]
> :t iterateM_
iterateM_ :: Monad m => (a -> m a) -> a -> m b

質問は、非モナド反復のように [Int] を取得するにはどうすればよいかです。IO [Int]このようなコードでプルアウトしてフィルタリング/処理できるように戻る関数が必要だと思います

main = do
    i <- getAllList
    let answer = last i -- or could be a repeated converged value, don't know yet
    putStrLn (show answer)

getAllList :: IO [Int]
4

2 に答える 2

7

結果の無限のリストを返すのではなく、関数を早期に終了させたい場合は、unfoldrMではなく iterateM. これは、次のような方法で実行できます。

url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=" 


start = 12345
stop  = 10000

shouldStop :: Int -> Bool
shouldStop x = x == stop

getNext :: Int -> IO (Maybe (Int, Int))
getNext prev
    | shouldStop prev = return Nothing
    | otherwise       = do
        rsp <- simpleHTTP (getRequest $ url ++ show prev)
        bdy <- getResponseBody rsp
        let num = read $ last $ splitWhen (==' ') bdy :: Int
        print (prev, num)
        return $ Just (num, num)

getAllList :: IO [Int]
getAllList = unfoldrM getNext start

これにより、ループを終了できるように停止基準を定義できますが、終了基準が満たされるまで結果は返されません。

このunfoldrM関数はmonad-loopsパッケージに含まれていますが、最新バージョンでは、ジェネレーター関数によって生成されたものではなく、元のシードを再利用し続けています (これは修正されていますが、Hackage にはアップロードされていないと思います)。これは、unfoldrMあなたが望むバージョンです。

-- |See 'Data.List.unfoldr'.  This is a monad-friendly version of that.
unfoldrM :: (Monad m) => (a -> m (Maybe (b,a))) -> a -> m [b]
unfoldrM = unfoldrM'

-- |See 'Data.List.unfoldr'.  This is a monad-friendly version of that, with a
-- twist.  Rather than returning a list, it returns any MonadPlus type of your
-- choice.
unfoldrM' :: (Monad m, MonadPlus f) => (a -> m (Maybe (b,a))) -> a -> m (f b)
unfoldrM' f z = go z
    where go z = do
            x <- f z
            case x of
                Nothing         -> return mzero
                Just (x, z)     -> do
                        xs <- go z
                        return (return x `mplus` xs)

これはPipes、遅延 I/O に頼ることなく、結果のストリームとして処理を行うことができる を使用してこれを行う方法です。

import Network.HTTP
import Control.Monad
import Data.List.Split
import Control.Monad
import Control.Proxy

url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="

grabber :: (Proxy p) => Int -> () -> Producer p String IO ()
grabber start () = runIdentityP $ loop $ show start where
    loop x = do
        -- Grab the next value
        x' <- lift $ getNext x
        -- Send it down stream
        respond x'
        -- Keep grabbing
        loop x'

-- Just prints the values recieved from up stream
printer :: (Proxy p, Show a) => () -> Consumer p a IO r
printer () = runIdentityP $ forever $ do
    a <- request ()  -- Consume a value
    lift $ putStrLn $ "Received a value: " ++ show a

getNext :: String -> IO String
getNext prev = do
    rsp <- simpleHTTP (getRequest $ url ++ prev)
    bdy <- getResponseBody rsp
    let num  = last $ splitWhen (== ' ') bdy
    return num

main = runProxy $ grabber start >-> printer
于 2013-02-06T15:22:47.660 に答える
1

だからあなたが望むのは基本的に

iterateM :: Monad m => (a -> m a) -> a -> m [a]
iterateM action a = do
   a' <- action a
   liftM (a':) $ iterateM action a'

問題は、これが期待どおりに機能しないことです: モナド バインドは厳密であるため、有限数のas のみを評価したい場合でも、無限ループに陥ります。

于 2013-02-06T15:08:40.697 に答える