Tho*_*mas 7 haskell functional-programming function-composition
我试图通过Haskell理解函数式编程,并且我在处理函数组合时遇到了很多麻烦.
其实我有这两个功能:
add:: Integer -> Integer -> Integer
add x y = x + y
sub:: Integer -> Integer -> Integer
sub x y = x - y
Run Code Online (Sandbox Code Playgroud)
我希望能够撰写它们.它没有任何意义,但这是一个学习目标.
我尝试过的:
foo:: (Integer -> Integer) -> (Integer -> Integer) -> Integer
foo = add . sub
Run Code Online (Sandbox Code Playgroud)
我明白了:
Haskell使用只有一个args的函数,因此我们返回一个新函数,在每次执行函数后执行.
所以第一个Integer
是param类型,而第二个是生成函数的返回类型,必须添加第二个数字.
这将返回另一个函数(sub
),它将产生相同的流程(返回带有参数等的函数...)
我对吗 ?
这是我的实际错误代码:
src\Main.hs:23:7:
Couldn't match type `Integer' with `Integer -> Integer'
Expected type: Integer -> (Integer -> Integer) -> Integer
Actual type: Integer -> Integer -> Integer
In the first argument of `(.)', namely `add'
In the expression: add . sub
src\Main.hs:23:13:
Couldn't match type `Integer -> Integer' with `Integer'
Expected type: (Integer -> Integer) -> Integer
Actual type: Integer -> Integer -> Integer
Probable cause: `sub' is applied to too few arguments
In the second argument of `(.)', namely `sub'
In the expression: add . sub
Run Code Online (Sandbox Code Playgroud)
我不知道我做错了什么.
你能帮我理解一下这个错误,以便找到解决方案吗?
给定一个功能
add :: Integer -> Integer -> Integer
Run Code Online (Sandbox Code Playgroud)
记住(正如你在" 我理解的内容"部分中指出的那样),->
类型签名与右侧相关联,即上述类型与
add :: Integer -> (Integer -> Integer)
Run Code Online (Sandbox Code Playgroud)
现在,考虑以下类型(.)
:
(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)
这意味着在表达式中
(.) add
Run Code Online (Sandbox Code Playgroud)
该b
在的类型(.)
IS Integer
和c
对应于Integer -> Integer
.写这个的另一种方法是
b ~ Integer
c ~ Integer -> Integer
Run Code Online (Sandbox Code Playgroud)
所以我们得到了
(.) add :: (a -> Integer) -> a -> (Integer -> Integer)
Run Code Online (Sandbox Code Playgroud)
如果你现在申请(.) add
到sub
的是,编译器会a -> Integer
无法进行匹配Integer -> Integer -> Integer
.
我怀疑你可能希望组合有三个参数:两个要应用sub
,然后结果与第三个参数一起传递给add
.因此,构成这两个函数的可能定义是
foo :: (Integer -> Integer -> Integer) -> (Integer -> Integer -> Integer) -> Integer -> Integer -> Integer
foo f g x y = f (g x y) y
Run Code Online (Sandbox Code Playgroud)
对于它的价值,存在一个相关的问题:用一个参数函数组成一个两个参数函数,例如组合
我希望能够撰写它们.它没有任何意义,但这是一个学习目标.
这实际上是问题所在.你想怎么写他们?让我们看看一些可能的成分:
foo x y = sub x (add x y) -- x + y - x = y
foo x y = sub y (add x y) -- x + y - y = x
foo x y = sub x (add y y) -- 2 * y - x
foo x y = sub y (add y y) -- 2 * y - y = y
foo x y = sub y (sub y (add x x)) -- 2 * x - 2 * y
Run Code Online (Sandbox Code Playgroud)
话虽这么说,让我们通过检查手中的类型来检查类型错误:
type I = Integer -- otherwise the lines are going to be very long
(.) :: (b -> c ) -> (a -> b ) -> a -> c
add :: I -> (I -> I)
sub :: I -> (I -> I)
-- |||||||||||||
(.) add :: (a -> I ) -> a -> (I -> I)
-- ^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
如您所见,(.) add
已经强制要求其他函数只能具有a -> Integer
任意类型a
.但是sub
类型是Integer -> (Integer -> Integer)
(记住,(->)
是正确的关联).
现在,你能做些什么才能真正解决这个问题?首先,让我们检查您提出的类型foo
:
foo :: (Integer -> Integer) -> (Integer -> Integer) -> Integer
Run Code Online (Sandbox Code Playgroud)
这实际上是一种非常有趣的功能.你怎么会得到你的结果?你手头有两个函数,但没有值:
> foo f g =
Run Code Online (Sandbox Code Playgroud)
您可以使用其中一个函数的固定点解决此问题,然后应用另一个函数:
> let x = f x in g x
>
> example = foo (const 12) (+1) -- returns 13
Run Code Online (Sandbox Code Playgroud)
但这不是你的意思,对吗?在这一点上,考虑你的作品的语义是非常重要的.由于这些不清楚,你不能写一般的方法来组合这两个功能.
但是,如果你真正的意思
foo :: Integer -> Integer -> Integer -> Integer
foo x y z = add (sub x y) z
Run Code Online (Sandbox Code Playgroud)
然后就可以了
foo = (add .) . sub
Run Code Online (Sandbox Code Playgroud)
以来
(.) add :: (a -> I) -> a -> (I -> I)
(.) ((.) add) :: (a -> b -> Integer) -> a -> b -> Integer -> Integer
Run Code Online (Sandbox Code Playgroud)
但是(add .) . sub
再也不容易了.foo
如果这种功能是你最初的目标,你最好写一个明确的定义.