4

我最近才开始学习 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)

我的推理在哪里错了?

4

7 回答 7

3

您确信的尝试是您逻辑中的简单失败:点运算符的工作方式如下:

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

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

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

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

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

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

filter lessThanTen . filter greaterThanOne $

于 2011-02-20T15:31:13.127 回答
3

进入 Applicative Functors 的奇妙世界:

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以获得详细说明。

于 2011-02-20T16:18:45.550 回答
2

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

您可以twoFilters像这样编写使用组合运算符:

twoFilters = (filter lessThanTen) . (filter greaterThanOne)
于 2011-02-20T15:31:13.970 回答
1

(.)期望一个接受一个参数并返回一个值的函数,但是您将一个Bool值传递给它:

lessThanTen . greaterThanOne xs

这是错误的。

这里:

lessThanTen xs . greaterThanOne xs

您正在尝试组合两个Bool值,但您应该组合两个返回Bool值的函数。

于 2011-02-20T15:30:50.273 回答
1

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

另一个问题是对.-(f . g) x等价于的误解f (g x),即第一个函数的结果是第二个函数的参数。所以g必须是的类型和必须是的(a -> b)类型(两者都是相同的类型变量!)。您想要将两个函数应用于同一参数并将结果与​​ . 据我所知,目前还没有这方面的功能(至少 Hoogle 没有找到任何相关功能)。您必须自己制作:f(b -> c)b&&(a -> Bool) -> (a -> Bool) -> a -> Bool

both f g x = f x && g x

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

于 2011-02-20T15:33:47.837 回答
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,但这应该足够继续了。

于 2011-02-20T15:38:48.810 回答
1

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

所以你有一个清单

twoFilters xs = xs

你想过滤greaterThanOne

twoFilters xs = filter greaterThanOne $ xs

您还想过滤lessThanTen

twoFilters xs = filter lessThanTen $ filter greaterThanOne $ xs

现在从左到右移动,用除了最后一个之外的所有$s.$

twoFilters xs = filter lessThanTen . filter greaterThanOne $ xs

您可以使用括号代替$现在:

twoFilters xs = (filter lessThanTen . filter greaterThanOne) xs

或者只定义函数 pointfree:

twoFilters = filter lessThanTen . filter greaterThanOne

我认为括号中的版本是最重要的。它表明您将两个部分应用的功能融合filter lessThanTenfilter greaterThanOne一个大型过滤功能,.然后列表应用到它。您需要将它们括起来,因为.通过空格(空格可以被认为是 uber-high-fixity 版本)的绑定不如函数应用程序那么紧密$。请记住,当您使用 时.,您正在将两个功能融合在一起以形成一个宏功能。

检查类型签名是相关的.

(.) :: (b -> c) -> (a -> b) -> a -> c

您提供给它的功能必须与非常特殊的类型签名“对齐”(它们必须与融合兼容)。但老实说,关键是要学会识别函数应用程序(带空格)何时绑定得比您预期的更紧密,并弄乱您尝试编写的函数的类型签名。反正对我来说就是这样。

于 2011-02-20T17:27:59.417 回答