我正在尝试定义一个函数,该函数将采用Double -> Double
函数并返回其数学导数。我尝试过以下操作:
der :: (Double -> Double) -> (Double -> Double)
der f
| f == exp = exp
| otherwise = undefined
但Haskell不支持==
Double -> Double
值。我在Haskell中想做的事情是不可能的吗?
答案 0 :(得分:23)
是的,你想要做的事情在Haskell中是不可能的,并且一般来说:决定两个函数对于所有可能的输入是否相等(不仅检查每个输入值,如果可能的话)等同于解决停止问题
但是,在您的特定情况下,您可以使用模拟Double
的自定义类型(即具有相同的实例,因此可以代替它使用)来绕过它,而不是评估为一个数字,它构造函数所做操作的抽象表示。 Expr
代表数学函数定义f(x) = ...
的右侧。
data Expr = X | Const Double |
Add Expr Expr | Mult Expr Expr |
Negate Expr | Inverse Expr |
Exp Expr | Log Expr | Sin Expr | ...
deriving (Show, Eq)
instance Num Expr where
(+) = Add
(*) = Mult
...
instance Fractional Expr where
recip = Inverse
...
instance Floating Expr where
pi = Const pi
exp = Exp
log = Log
sin = Sin
...
然后,使用rank-2 types,您可以定义转换函数,这些转换函数可以在采用任何Floating
和Expr
s的函数之间进行转换:
{-# LANGUAGE Rank2Types #-}
fromFunction :: (forall a. Floating a => (a -> a)) -> Expr
fromFunction f = f X
toFunction :: Expr -> (Double -> Double)
toFunction X = \x -> x
toFunction (Const a) = const a
toFunction (Add a b) = \x -> (toFunction a x) + (toFunction b x)
...
您还可以定义区分表达式的函数diff :: Expr -> Expr
:
diff X = Const 1
diff (Const _) = Const 0
diff (Add a b) = Add (diff a) (diff b)
diff (Exp a) = Mult (diff a) (Exp a)
...
拥有所有这些部分应该意味着你可以区分(某些)功能,例如
f x = sin x + cos x * exp x
f' = toFunction . diff . fromFunction $ f
<强>注意事项:强>
Eq
定义一个完整的Expr
实例很棘手(它等同于Halting问题,因为它基本上是在询问两个函数是否相等),答案 1 :(得分:11)
通常不可能测试函数是否相等,因为函数相等应该是扩展的,即如果它们为所有参数提供相同的结果,则两个函数是相等的。
但是还有其他方法可以在Haskell中定义使用不同类型的衍生物。例如,Automatic Differentiation,simpler version of AD。