【问题标题】:iterate list creation from IO Int, How to?从 IO Int 迭代列表创建,如何?
【发布时间】:2013-01-21 19:35:09
【问题描述】:

我正在处理 python 挑战中的linkedlist 问题,需要查询下一个值(猜猜它是 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]

我认为它应该是迭代的单子提升版本,并从 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]

【问题讨论】:

  • 你怎么知道什么时候找到答案?您在寻找特定的价值吗?
  • @sabauma 我不知道,但我想是的。这就像一个益智游戏。你可以从第一个任务开始here

标签: list haskell io loops


【解决方案1】:

如果您希望您的函数提前终止,而不是返回一个 无限的结果列表,您将希望使用 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 包中找到,但是 最新版本继续重用原始种子,而不是由 生成器功能(我相信这已经修复但没有上传到 黑客攻击)。这是您想要的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

【讨论】:

  • 谢谢,我试试看能不能采纳。我不知道这个谜题的终止条件stop 可能没有任何意义。我拥有的唯一信息是我必须继续请求上次返回的值形式。我认为理想的解决方案应该是惰性 IO,即使它是无限列表,我也可以看到从 putStr 弹出的每个返回值。
  • @wizzup Lazy IO 通常被认为是错误的做法。这很诱人,但打开了一罐蠕虫。我添加了一些关于如何使用 Pipes 执行此操作的代码,您可能会觉得这些代码很有用。
【解决方案2】:

所以你想要的基本上是

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

问题在于,这并不像人们预期的那样懒惰地工作:由于单子绑定是严格的,因此即使您只想评估有限数量的as,您也会陷入无限循环。

【讨论】:

  • 一元绑定不需要严格,iterateM 可以与Control.Monad.State.Lazy 和其他一些绑定一起使用。不过,对于iterateM 来说,这些太特殊了,一般都不会有用。
  • 不知道这一切是否正确。我可以得出结论,没有这样的惰性列表生成来模仿 IO monad 的 iterate 吗?只是好奇,如果 list 是一种 monad,为什么不是所有的 monad 都有这个特性?
  • 啊!我确实忘记了 IO 不仅是一个单子,而且它也不纯粹。所以这是有道理的,为什么它是不同的。
  • 其实IO 纯粹的,这不是问题。只是,它必须是严格的。
猜你喜欢
  • 1970-01-01
  • 2018-12-04
  • 2013-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-10
  • 2018-10-20
相关资源
最近更新 更多