用户输入的平均值

时间:2018-12-04 23:12:31

标签: function haskell average

我想让用户输入数字列表并找到平均值。但是,在查找示例之后,我没有发现任何看起来与我正在做的事情匹配的事情,因为列表中可以包含2-100个数字。任何帮助/建议,表示赞赏。

下面是我的工作代码。

main = do
  putStrLn "Enter how many numbers:"
  listlen <- getLine 
  if ((read listlen) <= 100) -- read converts a string to a number
  then do  

  putStrLn "Enter a String of numbers:"
  --numberString <- getLine

  numberString<- getLine
  let ints = map read (words numberString)  :: [Int]
  putStrLn("The List: " ++(numberString))

  putStrLn("The average: ")
  putStrLn("Number of values greater than average: ")

  else do
    putStrLn " Error: listlen must be less than or = to 100"
    main

2 个答案:

答案 0 :(得分:0)

好的,这是家庭作业,但是当您必须在Haskell中进行作业时,家庭作业可能真的很难。我将尝试逐步解释如何实现。

很高兴

首先,Haskell具有功能。您可以找到“功能”的不同定义,但是基本上您可以将其视为语言的一种特性:一切都是不变的(无副作用)。

第二,您可以通过在终端中输入ghci来启动REPL

jferard@jferard-Z170XP-SLI:~$ ghci
GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Prelude> :set +m -- enable parsing of multiline commands
Prelude> -- type the expression you want to evaluate here

递归

如何计算列表元素的总和?在命令式语言中,您将执行类似的操作(类似于Python)

s = 0
for every x in xs:
    s <- s + x

但是s不是常数。它会在每次迭代中更新,直到获得总和。我们可以重新制定算法以避免这种突变吗?幸运的是。这是关键思想:

sum [x] = x
sum [x1, ..., xn] = x1 + sum [x2, ..., xn]

稍加想象,您可以说sum [] = 0。所以我们可以用Haskell编写它:

sum [] = 0
sum (x:xs) = x + sum xs

-- sum [1, 2, 3, 5] == 11

(x:xs)的意思是:xhead)后跟列表xstail)。如果您了解这一点,那么您就会知道我们如何在许多情况下避免副作用:只需调用另一个函数即可完成其余的工作。 (注意:如果您了解堆栈,您可以想象在后台发生了什么。)

现在,如何计算列表的长度?在类似Python的语言中,您会做类似(忘记len(..))的事情:

l = 0
for every x in xs:
    l <- l + 1

再次,使用递归定义,您将:

length [] = 0
length (x:xs) = 1 + length xs

-- len [1, 2, 3, 5] == 4

折叠

计算总和和长度是如此普遍,以至于它们是Haskell中的内置函数。但是还有更重要的事情:如果仔细检查这两个功能,您会注意到这种模式:

f [] = <initial value>
f (x:xs) = g(f xs, x)

对于sum,初始值为0g(f xs, x) = x + f xs。对于length,初始值为0g(f xs, x) = 1 + f xs。这种模式非常普遍,Haskell为此提供了一个内置函数(实际上是几个内置函数):foldlfoldl接受一个函数,一个初始值和一个列表,并返回重复应用于先前结果和当前元素的函数,直到列表被消耗为止。您可以将函数视为循环的主体:

sum xs = foldl (+) 0 xs

(关于加密的注意事项:1.您可能有一天会了解到,Haskell函数始终采用一个参数,但这不是重点。2.您可以在两边都删除xs:sum = foldl (+) 0

Prelude> foldl (+) 0 [1,2,3,5]
11 -- (((0+1)+2)+3)+5

使用scanl,您可以通过某种方式“调试” foldl

Prelude> scanl (+) 0 [1,2,3,5]
[0,1,3,6,11]

length更为棘手,因为您没有内置功能g(f xs, x) = 1 + f xs。您可以使用lambda function\acc x -> 1 + acc,其中acc是当前值:

length xs = foldl (\acc x -> 1 + acc) 0 xs

您的问题

平均

让我们尝试使用内置的averagesum函数编写length

Prelude> average xs = sum xs / length xs

<interactive>:1:14: error:
    • Could not deduce (Fractional Int) arising from a use of ‘/’
    ...

那是什么意思?我不会详细介绍,但是您必须知道Haskell对数字非常严格。您不能不经过两个工作就将两个整数相除并期望得到浮点结果。

Prelude> :t (/)
(/) :: Fractional a => a -> a -> a

这意味着/将花费Fractional s。因此,工作是:将整数转换为Fractional s。

average xs = fromIntegral (sum xs) / fromIntegral (length xs)

-- average [1, 2, 3, 5] == 2.75

大于平均值的值数

现在,值的数量大于平均值。用类似Python的语言编写:

c = 0
for every x in xs:
    if x > avg:
        c <- c + 1

让我们尝试递归方法:

gtAvg [] = 0
gtAvg (x:xs) = (if x>avg then 1) + sum xs -- WRONG

您看到缺少一些东西。在命令式版本中,如果为x <= avg,我们什么也不做(因此不更新值)。在这里,我们必须返回一些内容:

gtAvg [] = 0
gtAvg (x:xs) = (if x>avg then 1 else 0) + gtAvg xs

但是avg值从何而来?我们需要对其进行预先计算。让我们定义一个以avg作为参数的函数:

gtAvg' [] _ = 0
gtAvg' (x:xs) avg = (if fromIntegral x>avg then 1 else 0) + gtAvg' xs avg

-- gtAvg' [1, 2, 3, 5] (average [1, 2, 3, 5]) == 2

然后:

gtAvg xs = gtAvg' xs (average xs)

-- gtAvg [1, 2, 3, 5] == 2

显然,使用foldl更简单:

gtAvg xs = foldl (\acc x -> if fromIntegral x>average xs then acc+1 else acc) 0 xs

更多(地图,过滤器和列表理解)

在了解Haskell的基础知识时,您可能还需要三个结构。

过滤器

首先,一个filter

Prelude> filter (>2.75) [1, 2, 3, 5]
[3.0,5.0]

如果使用该列表的长度,则得到的元素数将大于平均值:

gtAvg xs = length $ filter (\x -> fromIntegral x >average xs) xs

(或具有函数组合:length $ filter ((> average xs).fromIntegral) xs)请不要受$符号的干扰:这意味着表达式(filter...)的右侧是一个块,例如是否放在括号中。

地图

第二,map将函数应用于列表的每个元素,并返回映射元素的列表。例如,如果要对列表中的元素进行平方运算:

Prelude> sum $ map (**2)  [1, 2, 3, 5]
39.0

您可以这样使用它:

gtAvg xs = length $ filter (>average xs) $ map fromIntegral xs

它将元素转换为Fractional,然后应用过滤器。

列表理解

第三,您可以拥有filter和一个map并具有列表理解功能:

gtAvg xs = length [x | x<-xs, fromIntegral x>average xs]

我抛开了很多东西,大概做了一些近似,但是现在您应该具备回答问题的基本知识。

答案 1 :(得分:-1)

listlen :: [a] -> Int
listlen xs = go 0 xs                 -- 0 = initial value of accumulator
  where go s [] = s                  -- return accumulator
        go s (a:as) = go (s+1) as    -- compute the next value of the accumulator
sumx :: [a] -> Int
sumx xs = go 0 xs
  where go s [] = s
        go s (a:as) = go ... as      -- flll in the blank ...                                     -- and recurse
lenAndSum :: [a] -> (Int,Int)
lenAndSum xs = go (0,0) xs             -- (0,0) = initial values of both accumulators
  where go (s1,s2) [] = (s1,s2)        -- return both accumulators at the end
        go (s1,s2) (a:as) = go ... as  -- left as an exercise                                    

main = do

  putStrLn "Enter how many numbers:"
  listlen <- getLine 
  if ((read listlen) <= 100) -- read converts a string to a number
  then do  

  putStrLn "Enter a String of numbers:"
  --numberString <- getLine

  numberString<- getLine
  let ints = map read (words numberString)  :: [Int]
  putStrLn("The List: " ++(numberString))



  putStrLn("The average: ")
  putStrLn("Number of values greater than average: ")

  else do
    putStrLn " Error: listlen must be less than or = to 100"
    main`enter code here`