1-----------------------------------------------------------------------------
3-- Module : Data.Algorithm.Diff
4-- Copyright : (c) Sterling Clover 2008
5-- License : BSD 3 Clause
6-- Maintainer : s.clover@gmail.com
7-- Stability : experimental
8-- Portability : portable
10-- This is an implementation of the O(ND) diff algorithm as described in
11-- \"An O(ND) Difference Algorithm and Its Variations (1986)\"
12-- <http://citeseer.ist.psu.edu/myers86ond.html>. It is O(mn) in space.
13-- The algorithm is the same one used by standared Unix diff.
14-- The assumption is that users of this library will want to diff over
15-- interesting things or peform interesting tasks with the results
16-- (given that, otherwise, they would simply use the standard Unix diff
17-- utility). Thus no attempt is made to present a fancier API to aid
18-- in doing standard and uninteresting things with the results.
19-----------------------------------------------------------------------------
21module Diff (DI(..), getDiff, getGroupedDiff) where
25-- | Difference Indicator. A value is either from the First list, the Second
27data DI = F | S | B deriving (Show, Eq)
29data DL = DL {poi::Int, poj::Int, path::[DI]} deriving (Show, Eq)
31instance Ord DL where x <= y = poi x <= poi y
33canDiag :: (Eq a) => [a] -> [a] -> Int -> Int -> (Int, Int) -> Bool
34canDiag as bs lena lenb = \(i,j) ->
35 if i < lena && j < lenb then arAs ! i == arBs ! j else False
36 where arAs = listArray (0,lena - 1) as
37 arBs = listArray (0,lenb - 1) bs
39chunk :: Int -> [a] -> [[a]]
40chunk x = unfoldr (\a -> case splitAt x a of ([],[]) -> Nothing; a' -> Just a')
42dstep :: ((Int,Int)->Bool) -> [DL] -> [DL]
43dstep cd dls = map maximum $ [hd]:(chunk 2 rst)
44 where (hd:rst) = concatMap extend dls
45 extend dl = let pdl = path dl
46 in [addsnake cd $ dl {poi=poi dl + 1, path=(F : pdl)},
47 addsnake cd $ dl {poj=poj dl + 1, path=(S : pdl)}]
49addsnake :: ((Int,Int)->Bool) -> DL -> DL
51 | cd (pi, pj) = addsnake cd $
52 dl {poi = pi + 1, poj = pj + 1, path=(B : path dl)}
54 where pi = poi dl; pj = poj dl
56lcs :: (Eq a) => [a] -> [a] -> [DI]
57lcs as bs = path . head . dropWhile (\dl -> poi dl /= lena || poj dl /= lenb) .
58 concat . iterate (dstep cd) . (:[]) . addsnake cd $
59 DL {poi=0,poj=0,path=[]}
60 where cd = canDiag as bs lena lenb
61 lena = length as; lenb = length bs
63-- | Takes two lists and returns a list indicating the differences
65getDiff :: (Eq t) => [t] -> [t] -> [(DI, t)]
66getDiff a b = markup a b . reverse $ lcs a b
67 where markup (x:xs) ys (F:ds) = (F, x) : markup xs ys ds
68 markup xs (y:ys) (S:ds) = (S, y) : markup xs ys ds
69 markup (x:xs) (_:ys) (B:ds) = (B, x) : markup xs ys ds
72-- | Takes two lists and returns a list indicating the differences
73-- between them, grouped into chunks.
74getGroupedDiff :: (Eq t) => [t] -> [t] -> [(DI, [t])]
75getGroupedDiff a b = map go . groupBy (\x y -> fst x == fst y) $ getDiff a b
76 where go ((d,x) : xs) = (d, x : map snd xs)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list