在Haskell中如何重复执行IO命令?

11

我在 Haskell 中有一个名为 blabla 的函数,它接受一个整数并返回一个字符串:

blabla :: Int -> [Char]

我想在for循环中重复运行那个方法,类似于这样:

for i := 1 to 25 do begin
    write(i);
    write(": ");
    writeln(blabla(i));
end;

Haskell是否有for循环功能?


4
所以,您想要一个类型为[某些内容] -> IO()的函数,对吧?导入Control.Monad后,可以使用mapM_ print实现。 - AJF
4个回答

15

Control.Monad模块中有一个forM_函数,类似于命令式编程语言中的for循环:

import Control.Monad (forM_)

blabla :: Int -> String
blabla = show

main = forM_ [1..25] $ \i -> do
    putStr   (show i)
    putStr   ": "
    putStrLn (blabla i)

不过,我建议您远离这种命令式风格的代码。例如,同样的代码可以更简洁地编写:

import Control.Monad (mapM_)

blabla :: Int -> String
blabla = show

main = mapM_ (\i -> putStrLn $ show i ++ ": " ++ blabla i) [1..25]

希望这能有所帮助。


1
blabla =(:[]).("blabla" !!)的翻译是:blabla =(:[])。(“blabla”!!) - Mokosha
1
@Mokosha blabla x = blabla x - Aadit M Shah
1
我建议你远离这种命令式风格的代码。如果你提供一个更好的替代方案,我会支持你的答案。 - bheklilr
3
我会把这段代码改得更少命令式化:putStrLn $ unlines $ map (\x -> concat[show x, ": ", f x]) [1..25]。有时候需要逐行惰性地打印一个非常长甚至无限的字符串,但对于短字符串来说,一次构建整个字符串再打印通常更好。 - user2407038
@user2407038,你的版本也很懒惰,对于无限列表也可以完美工作。顺便说一下,putStrLnunlines都会添加最后一个换行符。 - Ørjan Johansen

7
喝了一杯茶后,我获得了最佳结果,我认为:
blablaResults = map (\x -> show x ++ ":" ++ blabla x) [0..25]

main = putStrLn $ unlines blablaResults

5
针对这个特定情况,我认为这是最佳方法,因为它明确分离了计算和输入输出。 - chi
是的,现在你正在以函数式风格思考。顺便说一下,你可以将它截断为一行 main = mapM_ (\x -> putStrLn $ show x ++ ": " ++ blabla x) [0..25] - Aadit M Shah
正如我之前对@user2407038提到的那样,putStrLnunlines都会添加一个最终的换行符。 - Ørjan Johansen

2

你还在以过程化的方式思考。尝试着用列表的方式来思考,而不是循环。

那么你想要的是这样一个列表:[1,f(1),2,f(2),3,f(3)...,n,f(n)]

这就是map的用武之地。如果你有一个函数f并想将其应用于列表[1,2,3...,n],你可以使用map f [1..n]

你需要的是一个函数,它接受一个数字i并将函数f应用于它,然后返回[i,f(i)]或者一个元组(i,f(i))。因此,你需要创建这个函数并将其映射到你的列表上。

当然,你还需要创建那个要操作的初始列表 - 就是那个从[1..n]开始的列表。

另一种方法是使用列表推导式:

forloop n = [(x,f x)|x <- [1..n] ]

(注:我现在没有Haskell编译器,所以我需要稍后验证最后一部分。我相信它应该按照提供的方式工作。)

看一下 putStrLn 的类型和你的列表中 items 的类型(应该有 25 个项)。 - zcleghern
putStrLn 使用 IO,这使得它更加复杂。但是你不必将其纳入映射中 - 你可以创建完整的字符串,然后稍后使用 putStrLn 输出它。 - S.L. Barth

0

我想,我自己搞定了:

blablaOutput :: [Int] -> IO()
blablaOutput (x:xs) = do 
  putStrLn (blabla x)
  blablaOutput xs
blablaOutput _ = putStrLn "That's all, folks!"  

main = blablaOutput [0..25]

虽然不是很符合函数式编程的要求,但它能够正常工作。


4
这肯定可行,使用显式递归没有问题。你会发现像上面那样的常规模式在标准库中有用武之地。例如main = do mapM_ (putStrLn . blabla) [0..25]; putStrLn "That's all, folks!"与上述代码是等价的。 - chi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接