неоднократно применяя функцию, пока результат не станет стабильным - PullRequest
25 голосов
/ 16 сентября 2011

Я хочу многократно применять функцию simplify' до тех пор, пока результат не станет "стабильным" (т.е. 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

Мне кажется, это общая проблема. Есть ли более элегантное решение?

Обновление: я нашел намного более простое решение, но я все еще ищу более элегантное решение:)

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

Ответы [ 5 ]

18 голосов
/ 16 сентября 2011

Вот небольшое обобщение, реализованное с помощью простого сопоставления с образцом и рекурсии.converge просматривает бесконечный список, ища два элемента в строке, которые удовлетворяют некоторому предикату.Затем возвращается второй.

converge :: (a -> a -> Bool) -> [a] -> a
converge p (x:ys@(y:_))
    | p x y     = y
    | otherwise = converge p ys

simplify = converge (==) . iterate simplify'

Это позволяет, например, легко использовать приблизительное равенство для теста сходимости.

sqrt x = converge (\x y -> abs (x - y) < 0.001) $ iterate sqrt' x
    where sqrt' y = y - (y^2 - x) / (2*y) 
17 голосов
/ 29 мая 2014

Упрощение кода https://stackoverflow.com/a/7448190/1687259 будет:

converge :: Eq a => (a -> a) -> a -> a
converge = until =<< ((==) =<<)

Функциональность не меняется.Функция передается в ((==) >>=), что дает аргументы (сокращенные) от сходящихся и более поздних до тех пор, пока не будет означать, что на каждой итерации будет проверяться, применяет ли текущий a к f, (f a == a).

9 голосов
/ 16 сентября 2011
simplify = until (\x -> simplify' x == x) simplify'

until - довольно малоизвестная функция Prelude. (Небольшой недостаток состоит в том, что при этом simplify' используется примерно в 2n раз вместо n).

Я думаю, что самый ясный способ - ваша версия модифицирована для использования охраны и где:

simplify x | x == y    = x
           | otherwise = simplify y
           where y = simplify' x

Еще один способ:

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
1 голос
/ 17 сентября 2011
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
0 голосов
/ 16 сентября 2011

Ниже приведена одна такая реализация, которую можно использовать:

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

Пример использования:

applyTill ( (==) stableExpr ) simplify' initExpr
...