repeatedly applying a function until the result is stable

旧街凉风 提交于 2019-11-29 10:55:27

问题


I want to repeatedly apply a function simplify' until the result is "stable" (i.e. simplify'(x) == x):

simplify :: Expr -> Expr
simplify expr =
    let iterations = iterate simplify' expr
        neighbours = zip iterations (tail iterations)
        simplified = takeWhile (\(a, b) -> a /= b) neighbours
    in  snd $ last ((expr, expr) : simplified)

simplify' :: Expr -> Expr

This seems to be a common problem to me. Is there a more elegant solution?

Update: I found a much simpler solution, but I'm still looking for a more elegant solution :)

simplify expr =
    let next = simplify' expr
    in  if next == expr
        then expr
        else simplify next

回答1:


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) 



回答2:


A simplification of https://stackoverflow.com/a/7448190/1687259 '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).




回答3:


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



回答4:


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



回答5:


Below is one such implementation which can be used:

applyTill :: (a -> bool) -> (a -> a) -> a -> a
applyTill p f initial = head $ filter p $ scanl (\s e -> f s) initial [1..]

Example usage:

applyTill ( (==) stableExpr ) simplify' initExpr


来源:https://stackoverflow.com/questions/7442892/repeatedly-applying-a-function-until-the-result-is-stable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!