我想让用户输入数字列表并找到平均值。但是,在查找示例之后,我没有发现任何看起来与我正在做的事情匹配的事情,因为列表中可以包含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
答案 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)
的意思是:x
(head
)后跟列表xs
(tail
)。如果您了解这一点,那么您就会知道我们如何在许多情况下避免副作用:只需调用另一个函数即可完成其余的工作。 (注意:如果您了解堆栈,您可以想象在后台发生了什么。)
现在,如何计算列表的长度?在类似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
,初始值为0
和g(f xs, x) = x + f xs
。对于length
,初始值为0
和g(f xs, x) = 1 + f xs
。这种模式非常普遍,Haskell为此提供了一个内置函数(实际上是几个内置函数):foldl
。 foldl
接受一个函数,一个初始值和一个列表,并返回重复应用于先前结果和当前元素的函数,直到列表被消耗为止。您可以将函数视为循环的主体:
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
让我们尝试使用内置的average
和sum
函数编写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`