尝试在Haskell中使用函数组合时出错

时间:2011-02-20 15:14:07

标签: haskell filter function-composition

我最近刚刚开始学习Haskell,更具体地说是关于函数组合,部分函数,​​映射,过滤器和剖分的主题。在其中一个练习中,我被要求通过使用函数组合来修改twoFilters函数。

我已经在.上阅读了几个wiki,但我很难让它正常工作。据我了解,它的工作原理是按字母顺序执行函数b . a并返回结果。换句话说,x = foo a然后是foo b of x。然而,在使用两个过滤器函数应用了几个“变体/可能性”之后,由于错误,我无法进行编译。

greaterThanOne :: Int -> Bool
greaterThanOne = (>1)

lessThanTen :: Int -> Bool
lessThanTen = (<10)

twoFilters :: [Int] -> [Int]
twoFilters xs= filter lessThanTen (filter greaterThanOne xs)

这两个是我最有信心的失败尝试;

twoFilters xs = filter (lessThanTen . greaterThanOne xs)

twoFilters xs = filter (lessThanTen xs . greaterThanOne xs)

在推理的哪个地方,我出错了?

7 个答案:

答案 0 :(得分:3)

你有信心的尝试是你的逻辑中的一个简单的失败:点运算符的工作原理如下:

(f.g)(x) = f(g(x))

因此,尝试计算5的示例给出:

lessThanThen(greaterThanOne(5)) = lessThanTen(True) -- that can't be right, can it???

你想要的是一个lambda和&amp;&amp;:

filter (\x-> (lessThanThen x) && greaterThanOne(x))

或者,您可以使用两个过滤器:

filter lessThanTen . filter greaterThanOne $

答案 1 :(得分:3)

进入应用函数的精彩世界:

import Control.Applicative

greaterThanOne = (>1)

lessThanTen = (<10)

twoFilters = filter ((&&) <$> greaterThanOne <*> lessThanTen)

twoFilters [1,2,3,4,5,6,7,8,9,10]
-- [2,3,4,5,6,7,8,9]

阅读Learn you a Haskell - Applicative Functors以获取详细说明。

答案 2 :(得分:2)

你不能像这样编写这两个函数。 f . g的作用类似于数学中的合成,即相当于f(g(x))。这意味着外部函数必须采用内部函数返回的类型的参数,在您的情况下,外部函数必须是Bool -> Bool

您可以使用合成运算符编写twoFilters,如下所示:

twoFilters = (filter lessThanTen) . (filter greaterThanOne)

答案 3 :(得分:1)

(.)需要一个函数,该函数接受一个参数并返回一个值,但是您在其中传递Bool值:

lessThanTen . greaterThanOne xs

这是错误的。

下面:

lessThanTen xs . greaterThanOne xs

您尝试撰写两个Bool值,但您应该编写两个返回Bool值的函数。

答案 4 :(得分:1)

功能应用程序具有最高优先级的一个问题。因此lessThanTen . greaterThanOne xs尝试使用lessThanTen的结果组合greaterThanOne xs(这不起作用,该函数适用于整数,而不适用于其列表)。同样地,lessThanTen xs. greaterThanOne xs试图组合这些函数调用的结果(假设它们首先是有意义的),而不是它们自己的函数。

另一个问题是对.的误解 - (f . g) x等同于f (g x),即第一个函数的结果是第二个函数的参数。因此g的类型必须为(a -> b)f的类型必须为(b -> c)(两个b都是相同的类型变量!)。您希望将两个函数应用于同一参数并将结果与​​&&结合使用。据我所知,目前还没有现有功能(至少Hoogle没有为(a -> Bool) -> (a -> Bool) -> a -> Bool找到任何东西)。你必须自己做:

both f g x = f x && g x

或者,您只需坚持过滤两次(由于懒惰的评估,这并不像听起来那么糟糕) - filter (>1) $ filter (<10) xs

答案 5 :(得分:1)

  

据我了解,它的工作原理是按字母顺序执行函数b . a并返回结果。换句话说,x = foo a然后是foo b of x

这可以用Haskell编写为

let x = foo a in 
foo b x

foo来自哪里?)但是正确的

(b . a) x = let y = a x in
            b y

或者,更短:

(b . a) x = b (a x)

现在,filter lessThanTen (filter greaterThanOne xs)与此定义的右侧具有相似的形状,如果您还记得可以将其写为(filter lessThanTen) ((filter greaterThanOne) xs)

((filter lessThanTen) . (filter greaterThanOne)) xs

大概你真正想要的是filter ??? xs,但这应该足以继续了。

答案 6 :(得分:1)

你几乎是对的。我发现使用.开始学习函数合成的最简单方法是首先使用$

所以你有一个清单

twoFilters xs = xs

您希望按greaterThanOne

进行过滤
twoFilters xs = filter greaterThanOne $ xs

您还希望按lessThanTen

进行过滤
twoFilters xs = filter lessThanTen $ filter greaterThanOne $ xs

现在从左向右移动,将所有$替换为.,除了上一个$

twoFilters xs = filter lessThanTen . filter greaterThanOne $ xs

您现在可以使用括号而不是$

twoFilters xs = (filter lessThanTen . filter greaterThanOne) xs

或者只是定义函数pointfree:

twoFilters = filter lessThanTen . filter greaterThanOne

我认为括号内的版本是最重要的。它表明您将两个部分应用的函数filter lessThanTenfilter greaterThanOne融合到一个超级过滤函数中,使用.然后将列表应用于它。您需要将它们括起来,因为.通过空格绑定比函数应用程序更紧密(空间可以被认为是$的超高固定性版本)。请记住,当您使用.时,您将两个函数融合在一起形成一个超级函数。

检查.

的类型签名是相关的
(.) :: (b -> c) -> (a -> b) -> a -> c

您提供的功能必须与非常特殊的类型签名“对齐”(它们必须兼容融合)。但老实说,关键是要学会识别函数应用程序(带空格)何时绑定比你打算更紧密,并弄乱你想要编写的函数的类型签名。无论如何,这就是我的意思。