gaa*_*kam 8 recursion haskell sequence lazy-evaluation
main = do
input <- sequence [getLine, getLine, getLine]
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
让我们看看这个程序的实际效果:
m@m-X555LJ:~$ runhaskell wtf.hs
asdf
jkl
powe
"asdf"
"jkl"
"powe"
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,这里似乎没有懒惰。取而代之getLine
的是,急切地评估所有3个值,将读取的值存储在内存中,然后而不是在此之前将所有值打印出来。
比较一下:
main = do
input <- fmap lines getContents
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
让我们看一下实际情况:
m@m-X555LJ:~$ runhaskell wtf.hs
asdf
"asdf"
lkj
"lkj"
power
"power"
Run Code Online (Sandbox Code Playgroud)
完全不同的东西。行被一一读取并一一打印。这对我来说很奇怪,因为我看不到这两个程序之间有什么区别。
从LearnYouAHaskell:
当I / O操作所使用,
sequenceA
是同样的事情sequence
!它获取I / O操作的列表,并返回将执行这些操作中的每个操作的I / O操作,并以结果的形式列出这些I / O操作的结果。这是因为要将一个[IO a]
值转换为一个IO [a]
值,以使一个I / O操作在执行时产生结果列表,所以所有这些I / O操作都必须先进行排序,以便在评估时依次执行。被迫。如果不执行I / O操作,则无法获得结果。
我糊涂了。我不需要执行所有IO操作就可以得到一个结果。
该书前面几段显示了以下内容的定义sequence
:
Run Code Online (Sandbox Code Playgroud)sequenceA :: (Applicative f) => [f a] -> f [a] sequenceA [] = pure [] sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
很好的递归;这里没有什么暗示我这个递归不应该是懒惰的;就像在其他任何递归中一样,要使Haskell成为返回列表的头部,不必执行所有递归步骤!
比较:
rec :: Int -> [Int]
rec n = n:(rec (n+1))
main = print (head (rec 5))
Run Code Online (Sandbox Code Playgroud)
实际上:
m@m-X555LJ:~$ runhaskell wtf.hs
5
m@m-X555LJ:~$
Run Code Online (Sandbox Code Playgroud)
显然,这里的递归是懒惰地执行,而不是急切地执行。
那么为什么要sequence [getLine, getLine, getLine]
急于执行示例中的递归?
关于为何无论结果如何都必须按顺序运行IO操作很重要:想象一个操作
createFile :: IO ()
和writeToFile :: IO ()
。当我执行一个操作时,sequence [createFile, writeToFile]
我希望它们既完成又井井有条,即使我根本不在乎它们的实际结果(都是非常无聊的价值()
)!
我不确定这如何适用于此问题。
也许我这样说我的Q ...
在我看来:
do
input <- sequence [getLine, getLine, getLine]
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
应该折腾成这样的东西:
do
input <- do
input <- concat ( map (fmap (:[])) [getLine, getLine, getLine] )
return input
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
反过来,应该反序列化为这样的东西(伪代码,对不起):
do
[ perform print on the result of getLine,
perform print on the result of getLine,
perform print on the result of getLine
] and discard the results of those prints since print was applied with mapM_ which discards the results unlike mapM
Run Code Online (Sandbox Code Playgroud)
getContents
很懒,getLine
不是。惰性IO本身并不是Haskell的功能,它是某些特定IO操作的功能。
我糊涂了。我不需要执行所有IO操作就可以得到一个结果。
是的你是!这是的最重要的功能之一IO
,如果您编写a >> b
或等效地,
do a
b
Run Code Online (Sandbox Code Playgroud)
那么您可以确定a
之前绝对是“运行” b
(请参见脚注)。getContents
实际上是相同的,它先于其后“运行”……但是它返回的结果是一个偷偷摸摸的结果,当您尝试对其进行评估时偷偷地做更多的 IO。这实际上是令人惊讶的,它在实践中可能会导致一些非常有趣的结果(例如,您正在读取文件的内容,而您正在处理的结果getContents
),因此在实际程序中,您可能不应该这样做不是使用它,它主要是为了方便起见,在您不关心此类事情的程序中(例如Code Golf,一次性脚本或教学)。
关于为何无论结果如何都必须按顺序运行IO操作很重要:想象一个操作createFile :: IO ()
和writeToFile :: IO ()
。当我执行一个操作时,sequence [createFile, writeToFile]
我希望它们既完成又井井有条,即使我根本不在乎它们的实际结果(都是非常无聊的价值()
)!
处理修改:
应该折腾成这样的东西:
Run Code Online (Sandbox Code Playgroud)do input <- do input <- concat ( map (fmap (:[])) [getLine, getLine, getLine] ) return input mapM_ print input
不,实际上变成了这样的东西:
do
input <- do
x <- getLine
y <- getLine
z <- getLine
return [x,y,z]
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
(的实际定义sequence
或多或少是这样的:
sequence [] = return []
sequence (a:as) = do
x <- a
fmap (x:) $ sequence as
Run Code Online (Sandbox Code Playgroud)