Haskell IO平均函数IO,纯vs非纯的尝试



我想写一个程序,用户不断输入数字,一旦输入负数,就会输出平均值这是我保留pure separate from unpure的尝试有人能给我提示在修复这个代码,和纯和不纯的分离提示。

getFloat :: IO Float
getFloat = do line <- getLine
              return (read line:: Float)  

"不纯部分"

average :: IO Float
average = do x <- getFloat
             return((fst help x)/(snd help x))

"pure part"

help x 
    |x>=0 = (x+sum1, 1+ counter)
    |otherwise = (sum1 , counter)
    where 
    sum1 = 0.0
    counter = 0.0

我得到这个错误

.hs:45:26:
    Couldn't match expected type `(t0 -> Float, b0)'
                with actual type `t1 -> (t1, t2)'
    In the first argument of `fst', namely `help'
    In the first argument of `(/)', namely `(fst help x)'
    In the first argument of `return', namely
      `((fst help x) / (snd help x))'
Failed, modules loaded: none.

让我们先看看你的代码出了什么问题,然后再看如何修复它。

函数getFloat是好的,并且做的正是你认为它做的。

函数help不是做你认为它做什么。事实上,当输入值x时,如果x > 0,则返回元组(x,1.0),否则返回(0.0,0.0)。你可以给它一个类型:

help :: Double -> (Double,Double)

而我猜你想让它的类型是

help :: [Double] -> (Double,Double)

你可以这样写你想要的版本:

help xs = (sum xs, fromIntegral (length xs))

在函数average中,您有表达式fst help x,它像这样括号括起来:(fst help) x。这就是你的错误消息的原因:你只能将fst应用于(a,b)类型的东西,但你试图将其应用于Double -> (Double,Double)类型的东西,因此抛出错误。

如果我要写这个函数,我可能会这样做:

import Data.List (takeWhile) -- take elements from a list while predicate is satisfied
avg xs = sum xs / fromIntegral (length xs)
main = do numbers <- fmap (takeWhile (>0) . map read . words) getContents
          return (avg numbers)

函数avg是纯函数。函数takeWhile (>0) . map read . words也是纯函数,可以这样分解:

helper :: String -> [Double]
helper = takeWhile (>0) . map read . words
main = do numbers <- fmap helper getContents
          return (avg numbers)

这说明了一个一般原则——使用非纯代码输入和输出将要处理的值,并使用纯代码转换这些值。

最新更新