Haskell 在do块内迭代
我写了这段代码:Haskell 在do块内迭代,haskell,monads,Haskell,Monads,我写了这段代码: toCouplesFile = do inputFile <- openFile "deletedId.csv" ReadMode outputFile <- openFile "couples.txt" WriteMode readAndChange inputFile outputFile readAndChange i o = do iseof <- hIsEOF i
toCouplesFile = do inputFile <- openFile "deletedId.csv" ReadMode
outputFile <- openFile "couples.txt" WriteMode
readAndChange inputFile outputFile
readAndChange i o = do iseof <- hIsEOF i
if iseof then (return o)
else do line <- hGetLine i
hPutStrLn o (show (extractNameAndId line))
readAndChange i o
import-Control.Monad
进口管制.Monad.Trans
进口控制。单子。反式
读取并更改IO=do
结果您可以使用let
模式进行递归,但这类似于单独定义递归函数:
main = do
let x = 10
let loop = do
print 1
when (x<20) loop
loop
你所说的是一种goto标签
模式。我不知道您是否可以实现这种行为,但上面使用的fix
或let
可以轻松帮助您实现递归
[编辑]
还有一些模式可以实现类似的结果,比如使用Cont
monad,如中所示
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
main = (`runContT` return) $ do
(x, loopBack) <- getCC' 0
lift (print x)
when (x < 10) (loopBack (x + 1))
lift (putStrLn "finish")
getCC'::MonadCont m=>a->m(a,a->mb)
getCC'x0=callCC(\c->让fx=c(x,f)作为回报(x0,f))
main=(`runContT`return)$do
(x,loopBack)你以一种不必要的方式编程,让生活变得困难。您正在使用漂亮的Haskell语言编程,您正在寻找一个goto
构造
为什么不直接导入Control.Applicative()
并编写
readAndChange' = writeFile "couples.txt" =<<
unlines.map (show.extractNameAndId).lines <$> readFile "deletedId.csv"
最后一个问题是我们如何在readAndChange
中使用
。如果数据量很大
deletedId.csv您将错过hello,但您当然可以这样做
greet <$> readFile "deletedId.csv" >>= writeFile "hi.txt"
take 4.lines <$> readFile "hi.txt"
试一试
侮辱“弗雷德”“巴尼”
侮辱“弗雷德”$“巴尼”
侮辱[“弗雷德”、“巴尼”][“威尔玛”、“贝蒂”]
侮辱只是“Fred”没什么
侮辱只是“弗雷德”只是“威尔玛”
侮辱readFile“someone.txt”readFile“someone.txt”
这里在函数后面使用
,在所需的参数之间使用
。它的工作原理一开始有点令人兴奋,但它是编写有效计算的最实用的风格
接下来请阅读有关应用函子的内容。他们很棒。
与命令式编程语言不同,也与其他函数式编程语言不同,Haskell不包含用于编写循环或while循环的语法结构,这正是您在这里要求的
其思想是递归过程和迭代过程可以统一地被递归函数捕获。只是迭代过程被捕获为特定类型的递归函数:这些函数是尾部递归的。命令式代码(如do块中出现的代码)也不例外。您可能会发现这种缺乏显式循环构造的做法很烦人,因为您必须为每个循环定义一个新函数,因此在某种意义上必须命名该循环。然而,这对于Haskell方法的统一性和简单性来说是一个微不足道的代价,原因有三:
您不必定义表示顶级循环的函数。您可以在本地定义它
在Haskell中,许多人通常对这类循环使用相同的名称。这里流行的选择是go
和aux
。因此,您的代码可以重写如下:
toCouplesFile = do inputFile <- openFile "deletedId.csv" ReadMode
outputFile <- openFile "couples.txt" WriteMode
let go = do
iseof <- hIsEOF inputFile
if iseof then (return outputFile)
else do line <- hGetLine inputFile
hPutStrLn outputFile (show (extractNameAndId line))
go
go
toCouplesFile=do inputFile您应该做的第一件事是阅读模块的文档,这对于编写Haskell代码是绝对必要的。当你这么做的时候,安装Hackage的软件包,并阅读上面的文档;您可能特别感兴趣的是whileM\u
函数:
import Data.Functor ((<$>)
import Control.Monad.Loops (whileM_)
readAndChange i o =
whileM_ notEOF $ do line <- hGetLine i
hPutStrLn o (show (extractNameAndId line))
where notEOF = not <$> (hIsEOF i)
尽管如此,我还是同意你写这篇文章的方式过于迫切。试着这样想:您的程序基本上是将输入字符串转换为输出字符串。这立即表明程序逻辑的核心应具有以下类型:
transformInput :: String -> String
transformInput = ...
您的转换是逐行进行的。这意味着您可以通过这种方式优化草图(函数lines
将字符串拆分为多行;取消线
重新加入列表):
现在,您已经在transformInput
函数中找到了逻辑的核心,因此您只需要以某种方式将其连接到输入和输出句柄。如果您正在处理stdin和stdout,则可以使用该函数来完成此操作。但我们实际上可以窃取它的实现并修改它:
hInteract :: Handle -> Handle -> (String -> String) -> IO ()
hInteract i o f = do s <- hGetContents i
hPutStr o (f s)
hinterract::Handle->Handle->(字符串->字符串)->IO()
hinterract i o f=我个人非常喜欢fix
方法。注意,它在道德上等同于“递归let
”方法,因为fix
是定义递归的一种方法。(事实上,我刚刚将一些代码切换为使用fix
而不是递归的let
)另外,请注意fix
也可以在Data.Function
中使用;这一点也不奇怪Control.Monad.Fix
还提供了MonadFix
类型类,该类提供了mfix::MonadFix m=>(a->ma)->ma
。对于喜欢“循环组合器”方法的任何人,你都可以在中找到比Fix
更复杂的循环。它非常整洁。令人惊讶:)你可以猜到我是哈斯克尔的新手;你能解释一下$
和
之间有什么不同吗?@Aslan986我现在又加了一点。不可能不接受你的回答。非常感谢安德鲁。
insult :: String -> String -> String
insult a b = a ++ ", you're almost as ugly as " ++ b
insult "Fred" "Barney"
insult "Fred" $ "Barney"
insult <$> ["Fred","Barney"] <*> ["Wilma","Betty"]
insult <$> Just "Fred" <*> Nothing
insult <$> Just "Fred" <*> Just "Wilma"
insult <$> readFile "someone.txt" <*> readFile "someoneElse.txt"
toCouplesFile = do inputFile <- openFile "deletedId.csv" ReadMode
outputFile <- openFile "couples.txt" WriteMode
let go = do
iseof <- hIsEOF inputFile
if iseof then (return outputFile)
else do line <- hGetLine inputFile
hPutStrLn outputFile (show (extractNameAndId line))
go
go
import Data.Functor ((<$>)
import Control.Monad.Loops (whileM_)
readAndChange i o =
whileM_ notEOF $ do line <- hGetLine i
hPutStrLn o (show (extractNameAndId line))
where notEOF = not <$> (hIsEOF i)
-- |Execute an action repeatedly as long as the given boolean expression
-- returns True. The condition is evaluated before the loop body.
-- Discards results.
whileM_ :: (Monad m) => m Bool -> m a -> m ()
whileM_ p f = do
x <- p
if x
then do
f
whileM_ p f
else return ()
transformInput :: String -> String
transformInput = ...
transformInput :: String -> String
transformInput input = unlines (map transformLine (lines input))
transformLine :: String -> String
transformLine line = show (extractNameAndId line)
hInteract :: Handle -> Handle -> (String -> String) -> IO ()
hInteract i o f = do s <- hGetContents i
hPutStr o (f s)
toCouplesFile = do inputFile <- openFile "deletedId.csv" ReadMode
outputFile <- openFile "couples.txt" WriteMode
hInteract inputFile outputFile transformInput