我通过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
字符串
(.)
是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
型
4条答案
按热度按时间polhcujo1#
下面是排列类型的一种方法:
字符串
或者,从最后一个代码块开始:
型
让我们重命名
a
,使它们不同,并插入一些解释性空格。型
现在我们可以用
c ~ b -> Bool
、b ~ [a]
和d ~ a
来统一这些类型。(将~
读作相等。=
符号不用于此以避免语法冲突。)进行这些替换,我们有:型
qgzx9mmu2#
无论是左关联还是右关联,都没有关系,因为有括号,优先顺序是明确的。
例如,如果我们看一下:
字符串
显然,应用“外部”
.
时,(and .)
作为第一操作数,map
作为第二操作数。(and .)
是一个section of an infix operator [Haskell-wiki],所以它等价于(.) and
。因此,它有助于首先以 * 规范形式 * 编写项目,其中:
型
我在这里添加了
(.1)
和(.2)
以使区别更清楚。首先我们将给予函数类型。我们使用不同的类型变量,所以这里的类型是:
型
现在我们可以开始确定类型。我们可以首先确定
((.2) and)
的类型,因此使用(.2) all
和all
作为参数。因此,这意味着我们可以将类型确定为:
型
因此,这意味着
f
的类型与Bool
相同,而e
的类型为[Bool]
。因此,这意味着(.2)
专门用于:型
因此
((.2) and)
的类型是(d -> [Bool]) -> (d -> Bool)
。现在我们可以确定整个表达式的类型,因此
(.1)
带有参数((.2) and)
和map
。因此,我们可以分析这一点:
型
因此,这意味着
b
与d -> [Bool]
的类型相同,c
等于d -> Bool
,a
等于g -> h
,b
等于[g] -> [h]
。因为我们知道
b
等于d -> [Bool]
和[g] -> [h]
,所以我们知道h ~ Bool
(h
和Bool
是同一类型)和d ~ [g]
。因此,这也意味着
c ~ [g] -> Bool
和a ~ g -> Bool
。这意味着
(.1) ((.2) and) map
的结果是a -> c
,因此(g -> Bool) -> ([g] -> Bool)
或因此:型
我离开确定
(not .) . any
的类型与行使相同的程序。sczxawaw3#
我不想回答你关于你提供的解决方案如何工作的问题,我想指出的是,这个解决方案虽然很漂亮,但大多数人都不会写,而且很多人都不容易阅读它。相反,你可以先手写定义,然后寻找你可以抽象或分解的模式。
字符串
最明显的事情就是注意这种模式:“给定两个函数和一个列表,运行一个函数,然后将结果交给另一个函数”。
型
我在
comp2
的实现中使用了有点俗气的lambda,以说明它与all
和friends的原始手写定义的匹配程度。如果你真的对无点定义很感兴趣的话,你可能会意识到
comp2
可以被无点定义为comp2 = (.) . (.)
,你可以从那里得到“漂亮”的定义,但是没有必要去做这一步,或者在我的第一个代码块中的手写体定义之后的任何步骤:它们已经很清楚了。pftdvrlh4#
知道了
字符串