Haskell:二元函数求偏导数
问题
对于已知的二元函数f = f(x, y)
,且有一个对一元函数求导的函数diff = diff(f)
可用。
问题:如何利用diff
函数求f
函数对于第一个、第二个参数的偏导数?
解答
首先确定上面提到函数的函数类型:
f :: Double -> Double -> Double
diff :: (Double -> Double -> Double) -> Double -> Double -> Double
-- (只考虑最简单的情况。因为这篇博文关注点不在实用性。)
我们用partial
函数来解决问题,设计其类型如下:
partial :: Bool -> (Double -> Double -> Double) -> Double -> Double -> Double
partial False f
返回一个二元函数,为f
对第一个参数求导之后得到的函数;partial True f
为f
对第二个参数求导之后得到的函数。
用lambda演算来固定函数的另一个参数,很容易地描述partial
:
partial False f x0 y0 = (diff (\x -> f x y0)) x0
partial True f x0 y0 = (diff (\y -> f x0 y)) y0
以partial False f
为例,解释上述的声明:通过传入参数y0
将y
固定,此时\x -> f x y0
是一个一元函数,对其求导得到导函数,再代入x0
的值确定函数值。partial True f
也类似。
对上面的描述进行lambda化,得到函数定义(为了方便看,加了括号):
partial False f = \x0 -> (\y0 -> (diff (\x -> f x y0) x0))
partial True f = \x0 -> (\y0 -> (diff (\y -> f x0 y) y0))
思考1:化简
partial True f
= \x0 -> (\y0 -> (diff (\y -> f x0 y) y0))
= \x0 -> (diff (\y -> f x0 y))
= \x0 -> (diff (f x0))
= \x0 -> (diff . f) x0
= diff . f
原本以为很有启发性,可能可以和Zipper和类型求导关联起来,然而推出来后发现只是一个简单的函数复合:f x
提供了一个一元函数,而diff
对这个一元函数进行了求导,并没有什么深刻的本质。顿时觉得没什么意思了。
思考2:换元
记rev_xy
函数:
rev_xy :: (Double -> Double -> Double) -> Double -> Double -> Double
rev_xy f = \y -> \x -> f x y
-- rev_xy f y x = f x y
则有
partial False f
= rev_xy (partial True (rev_xy f))
= rev_xy (diff . (rev_xy f))
依然没有什么启发性的东西。