Haskell:如何在do块中内联[a]->IO a类型的辅助函数?
问题与解答:将
[a]->IO a函数内联到Haskell do块中 问题描述
我正在学习Graham Hutton所著的《Programming in Haskell》(第二版),刚完成了第138页的练习10.5。该练习要求编写一个类型为adder :: IO ()的函数,它能交互式读取n个数字,求和后输出结果,示例运行效果如下:
> adder How many numbers? 3 5 4 6 The total is 15
已知readInt和readLine函数定义如下:
readInt :: IO Int readInt = do line <- readLine return (read line :: Int) readLine :: IO String readLine = do c <- getChar case c of '\n' -> return [] _ -> do cs <- readLine return (c:cs)
我已实现了adder函数:
adder :: IO () adder = do putStr "How many numbers? " n <- readInt ns <- sequence [readInt | _ <- [1..n]] sum <- sumUp ns putStr $ "The total is " ++ (show sum) ++ "\n" sumUp :: [Int] -> IO Int sumUp xs = return $ foldl (+) 0 xs
我对当前方案基本满意,但希望将sumUp函数内联到do块中,却不知如何操作,请问该如何将[a]->IO a类型的函数内联到do块中?
解答
你的sumUp函数本质是把纯函数计算结果包装进IO上下文里——foldl (+) 0 xs是纯的数值求和操作,只是用return把它提升到IO类型。要内联它,有几种简洁且符合Haskell风格的方式:
方式1:用let绑定纯计算结果(推荐)
在do块中,let可以直接绑定纯值(不需要IO上下文),之后直接使用这个值即可,无需额外的return或<-:
adder :: IO () adder = do putStr "How many numbers? " n <- readInt ns <- sequence [readInt | _ <- [1..n]] let total = foldl (+) 0 ns -- 绑定纯求和结果 putStr $ "The total is " ++ show total ++ "\n"
方式2:直接内嵌计算表达式
如果追求代码紧凑,可以把求和逻辑直接写在输出语句里,甚至用Haskell内置的sum函数替代手动的foldl,让代码更简洁:
adder :: IO () adder = do putStr "How many numbers? " n <- readInt ns <- sequence [readInt | _ <- [1..n]] putStr $ "The total is " ++ show (sum ns) ++ "\n"
核心逻辑说明
原来的sum <- sumUp ns是从IO Int上下文里取出内部的Int值,但sumUp本身只是对纯求和结果做了一层IO包装。既然求和是纯操作,我们完全可以直接计算纯值,避免不必要的IO上下文包装,这也是Haskell中区分纯代码与IO代码的最佳实践。
内容的提问来源于stack exchange,提问作者Patrick Bucher
相关产品推荐
相关产品推荐

