Haskell多功能组合

时间:2017-01-11 20:22:12

标签: filter composition

我试图理解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

`

1 个答案:

答案 0 :(得分:0)

简短的回答是:sndBool - 返回函数filter所期望的。在你写的表达式中:map fst . filter (==True) snd . assocs . soeAsnd将是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)))

现在很清楚,sndfilter的第一个参数,而第二个参数将评估assocs (soeA x)将评估的内容。

更一般地说,当一个人写f . g . h时,可以从右到左阅读这个函数,首先将h应用于其参数,然后将g应用于结果,然后f到下一个结果,并产生最终值。

现在,对于更长的答案,我们可以看一下如何推断表达式的类型。它会告诉我们为什么sndBool - 返回函数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 -> Boolfilter(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插件。