您可以使用(==)< *>来查明列表是否是回文结构。相反。它是如何工作的?

时间:2016-05-31 17:21:23

标签: list haskell reverse palindrome

我已尝试使用这些类型进行此操作,但我仍然难以理解其工作原理。

假设:

> :t (==)
(==) :: Eq a => a -> a -> Bool

> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

> :t reverse
reverse :: [a] -> [a]

> :t (==) <*> reverse
(==) <*> reverse :: Eq a => [a] -> Bool

直观地我可以理解它将相等运算符与反向结合起来,它创建了一个函数,用于检查反转列表是否与原始列表相等,但实际上并不是更多的信息。已经很明显了。

有人可以更详细地分析这里实际发生的事情的内部结构吗?

2 个答案:

答案 0 :(得分:7)

(<*>)

的类型开头
> : t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

现在(->) kApplicative的实例,带有实现

instance Applicative ((->) k) where
    pure a  = \_ -> a
    f <*> g = \k -> f k (g k)

特别是(<*>)专门用于(->) k的类型是

(<*>) :: (k -> a -> b) -> (k -> a) -> (k -> b)

因此应用程序(==) <*> reverse

(==) <*> reverse = \k -> (==) k (reverse k)
                 = \k -> k == reverse k

即。它会检查列表是否与其相反。

答案 1 :(得分:2)

克里斯·泰勒的答案是正确的,但另一种看待它的方式,我觉得更直观,是:函数类型Applicative实例的作用是:

  1. &#34;进料&#34;具有相同参数类型的两个函数的相同参数值;
  2. 将他们的结果与另一个功能结合起来。
  3. 基本上,如果您有f :: t -> ag:: t -> bApplicative实例可让您将功能h :: a -> b -> c映射到ab结果,假设fg将被提供相同的参数。

    所以想想你如何以非复杂的方式写下回文测试:

    palindrome :: Eq a => [a] -> Bool
    palindrome xs = xs == reverse xs
    

    xs在定义的右侧出现两次:一次作为==的参数,另一次作为reverse的参数。这会自动告诉您可能有一种方法可以使用(->) t应用程序实例来消除重复。对此的一种不同的,可能更直观的攻击是首先将函数重写为:

    palindrome xs = id xs == reverse xs
    

    ...其中id x = x标识函数,它只返回其参数(并且是标准库函数)。现在,您可以使用标准Applicative习语(f <$> a0 <*> ... <*> an)将其重写为:

    -- Function that feed the same argument value to both `id` and `reverse`,
    -- then tests their results with `==`: 
    palindrome = (==) <$> id <*> reverse
    

    现在我们可以问一下是否有办法摆脱重写中的id。由于<$>只是fmap的简写,我们可以研究Functor的{​​{1}}实例,这只是表达函数组合的另一种方式:

    (->) t

    功能组合最重要的特性之一是任何函数instance Functor ((->) t) where -- Mapping `f` over a *function* `g` is just the same as composing `f` -- on the results of `g`. fmap f g = f . g

    f

    因此,将其应用于上述版本f . id = f ,我们得到:

    palindrome