我有一些代码可以反复接近一个解决方案,它实际上做的并不重要,但是它通过改变mg来实现r'== rt(m猜测,从4.0开始因为我“知道”应该在球场)。
solve_m f ar st qt = solve_m' f ar st qt 4.0
where
solve_m' f ar st qt mg
| rd > precis = f' (mg - sf)
| rd < (-precis) = f' (mg + sf)
| otherwise = mg
where
f' = solve_m' f ar st qt
rt = st + qt
r' = f st ar mg
rd = rt - r'
sf = abs(rd)
我希望能够做的是计算周期数,我知道正确的方法是使用State monad,但是最优雅的方法是将put / get放入像这样的函数中这个?让f'成为阻止?或者只是添加一个计数器solve_m'并返回(counter,mg)?
谢谢!
编辑:这似乎基本上是我想要的,没有必要的Monads:
solve_m f ar st qt = (last (series), length(series))
where
series = takeWhile termPred (iterate solve_m' 4.0)
termPred m' = (abs (rt - (f st ar m'))) > precis
rt = st + qt
solve_m' mg
| rt > r' = (mg - sf)
| rt < r' = (mg + sf)
where
r' = f st ar mg
rd = rt - r'
sf = abs(rd)
仍然看起来有点乱(重复的代码),但我会把它整理一下......这将使我接受的结果将是它将取代的代码的1/10000次迭代!
答案 0 :(得分:5)
在不查看算法的情况下,执行此操作的一般方法是将终止条件与迭代算法分开:
terminationPred :: a -> Bool
algorithm :: a -> a
然后使用iterate和takeWhile:
itermediates = takeWhile (not . terminationPred) . iterate algorithm
resultAndRecursions :: a -> (a, Int)
resultAndRecursions a = (last (intermediates a), length (intermediates a) - 1)
-- you'd want to make your own safe function here, not use last and length
或展开:
intermediates = unfoldr op
where
op a | terminationPred a = Nothing
| otherwise = let a' = algorithm a
in Just (a', a')
编辑:还注意到这两个中间体略有不同,因为第一个维持基本情况(输入a
,因此- 1
)而第二个没有,因此会有微小的差异在补充resultAndRecursions
。
答案 1 :(得分:4)
首先,您可以删除solve_m'
的大多数参数:它们不会在递归调用中更改,solve_m
的参数在where
的范围内}子句。这也使f'
函数变得不必要。
solve_m f ar st qt = solve_m' 4.0
where
solve_m' mg
| rd > precis = solve_m' (mg - sf)
| rd < (-precis) = solve_m' (mg + sf)
| otherwise = mg
where
rt = st + qt
r' = f st ar mg
rd = rt - r'
sf = abs(rd)
现在,solve_m'
具有类型Double -> Double
,因为它所做的只是执行下一次迭代,然后以递归方式完成或调用自身。碰巧的是,标准库包含一个名为iterate
且函数为(a -> a) -> a -> [a]
的函数,它接受这样的函数,生成迭代中每个步骤的(可能是无限的)列表。当然,所需的递归调用的数量正是结果列表的长度。在我的回答中产生了一个令人尴尬的错误。
iterate
实际上做的是产生无限列表,在这种情况下,无休止地重复“最终”结果的副本。不是你想要的。我可能在考虑unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
。
另一个选项 - 我实际上更喜欢 - 将删除检查答案足够接近的守卫并且毕竟使用iterate
,产生无限的新近似列表,然后消耗结果列出比较相邻元素,看看你有多接近。我给出了一些示例代码,但考虑到之前可能不明智的错误。
编辑:好的,为了完整起见,这里有几个简单的例子:
使用iterate
和takeWhile
:
solve_m_iter f ar st qt = takeWhile notDoneYet $ iterate nextApprox 4.0
where rd mg = st + qt - f st ar mg
notDoneYet mg = abs (rd mg) > precis
nextApprox mg | rd mg > precis = mg - abs (rd mg)
| rd mg < -precis = mg + abs (rd mg)
使用unfoldr
:
solve_m_unfold f ar st qt = unfoldr nextApprox
where nextApprox mg | rd > precis = keep $ mg - abs rd
| rd < -precis = keep $ mg + abs rd
| otherwise = Nothing
where rd = st + qt - f st ar mg
keep x = Just (x, x)
一个稍微好一点的函数来获得结果而不遍历列表两次:
getResult = foldl (\(n, _) x -> (n + 1, x)) (0, 4.0)
绝对快速而肮脏的代码,但希望有用。