我试图理解Haskell中的函数组合。
根据ZVON http://zvon.org/other/haskell/Outputprelude/filter_f.html
filter函数应该有两个参数,一个bool函数和一个列表。
示例filter (>5) [1,2,3,4,5,6,7,8]
返回大于5的任何内容:
[6,7,8]
问题,具有多个函数组合的以下行如何传入一个布尔值以供过滤器使用?
map fst . filter snd . assocs . soeA
不应该是map fst。过滤器(== True)snd。联想。 soeA
分析我运行合成的前两个函数并传递一个参数:assocs . soeA $ 9
返回
[(0,False),(1,False),(2,True),(3,True),(4,False),(5,True),(6,False),(7,True),(8,False),(9,False)]
soe 9
返回[2,3,5,7]
以某种方式使用soeA的每个数组元素中的bool值,但是非常感谢任何帮助解释这个组合是如何工作的。
完整代码是: `
module FastSeive where
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
soeST :: forall s. Int -> ST s (STUArray s Int Bool)
soeST n = do
arr <- newArray (0, n) True
mapM_ (\i -> writeArray arr i False) [0, 1]
let n2 = n `div` 2
let loop :: Int -> ST s ()
loop i | i > n2 = return ()
loop i = do
b <- readArray arr i
let reset :: Int -> ST s ()
reset j | j > n = return ()
reset j = writeArray arr j False >> reset (j + i)
when b (reset (2*i))
loop (succ i)
loop 2
return arr
soeA :: Int -> UArray Int Bool
soeA n = runST (soeST n >>= freeze)
soe :: Int -> [Int]
soe = map fst . filter snd . assocs . soeA
soeCount :: Int -> Int
soeCount = length . filter id . elems . soeA
`
答案 0 :(得分:0)
简短的回答是:snd
是Bool
- 返回函数filter
所期望的。在你写的表达式中:map fst . filter (==True) snd . assocs . soeA
。 snd
将是filter
的第二个参数,而(==True)
将是第一个参数。当然,它不会进行类型检查,因为filter
已经应用于两个参数,并且不能在函数组合中使用:它不再是函数。
要获得更长的答案,我们实际上可以应用(.)
的定义来了解正在发生的事情:
(f . g) x = f (g x)
-- In haskell, it is defined as being right associative
-- Meaning that if we put explicit parenthesises, we'd have:
soe = (map fst . (filter snd . (assocs . soeA)))
-- That only really matters for the compiler, though,
-- because we know function composition is associative.
soe = map fst . filter snd . assocs . soeA
-- "Un-pointfree-ing" it:
soe x = (map fst . filter snd . assocs . soeA) x
-- Applying (.)'s definition:
soe x = map fst ((filter snd . assocs . soeA) x)
-- Again:
soe x = map fst (filter snd ((assocs . soeA) x))
-- And again:
soe x = map fst (filter snd (asocs (soeA x)))
现在很清楚,snd
是filter
的第一个参数,而第二个参数将评估assocs (soeA x)
将评估的内容。
更一般地说,当一个人写f . g . h
时,可以从右到左阅读这个函数,首先将h
应用于其参数,然后将g
应用于结果,然后f
到下一个结果,并产生最终值。
现在,对于更长的答案,我们可以看一下如何推断表达式的类型。它会告诉我们为什么snd
是Bool
- 返回函数filter
期望即使它的类型签名为snd :: (a, b) -> b
。
免责声明:我没有编译器工程的背景知识;我将要使用的术语可能不准确。
filter
的类型为(a -> Bool) -> [a] -> [a]
。 snd
的类型为(a, b) -> b
。
这些实际上是参数化类型。我们可以使类型参数明确:
filter :: forall a. (a -> Bool) -> [a] -> [a]
snd :: forall a b. (a, b) -> b
我们还将重命名filter
的类型参数,以使其在下一步写入时不明确:
filter :: forall c. (c -> Bool) -> [c] -> [c]
filter
首先应用于snd
。因此,我们可以尝试将c -> Bool
与filter
,(a, b) -> b
类型统一起来snd
。我们得到这些方程式:
c -> Bool = (a, b) -> b
===
c = (a, b)
b = Bool
===
c = (a, Bool)
b = Bool
我们假设assocs (soeA x)
的类型为[(Int, Bool)]
。由于filter
的第二个参数的类型为[c]
,我们可以进一步统一:
[c] = [(Int, Bool)]
===
c = (Int, Bool)
这也给了我们:
(Int, Bool) = c = (a, Bool)
===
a = Int
因此,在类型应用之后,我们为子表达式获取这些具体类型:
filter :: ((Int, Bool) -> Bool) -> [(Int, Bool)] -> [(Int, Bool)]
snd :: (Int, Bool) -> Bool
当然,我们可以一直使用GHC的类型推断告诉我们,使用GHCi,或者通过文本编辑器的haskell插件。