我有以下代码,基于这个公式实现逆函数计算:
derivation :: (Fractional a) => (a -> a) -> (a -> a)
derivation f = \ x -> ( ( f (x + dx) - f (x) ) / dx ) where dx = 0.1
evalA k f
| k == 0 = \x -> x
| otherwise = \x -> (derivation (evalA (k-1) f) x) / (derivation f x)
inverseFun f x =
let
x0 = 3.0
eps = 0.001
iter k prev sum =
let
elemA = evalA k f x0
elemB = prev * (x - (f x0)) / (if k == 0 then 1 else k)
newItem = elemA * elemB
in
if abs (newItem) < eps
then sum
else iter (k + 1) elemB (sum + newItem)
in
iter 0 1.0 0.0
f1 = \x -> 1.0 * x * x
main = do
print $ inverseFun f1 2.5
我需要通过在evalA
内移动inverseFun
和存储上一步计算A'n / F'来优化它,以便在下一次迭代中重复使用 。据我了解,每次evalA
返回某种功能,然后 x 适用于声明elemA
之前。
如何转换我的evalA
或重写它以存储以前的结果(显然将这些结果传递到iter
)?
请注意,如果此计算不太精确,则需要选择良好的x0
和eps
。我的主要问题是lambda转换。
答案 0 :(得分:3)
如果您更改inverseFun
的定义,使(if k == 0 then 1 else k)
改为fromIntegral (if k == 0 then 1 :: Int else k)
,那么您可以为所有功能提供类型签名:
derivation :: (Fractional a) => (a -> a) -> a -> a
evalA :: (Fractional a) => Int -> (a -> a) -> a -> a
inverseFun :: (Fractional a, Ord a) => (a -> a) -> a -> a
f1 :: (Fractional a) => a -> a
这当然有帮助。
这对我的问题解决方案非常重要,因为我们需要k
成为Int
,并且您已将其用作Fractional a => a
。 fromIntegral
修复了这个问题,但需要知道它是Int
,所以我只是添加了内联类型签名来帮助编译器。
由于您的功能仅取决于之前的单个值,因此您可以使用Prelude
iterate :: (a -> a) -> a -> [a]
中的方便朋友。这反复应用函数,产生无限的值列表。然后我们可以在任何时候对其进行索引以获得所需的结果(这就是为什么k
和Int
很重要!)。
我们的功能看起来像
evalA :: Fractional a => Int -> (a -> a) -> a -> a
evalA k f = iterate go id !! k
where
go = ???
此处id
与\x -> x
的基本情况相同,只是更短且具有更多优化规则。它用作生成此列表的初始值。要实现go
,实际计算,我们需要它接受先前的结果作为其参数:
where
go prev = \x -> derivation prev x / derivation f x
但这被认为是&#34;风格不佳&#34;按hlint
,因此建议将其转换为
where
go prev x = derivation prev x / derivation f x
那就是它!我测试了它,并为您的示例输入得到了完全相同的结果。可以查看完整代码here。