Haskell 函数与map函数的组合
Haskell Function Composition with Map Function
我正在阅读 Richard Bird 的 "Thinking Functionally with Haskell" 书,其中有一节我无法理解他在何处证明了过滤方法的 属性。他证明的是:
filter p . map f = map f . filter (p . f)
之前在书中,他将过滤器定义为:
filter p = concat . map (test p)
test p x = if p x then [x] else []
他是这样证明第一个方程的:
filter p . map f
= {second definition of filter} -- He's referring to the definition I gave above
concat . map (test p) . map f
= {functor property of map}
concat . map (test p . f)
= {since test p . f = map f . test (p . f)}
concat . map (map f . test (p . f))
= {functor property of map}
concat . map (map f) . map (test (p . f))
= {naturality of concat}
map f . concat . map (test (p . f))
= {second definition of filter}
map f . filter (p . f)
我不明白的是test p . f
怎么等于map f . test (p . f)
。
我是这样测试的:
test :: (a -> Bool) -> a -> [a]
test p x = if p x then [x] else []
test ((<15) . (3*)) 4 -- test p .f, returns [4]
(map (3*) . test((<15) . (3*))) 4 -- map f . test (p . f), returns [12]
任何人都可以解释一下我在这里缺少什么吗?
您测试过
test (p . f) = map f . test (p . f)
这确实是错误的。 属性 实际上是
test p . f = map f . test (p . f)
LHS 关联为
test p . f = (test p) . f
请记住,函数应用程序比任何用户可定义的运算符都更紧密地绑定,表现得像 infixl 10
。彼此相邻的两个标识符始终是前缀函数应用程序的一部分。 (除了 as-patterns 的情况:f xs@ys zs
表示 f (xs@ys) zs
。)
证明属性:
test p . f
={definition of (.)}
\x -> test p (f x)
={definition of test}
\x -> if p (f x) then [f x] else []
={definition of map, multiple times}
\x -> if p (f x) then map f [x] else map f []
={float map f out of cases}
\x -> map f (if p (f x) then [x] else [])
={definition of (.)}
\x -> map f (if (p . f) x then [x] else [])
={definition of test}
\x -> map f (test (p . f) x)
={definition of (.)}
map f . test (p . f)
调整您的示例,test (<15) . (*3)
表示 "multiply by 3
, ensuring that the result is less than 15
." map (*3) . test ((<15) . (*3))
表示 "ensure that thrice the input is less than 15
, and, if so, return thrice the input."
涵盖了您的测试用例以及如何使用 test
的定义来证明方程。不过,我想说仍然存在一个潜在的问题:我们应该从哪个帽子得出这个等式——我们为什么要考虑它是真的可能性?为了回答这个问题,让我们首先再看一下等式:
test p . f = map f . test (p . f)
换句话说,它表示使用一些 f
函数修改值,然后给定一些合适的谓词 p
,对其应用 test p
与修改值相同在使用 test
之后(通过将谓词与 f
组合进行适当修改,使其适合未修改值的类型)。
接下来,让我们考虑test
的类型:
-- I'm adding the implicit forall for the sake of emphasis.
forall a. (a -> Bool) -> a -> [a]
这里的关键是具有这种类型的函数必须适用于 a
的任何选择。如果它可以是任何东西,我们在用这种类型实现函数时事先不知道任何事情,比如 test
。这严重限制了这样一个函数的功能:特别是,结果列表的元素(如果有的话)必须与提供的类型 a
的值相同(我们怎么能在不知道的情况下将其更改为其他内容它的类型提前?),并且谓词必须被忽略或应用于相同的值(我们还要将它应用于什么?)。考虑到这一点,等式现在说的很自然:我们在 test
之前或之后用 f
更改值并不重要,因为 test
不会更改值靠自己。
使这种推理变得严格的一种方法是通过自由定理。由于参数多态性,类型的自由定理保证始终适用于该类型的任何可能值,并且您不需要除类型以外的任何东西来计算它。碰巧 forall a. (a -> Bool) -> a -> [a]
的自由定理恰好是 test p . f = map f . test (p . f)
。由于我不能在这些简短的行中公正地对待这个主题,这里有一些关于自由定理的参考资料:
Parametricity: Money for Nothing and Theorems for Free,作者 Bartosz Milewski,是一个很好的阐述,如果你想深入挖掘的话,它提供了指向主要主要来源的指针。
What Does fmap Preserve?,真的,不完全是关于自由定理,但它以(希望)一种易于访问的方式呈现了一些相关主题。
相关 Stack Overflow 帖子:amalloy's answer to Polymorphic reasoning; Type signatures that never make sense.
lambdabot 可以为您生成免费的定理。您可以将其用作命令行工具或通过在#haskell Freenode IRC 频道上运行的机器人来使用。
我正在阅读 Richard Bird 的 "Thinking Functionally with Haskell" 书,其中有一节我无法理解他在何处证明了过滤方法的 属性。他证明的是:
filter p . map f = map f . filter (p . f)
之前在书中,他将过滤器定义为:
filter p = concat . map (test p)
test p x = if p x then [x] else []
他是这样证明第一个方程的:
filter p . map f
= {second definition of filter} -- He's referring to the definition I gave above
concat . map (test p) . map f
= {functor property of map}
concat . map (test p . f)
= {since test p . f = map f . test (p . f)}
concat . map (map f . test (p . f))
= {functor property of map}
concat . map (map f) . map (test (p . f))
= {naturality of concat}
map f . concat . map (test (p . f))
= {second definition of filter}
map f . filter (p . f)
我不明白的是test p . f
怎么等于map f . test (p . f)
。
我是这样测试的:
test :: (a -> Bool) -> a -> [a]
test p x = if p x then [x] else []
test ((<15) . (3*)) 4 -- test p .f, returns [4]
(map (3*) . test((<15) . (3*))) 4 -- map f . test (p . f), returns [12]
任何人都可以解释一下我在这里缺少什么吗?
您测试过
test (p . f) = map f . test (p . f)
这确实是错误的。 属性 实际上是
test p . f = map f . test (p . f)
LHS 关联为
test p . f = (test p) . f
请记住,函数应用程序比任何用户可定义的运算符都更紧密地绑定,表现得像 infixl 10
。彼此相邻的两个标识符始终是前缀函数应用程序的一部分。 (除了 as-patterns 的情况:f xs@ys zs
表示 f (xs@ys) zs
。)
证明属性:
test p . f
={definition of (.)}
\x -> test p (f x)
={definition of test}
\x -> if p (f x) then [f x] else []
={definition of map, multiple times}
\x -> if p (f x) then map f [x] else map f []
={float map f out of cases}
\x -> map f (if p (f x) then [x] else [])
={definition of (.)}
\x -> map f (if (p . f) x then [x] else [])
={definition of test}
\x -> map f (test (p . f) x)
={definition of (.)}
map f . test (p . f)
调整您的示例,test (<15) . (*3)
表示 "multiply by 3
, ensuring that the result is less than 15
." map (*3) . test ((<15) . (*3))
表示 "ensure that thrice the input is less than 15
, and, if so, return thrice the input."
test
的定义来证明方程。不过,我想说仍然存在一个潜在的问题:我们应该从哪个帽子得出这个等式——我们为什么要考虑它是真的可能性?为了回答这个问题,让我们首先再看一下等式:
test p . f = map f . test (p . f)
换句话说,它表示使用一些 f
函数修改值,然后给定一些合适的谓词 p
,对其应用 test p
与修改值相同在使用 test
之后(通过将谓词与 f
组合进行适当修改,使其适合未修改值的类型)。
接下来,让我们考虑test
的类型:
-- I'm adding the implicit forall for the sake of emphasis.
forall a. (a -> Bool) -> a -> [a]
这里的关键是具有这种类型的函数必须适用于 a
的任何选择。如果它可以是任何东西,我们在用这种类型实现函数时事先不知道任何事情,比如 test
。这严重限制了这样一个函数的功能:特别是,结果列表的元素(如果有的话)必须与提供的类型 a
的值相同(我们怎么能在不知道的情况下将其更改为其他内容它的类型提前?),并且谓词必须被忽略或应用于相同的值(我们还要将它应用于什么?)。考虑到这一点,等式现在说的很自然:我们在 test
之前或之后用 f
更改值并不重要,因为 test
不会更改值靠自己。
使这种推理变得严格的一种方法是通过自由定理。由于参数多态性,类型的自由定理保证始终适用于该类型的任何可能值,并且您不需要除类型以外的任何东西来计算它。碰巧 forall a. (a -> Bool) -> a -> [a]
的自由定理恰好是 test p . f = map f . test (p . f)
。由于我不能在这些简短的行中公正地对待这个主题,这里有一些关于自由定理的参考资料:
Parametricity: Money for Nothing and Theorems for Free,作者 Bartosz Milewski,是一个很好的阐述,如果你想深入挖掘的话,它提供了指向主要主要来源的指针。
What Does fmap Preserve?,真的,不完全是关于自由定理,但它以(希望)一种易于访问的方式呈现了一些相关主题。
相关 Stack Overflow 帖子:amalloy's answer to Polymorphic reasoning; Type signatures that never make sense.
lambdabot 可以为您生成免费的定理。您可以将其用作命令行工具或通过在#haskell Freenode IRC 频道上运行的机器人来使用。