Haskell:给出列表的下一个元素的函数

时间:2017-06-01 15:22:42

标签: haskell

我想知道在Haskell中是否有可能定义一个函数,该函数在调用时给出(无限)列表的下一个元素,例如:

Prelude> func
1
Prelude> func
2

是否有可能在Haskell中有这样的功能,如果有,你能给我一个例子吗?

3 个答案:

答案 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)

听起来您正在寻找其他语言的IteratorGenerator结构。如果是这样,这似乎是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)