我关注'Learn Haskell Fast and Hard'并且我能够完成大部分内容,但我对以下代码示例有两个问题。
l
但在第二个版本中我需要l
?evenSum1
中,当递归调用该函数时,会一次又一次地在列表中调用filter
,或者在第一次调用时只调用filter
一次吗?
evenSum = accumSum 0
where
accumSum n [] = n
accumSum n (x:xs) =
if even x
then accumSum (n+x) xs
else accumSum n xs
evenSum1 l = mysum 0 (filter even l)
where
mysum n [] = n
mysum n (x:xs) = mysum (n+x) xs
答案 0 :(得分:6)
您实际上也可以删除第二个示例中的l
,但您需要切换到所谓的point free notation并使用函数组合运算符(.)
:
evenSum1 = mysum 0 . filter even
where
mysum n [] = n
mysum n (x:xs) = mysum (n + x) xs
在evenSum1
中,filter even
函数只会被调用一次。会发生的事情是filter even
用完了传入的列表,然后将其输出传递给mysum 0
。
假设您有一个功能add
:
add :: Int -> Int -> Int
add x y = x + y
然后你想创建一个总是为add5
添加5的函数Int
。你可以这样做
add5 :: Int -> Int
add5 y = add 5 y
但是由于函数是Haskell中的第一类对象,我们可以部分应用函数,这相当于说
add5 :: Int -> Int
add5 = add 5
另一种看待它的方法是在add
的类型签名中添加一些可选括号:
add :: Int -> (Int -> Int)
add x y = x + y
这样写的,我们可以说add
是一个接受单个Int
参数的函数,并返回Int -> Int
的新函数。因此,如果我们给add
一个Int
,我们就会得到一个新函数。这也是让我们编写像
filter even list
而不是
filter (\x -> even x) list
无点符号的一个好的经验法则是,变量可以从最后$
转变为.
:
f x y = h x $ g y
f x = h x . g
f x y z = h x $ g y $ j z
f x y = h x $ g y . j
这并不总是适用于多参数函数:
f x y = h $ g x y
与
不一样f = h . g
因为h . g
无法进行类型检查。这是因为隐含的括号:
f x y = h $ (g x) y
f x = h . (g x)
现在有一些括号可以放弃x
参数。
此外,请注意f x y = h (g x y)
等同于f x y = h $ g x y
,因此您通常可以将最外面的括号转换为$
,这可能会让您减少并更改{ {1}}到$
。如果所有这些看起来令人困惑,您还可以抓取pointfree hackage包,其中包含一个命令行工具,可以自动为您执行eta-reduction。