repeatedly applying a function until the result is stable
simplify = until (\x -> simplify' x == x) simplify'
until
is a rather less-known Prelude function. (A small disadvantage is that this uses simplify'
about 2n times instead of about n.)
I think the clearest way, however, is your version modified to use guards and where:
simplify x | x == y = x
| otherwise = simplify y
where y = simplify' x
Yet another way:
until' :: (a -> Maybe a) -> a -> a
until' f x = maybe x (until' f) (f x)
simplify :: Integer -> Integer
simplify = until' $ \x -> let y = simplify' x in
if x==y then Nothing else Just y
Here's a slight generalization implemented with straightforward pattern matching and recursion. converge
searches through an infinite list, looking for two elements in a row which satisfy some predicate. It then returns the second one.
converge :: (a -> a -> Bool) -> [a] -> a
converge p (x:ys@(y:_))
| p x y = y
| otherwise = converge p ys
simplify = converge (==) . iterate simplify'
This makes it easy to for example use approximate equality for the convergence test.
sqrt x = converge (\x y -> abs (x - y) < 0.001) $ iterate sqrt' x
where sqrt' y = y - (y^2 - x) / (2*y)
import Data.List.HT (groupBy)
fst_stable = head . (!!1) . groupBy (/=)
-- x, f(x), f^2(x), etc.
mk_lst f x = let lst = x : (map f lst) in lst
iter f = fst_stable . mk_lst f
test1 = iter (+1) 1 -- doesn't terminate
test2 = iter id 1 -- returns 1
test3 = iter (`div` 2) 4 -- returns 0
A simplification of sdcvvc's code would be:
converge :: Eq a => (a -> a) -> a -> a
converge = until =<< ((==) =<<)
The functionality doesn't change. The function is handed to ((==) >>=)
, which given arguments (reduced) from converge and later until means that in each iteration it will check if applying current a
to f
, (f a == a)
.