如何推断haskell中“(not .). any”的类型

cbjzeqam  于 11个月前  发布在  其他
关注(0)|答案(4)|浏览(91)

我通过codewars学习Haskell。
https://www.codewars.com/kata/54589f3b52756d34d6000158/train/haskell
答案是

module Codewars.Kata.AllNoneAny where

import Prelude hiding (all, any)

all, none, any :: (a -> Bool) -> [a] -> Bool
all  = (and .) . map
none = (not .) . any
any  = (or .) . map

字符串

  1. (.)infixr 9,所以我认为(. any)应该先合并,然后再合并(not .),对吗?
    1.如何将合并(not .) :: (a -> Bool) -> a -> Bool(. any) :: (([a] -> Bool) -> c) -> (a -> Bool) -> c组合成(not .) . any :: (a -> Bool) -> [a] -> Bool
not :: Bool -> Bool
. :: (b -> c) -> (a -> b) -> (a -> c)
any :: (a -> Bool) -> [a] -> Bool

(. any) :: (([a] -> Bool) -> c) -> (a -> Bool) -> c
(not .) :: (a -> Bool) -> a -> Bool
(not .) . any :: (a -> Bool) -> [a] -> Bool

polhcujo

polhcujo1#

下面是排列类型的一种方法:

(.)    :: (b             -> c          ) -> (a           -> b          ) -> a           -> c
(not .)       ::  ([d] -> Bool) -> [d] -> Bool
          any ::                                    (d -> Bool) -> [d] -> Bool
(not .) . any ::                                                                   (d -> Bool) -> [d] -> Bool

字符串
或者,从最后一个代码块开始:

(. any) :: (([a] -> Bool) -> c) -> (a -> Bool) -> c
(not .) :: (a -> Bool) -> a -> Bool
(not .) . any :: (a -> Bool) -> [a] -> Bool


让我们重命名a,使它们不同,并插入一些解释性空格。

(. any) :: (([a] -> Bool) -> c        ) -> (a -> Bool) -> c
(not .)        ::  (b   -> Bool) -> b -> Bool
(not .) . any  ::                                 (d -> Bool) -> [d] -> Bool


现在我们可以用c ~ b -> Boolb ~ [a]d ~ a来统一这些类型。(将~读作相等。=符号不用于此以避免语法冲突。)进行这些替换,我们有:

(. any) :: (([a] -> Bool) -> [a] -> Bool) -> (a -> Bool) -> [a] -> Bool
(not .)        ::  ([a] -> Bool) -> [a] -> Bool
(not .) . any  ::                                   (a -> Bool) -> [a] -> Bool

qgzx9mmu

qgzx9mmu2#

无论是左关联还是右关联,都没有关系,因为有括号,优先顺序是明确的。
例如,如果我们看一下:

all  = (and .) . map

字符串
显然,应用“外部”.时,(and .)作为第一操作数,map作为第二操作数。
(and .)是一个section of an infix operator [Haskell-wiki],所以它等价于(.) and
因此,它有助于首先以 * 规范形式 * 编写项目,其中:

all = (.1) ((.2) and) map


我在这里添加了(.1)(.2)以使区别更清楚。
首先我们将给予函数类型。我们使用不同的类型变量,所以这里的类型是:

(.1) :: (b -> c) -> ((a -> b) -> (a -> c))
(.2) :: (e -> f) -> ((d -> e) -> (d -> f))
and :: [Bool] -> Bool
map :: (g -> h) -> ([g] -> [h])


现在我们可以开始确定类型。我们可以首先确定((.2) and)的类型,因此使用(.2) allall作为参数。
因此,这意味着我们可以将类型确定为:

(.2)    :: (e      -> f   ) -> ((d -> e) -> (d -> f))
    and ::  [Bool] -> Bool


因此,这意味着f的类型与Bool相同,而e的类型为[Bool]。因此,这意味着(.2)专门用于:

(.2) :: ([Bool] -> Bool) -> ((d -> [Bool]) -> (d -> Bool))


因此((.2) and)的类型是(d -> [Bool]) -> (d -> Bool)
现在我们可以确定整个表达式的类型,因此(.1)带有参数((.2) and)map
因此,我们可以分析这一点:

(.1)               :: (b            -> c           ) -> ((a        -> b            ) -> (a -> c))
     (.2) and      ::  (d -> [Bool]) -> (d -> Bool)
               map ::                                     (g -> h) -> ([g] -> [h])


因此,这意味着bd -> [Bool]的类型相同,c等于d -> Boola等于g -> hb等于[g] -> [h]
因为我们知道b等于d -> [Bool][g] -> [h],所以我们知道h ~ BoolhBool是同一类型)和d ~ [g]
因此,这也意味着c ~ [g] -> Boola ~ g -> Bool
这意味着(.1) ((.2) and) map的结果是a -> c,因此(g -> Bool) -> ([g] -> Bool)或因此:

(.1) ((.2) and) map :: (g -> Bool) -> [g] -> Bool


我离开确定(not .) . any的类型与行使相同的程序。

sczxawaw

sczxawaw3#

我不想回答你关于你提供的解决方案如何工作的问题,我想指出的是,这个解决方案虽然很漂亮,但大多数人都不会写,而且很多人都不容易阅读它。相反,你可以先手写定义,然后寻找你可以抽象或分解的模式。

all, none, any :: (a -> Bool) -> [a] -> Bool
all f xs = and (map f xs)
any f xs = or (map f xs)
none f xs = not (any f xs)

字符串
最明显的事情就是注意这种模式:“给定两个函数和一个列表,运行一个函数,然后将结果交给另一个函数”。

comp2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
comp2 outer inner = \f xs -> outer (inner f xs)

all, none, any :: (a -> Bool) -> [a] -> Bool
all = comp2 and map
any = comp2 or map
none = comp2 not any


我在comp2的实现中使用了有点俗气的lambda,以说明它与all和friends的原始手写定义的匹配程度。
如果你真的对无点定义很感兴趣的话,你可能会意识到comp2可以被无点定义为comp2 = (.) . (.),你可以从那里得到“漂亮”的定义,但是没有必要去做这一步,或者在我的第一个代码块中的手写体定义之后的任何步骤:它们已经很清楚了。

pftdvrlh

pftdvrlh4#

知道了

(not .) . :: (a1 -> a2 -> Bool) -> (a1 -> a2 -> Bool)
any :: (a -> Bool) -> [a] -> Bool
(not .) . any :: (a -> Bool) -> [a] -> Bool

字符串

相关问题