2017-06-01 61 views
0

我想知道如果它是在Haskell可能定義一個函數,它當打電話給一個(無限)列表的下一個元素的下一個元素,因此,例如:的Haskell:函數,給出了一個列表

Prelude> func 
1 
Prelude> func 
2 

是否有可能在Haskell中有這樣的功能,如果有的話,你能舉個例子嗎?

+3

通常純函數應始終返回給定相同的輸入相同的答案。所以你只能用monad來實現。 –

+0

我試着google'ing如何做monads,但我找不到太多。你有一個想法/如何做的例子? – Coozekoek

+9

這對我來說看起來不太好。用純函數式編程語言編程的重點是避免這種副作用,並實現參照透明。 – chi

回答

4

可以使用可變引用和IO單子(或其他有狀態的單子)。這可以通過製造局部應用相當漂亮:

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 
5

你可以做一個State FUL這樣的事:

{-# 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) 
+0

也可以使用'IORef'(或類似的)。如果你對這看起來感興趣,請告訴我。 –

+0

我同意,在大多數情況下,使用國家是很愚蠢的,而且問題的確表明這是其中的一種情況。但肯定有些情況下,與國家「職能」一起工作比直接處理無限清單更容易。例如,https://stackoverflow.com/q/41941740/625403是一個類似於這個問題,但在不同的上下文中,我的回答https://stackoverflow.com/a/41942347/625403使用狀態來跟蹤在無限列表中的位置。 – amalloy

2

這聽起來像你正在尋找的東西像其他語言IteratorGenerator結構。如果是這樣,這對於conduit庫來說似乎是一個很好的用例。請注意,有些選項(例如pipes);然而,管道可能是一個很好的起點。

如果您嘗試僅使用列表操作,則使用State Monad可能是一個更簡單的答案(如Daniel所示);然而,如果你正在尋找一個更一般的解決方案,管道(或類似)可能確實是答案。

您正在尋找的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) 
相關問題