2016-10-12 23:34:12 +03:00
|
|
|
{-# LANGUAGE Strict #-}
|
2015-11-18 21:44:02 +03:00
|
|
|
module SES where
|
2015-11-18 18:11:05 +03:00
|
|
|
|
2015-12-01 00:17:24 +03:00
|
|
|
import qualified Data.Map as Map
|
2016-06-06 15:32:13 +03:00
|
|
|
import Patch
|
|
|
|
import Prologue
|
2016-06-07 05:13:00 +03:00
|
|
|
|
2015-11-18 18:11:05 +03:00
|
|
|
|
2016-06-03 16:27:33 +03:00
|
|
|
-- | Edit constructor for two terms, if comparable. Otherwise returns Nothing.
|
|
|
|
type Compare term edit = term -> term -> Maybe edit
|
2016-01-21 18:29:52 +03:00
|
|
|
|
2016-06-03 16:27:33 +03:00
|
|
|
-- | A function that computes the cost of an edit.
|
2016-08-18 17:52:49 +03:00
|
|
|
type Cost edit = edit -> Int
|
2015-11-18 21:28:07 +03:00
|
|
|
|
2016-01-21 18:29:52 +03:00
|
|
|
-- | Find the shortest edit script (diff) between two terms given a function to compute the cost.
|
2016-06-03 16:27:33 +03:00
|
|
|
ses :: Applicative edit => Compare term (edit (Patch term)) -> Cost (edit (Patch term)) -> [term] -> [term] -> [edit (Patch term)]
|
2016-07-12 16:51:41 +03:00
|
|
|
ses diffTerms cost as bs = fst <$> evalState diffState Map.empty where
|
|
|
|
diffState = diffAt diffTerms cost (0, 0) as bs
|
2015-12-01 00:02:37 +03:00
|
|
|
|
2016-01-21 18:29:52 +03:00
|
|
|
-- | Find the shortest edit script between two terms at a given vertex in the edit graph.
|
2016-08-18 17:52:49 +03:00
|
|
|
diffAt :: Applicative edit => Compare term (edit (Patch term)) -> Cost (edit (Patch term)) -> (Int, Int) -> [term] -> [term] -> State (Map.Map (Int, Int) [(edit (Patch term), Int)]) [(edit (Patch term), Int)]
|
2016-06-03 06:40:02 +03:00
|
|
|
diffAt diffTerms cost (i, j) as bs
|
|
|
|
| (a : as) <- as, (b : bs) <- bs = do
|
2015-12-01 00:55:22 +03:00
|
|
|
cachedDiffs <- get
|
|
|
|
case Map.lookup (i, j) cachedDiffs of
|
2016-05-26 19:58:04 +03:00
|
|
|
Just diffs -> pure diffs
|
2015-12-01 00:51:45 +03:00
|
|
|
Nothing -> do
|
2015-12-01 02:17:21 +03:00
|
|
|
down <- recur (i, succ j) as (b : bs)
|
|
|
|
right <- recur (succ i, j) (a : as) bs
|
2016-06-06 22:36:51 +03:00
|
|
|
nomination <- best <$> case diffTerms a b of
|
2015-12-01 02:17:21 +03:00
|
|
|
Just diff -> do
|
|
|
|
diagonal <- recur (succ i, succ j) as bs
|
2016-06-03 06:40:02 +03:00
|
|
|
pure [ delete a down, insert b right, consWithCost cost diff diagonal ]
|
|
|
|
Nothing -> pure [ delete a down, insert b right ]
|
2015-12-01 02:25:59 +03:00
|
|
|
cachedDiffs' <- get
|
|
|
|
put $ Map.insert (i, j) nomination cachedDiffs'
|
2016-05-26 19:58:04 +03:00
|
|
|
pure nomination
|
2016-06-06 15:31:53 +03:00
|
|
|
| null as = pure $ foldr insert [] bs
|
|
|
|
| null bs = pure $ foldr delete [] as
|
|
|
|
| otherwise = pure []
|
2015-12-01 00:57:23 +03:00
|
|
|
where
|
2016-08-04 19:51:36 +03:00
|
|
|
delete = consWithCost cost . deleting
|
|
|
|
insert = consWithCost cost . inserting
|
2015-12-24 09:08:53 +03:00
|
|
|
costOf [] = 0
|
|
|
|
costOf ((_, c) : _) = c
|
2015-12-24 09:09:40 +03:00
|
|
|
best = minimumBy (comparing costOf)
|
2015-12-01 01:25:18 +03:00
|
|
|
recur = diffAt diffTerms cost
|
2015-12-01 01:37:13 +03:00
|
|
|
|
2016-06-03 16:27:33 +03:00
|
|
|
-- | Prepend an edit script and the cumulative cost onto the edit script.
|
2016-08-18 17:52:49 +03:00
|
|
|
consWithCost :: Cost edit -> edit -> [(edit, Int)] -> [(edit, Int)]
|
2016-07-12 16:51:41 +03:00
|
|
|
consWithCost cost edit rest = (edit, cost edit + maybe 0 snd (fst <$> uncons rest)) : rest
|