sho*_*tor 2 io haskell functional-programming
我正在尝试使用IO创建一个无限增加1的简单计数器.
从那以后我一直在挠头.
理想情况下,我想做一些事情
tick = do putStr (counter)
counter + 1
where counter = 0
Run Code Online (Sandbox Code Playgroud)
然后重复这个过程.然后重复前两个表达式.或者类似的东西:
tick = tick'
where
counter = 1
tick' counter | counter > 0 = do putStrLn (show counter)
tick' (counter + 1)
| otherwise = tick
Run Code Online (Sandbox Code Playgroud)
这给了我错误:/
任何帮助表示赞赏:)
如果不使用可变细胞,有几种方法可以做到这一点.你已经在第二次尝试时做到了,只有一点点错误.您需要将初始值传递给tick'函数,而不是"设置它"(haskell不知道分配变量 - 只有定义.如果该行x = y出现,x将是y整个生命周期).
tick = tick' 0
where ...
Run Code Online (Sandbox Code Playgroud)
这counter = 0条线没有做任何事情; 它定义了一个从未使用过的名称.在counter在所使用的tick'功能被绑定为它的一个参数(和阴影定义为0的一个).花点时间盯着它,看看是否有意义.
有一个很好的"高阶"方式,我们也可以做到这一点.基本上我们想要运行无限长的代码块:
do
print 0
print 1
print 2
...
Run Code Online (Sandbox Code Playgroud)
有一个函数叫sequence :: [IO a] -> IO [a](见下面的警告),它将采取一系列动作并构建一个动作.因此,如果我们可以构造列表,[print 0, print 1, print 2, ...]那么我们可以将其传递sequence给构建我们正在寻找的无限长块.
请注意,这是Haskell中一个非常重要的概念:[print 0, print 1, print 2]不打印这三个数字然后构造列表[0,1,2].相反,它本身就是一个动作列表,其类型是[IO ()].使列表无效; 只有当你将一个动作绑定到main它才会执行它.例如,我们可能会说:
main = do
let xs = [putStrLn "hello", getLine >> putStrLn "world"]
xs !! 0
xs !! 0
xs !! 1
xs !! 1
xs !! 0
Run Code Online (Sandbox Code Playgroud)
这将两次打印hello,两次获得一行并world在每次打印后打印,然后再次打印hello.
有了这个概念,很容易[print 0, print 1, ...]用列表理解来构建动作列表:
main = sequence [ print x | x <- [0..] ]
Run Code Online (Sandbox Code Playgroud)
我们可以简化一下:
main = sequence (map (\x -> print x) [0..])
main = sequence (map print [0..])
Run Code Online (Sandbox Code Playgroud)
我们正在寻找map print [0..]的动作列表也是如此[print 0, print 1, ...],然后我们将sequence它们传递给它们在一起的链.
这种模式sequence很常见,并且有其独特之处mapM:
mapM :: (a -> IO b) -> [a] -> IO [b]
mapM f xs = sequence (map f xs)
Run Code Online (Sandbox Code Playgroud)
从而:
main = mapM print [0..]
Run Code Online (Sandbox Code Playgroud)
关于你想要的简单.
关于性能的一个注意事项:因为我们没有使用这些函数的输出,所以我们应该使用sequence_并且mapM_使用尾随下划线,这些是为此目的而优化的.通常这在Haskell程序中由于垃圾收集无关紧要,但在这个特定的用例中由于各种细微之处而是一种特殊情况.您会发现,如果没有_s,程序的内存使用量会随着结果列表(在本例中[(),(),(),...])构建但从未使用过而逐渐增长.
警告:我已经给出了类型签名sequence和mapM专门的IO,而不是一般的monad,因此读者不必同时了解具有类型和类型类的动作的正交概念.
| 归档时间: |
|
| 查看次数: |
2447 次 |
| 最近记录: |