我想知道在Haskell中是否有可能定义一个函数,该函数在调用时给出(无限)列表的下一个元素,例如:
Prelude> func
1
Prelude> func
2
是否有可能在Haskell中有这样的功能,如果有,你能给我一个例子吗?
答案 0 :(得分:5)
你可以做State
这样的事情:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State
import Data.List
import Data.Maybe
-- This is not a function! The misleading name func comes from the question text.
func :: MonadState [a] m => m a
func = state (fromJust . uncons)
exampleUsage :: State [Int] (Int, Int)
exampleUsage = do
x <- func
y <- func
return (x, y)
您可以在ghci中尝试:
> evalState exampleUsage [1..]
(1, 2)
然而,在高层次上,我建议重新考虑您的要求。 func
根本不是惯用语;简单地直接使用无限列表通常会更清晰,具有更低的(语法)开销,并导致更好的生成代码。例如:
exampleUsage' :: [a] -> (a, a)
exampleUsage' (x:y:_) = (x,y)
N.B。这是两行代码,没有扩展或导入,与之前的11行代码相比,包括语言扩展和三个导入。用法也简化了;你可以完全放弃evalState
的电话并完成。
> exampleUsage' [1..]
(1, 2)
答案 1 :(得分:4)
您可以使用可变引用和IO
monad(或其他有状态monad)。通过部分应用,这可以做得相当漂亮:
Prelude> import Data.IORef
Prelude Data.IORef> ref <- newIORef 0
Prelude Data.IORef> let func = readIORef ref >>= \r -> writeIORef ref (r+1) >> return r
Prelude Data.IORef> func
0
Prelude Data.IORef> func
1
或者更接近您的要求:
Prelude Data.IORef> ref2 <- newIORef [0..]
Prelude Data.IORef> let func2 = readIORef ref2 >>= \(x:xs) -> writeIORef ref2 xs >> return x
Prelude Data.IORef> func2
0
Prelude Data.IORef> func2
1
答案 2 :(得分:2)
听起来您正在寻找其他语言的Iterator
或Generator
结构。如果是这样,这似乎是conduit库的一个很好的用例。请注意,有选项(例如pipes);但是,管道可能是一个很好的起点。
如果您尝试仅在列表上操作,使用State
Monad可能是一个更简单的答案(正如丹尼尔建议的那样);但是,如果您正在寻找更通用的解决方案,那么管道(或类似)可能确实是答案。
您正在搜索的func
很可能是await函数。
这是一个简单的例子 -
import Prelude
import Conduit
import Data.MonoTraversable
main :: IO ()
main = runConduit (source .| consume) >>= print
source :: Monad m => Producer m (Element [Integer])
source = yieldMany [0..]
consume :: Monad m => ConduitM i o m (Maybe (i, i))
consume = do
mx <- await
my <- await
return $ (,) <$> mx <*> my
及其输出 -
λ main
Just (0,1)