packages feed

levenshtein 0.1.0.0 → 0.1.1.0

raw patch · 3 files changed

+40/−14 lines, 3 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Data.Foldable.Levenshtein: type Edits a = [Edit a]
- Data.Foldable.Levenshtein: applyEdits :: Eq a => [Edit a] -> [a] -> Maybe [a]
+ Data.Foldable.Levenshtein: applyEdits :: Eq a => Edits a -> [a] -> Maybe [a]
- Data.Foldable.Levenshtein: genericLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: genericLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: genericLevenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: genericLevenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: genericReversedLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: genericReversedLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: genericReversedLevenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: genericReversedLevenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> (a -> b) -> (a -> b) -> (a -> a -> b) -> f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: levenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: levenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: levenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: levenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: reversedLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: reversedLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b) => f a -> g a -> (b, Edits a)
- Data.Foldable.Levenshtein: reversedLevenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> f a -> g a -> (b, [Edit a])
+ Data.Foldable.Levenshtein: reversedLevenshtein' :: (Foldable f, Foldable g, Num b, Ord b) => (a -> a -> Bool) -> f a -> g a -> (b, Edits a)

Files

levenshtein.cabal view
@@ -1,5 +1,5 @@ name:                levenshtein-version:             0.1.0.0+version:             0.1.1.0 synopsis:            Calculate the edit distance between two foldables. description:   A package to determine the edit distance between two 'Foldable's.
src/Data/Foldable/Levenshtein.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, DeriveTraversable, Safe #-}+{-# LANGUAGE CPP, DeriveDataTypeable, DeriveGeneric, DeriveTraversable, Safe #-}  {-| Module      : Data.Foldable.Levenshtein@@ -21,7 +21,7 @@     -- * Obtain the Levenshtein distance together with a reversed path of 'Edit's   , genericReversedLevenshtein, genericReversedLevenshtein', reversedLevenshtein, reversedLevenshtein'     -- * Data type to present modifications from one 'Foldable' to the other.a-  , Edit(Add, Rem, Copy, Swap), applyEdits+  , Edit(Add, Rem, Copy, Swap), Edits, applyEdits   ) where  import Control.Arrow(second)@@ -33,6 +33,9 @@ import Data.Functor.Classes(Eq1(liftEq), Ord1(liftCompare)) import Data.Hashable(Hashable) import Data.Hashable.Lifted(Hashable1)+#if __GLASGOW_HASKELL__ < 803+import Data.Semigroup(Semigroup((<>)))+#endif  import GHC.Generics(Generic, Generic1) @@ -105,12 +108,15 @@           go _ (Copy _) = GT           go (Swap xa ya) (Swap xb yb) = cmp xa xb <> cmp ya yb +-- | A type alias for a /list/ of 'Edit's.+type Edits a = [Edit a]+ -- | Apply the given list of 'Edit's to the given list. -- If the 'Edit's make sense, it returns the result wrapped -- in a 'Just', if a check with the item that is removed/replaced -- fails, the function will return 'Nothing'. applyEdits :: Eq a-  => [Edit a]  -- ^ The given list of 'Edit's to apply to the given list.+  => Edits a  -- ^ The given list of 'Edit's to apply to the given list.   -> [a]  -- ^ The list of items to edit with the given 'Edit's.   -> Maybe [a]  -- ^ The modified list, given the checks hold about what item to remove/replace wrapped in a 'Just'; 'Nothing' otherwise. applyEdits [] ys = Just ys@@ -138,7 +144,7 @@ levenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b)   => f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ The edit distance between the two 'Foldable's.+  -> (b, Edits a)  -- ^ The edit distance between the two 'Foldable's. levenshtein = levenshtein' (==)  -- | Determine the edit distance together with the steps to transform the first 'Foldable'@@ -149,7 +155,7 @@   => (a -> a -> Bool)  -- ^ The given equivalence relation to work with.   -> f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ The edit distance between the two 'Foldable's together with a list of 'Edit's to transform the first 'Foldable' to the second one.+  -> (b, Edits a)  -- ^ The edit distance between the two 'Foldable's together with a list of 'Edit's to transform the first 'Foldable' to the second one. levenshtein' = _addDefaults . genericLevenshtein'  -- | Determine the edit distance together with the steps to transform the first 'Foldable'@@ -159,7 +165,7 @@ reversedLevenshtein :: (Foldable f, Foldable g, Eq a, Num b, Ord b)   => f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ The edit distance between the two 'Foldable's together with the 'Edit's to make to convert the first sequence into the second.+  -> (b, Edits a)  -- ^ The edit distance between the two 'Foldable's together with the 'Edit's to make to convert the first sequence into the second. reversedLevenshtein = reversedLevenshtein' (==)  -- | Determine the edit distance together with the steps to transform the first 'Foldable'@@ -170,7 +176,7 @@   => (a -> a -> Bool)  -- ^ The given equivalence relation to work with.   -> f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ The edit distance between the two 'Foldable's together with a reversed list of 'Edit's to transform the original sequence into the target sequence.+  -> (b, Edits a)  -- ^ The edit distance between the two 'Foldable's together with a reversed list of 'Edit's to transform the original sequence into the target sequence. reversedLevenshtein' = _addDefaults . genericReversedLevenshtein'  -- | Determine the edit distance to transform the first 'Foldable' (as list)@@ -234,7 +240,7 @@   -> (a -> a -> b)  -- ^ The cost that it takes to replace an item of the first parameter with one of the second parameter. The return value should be positive.   -> f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /normal/ order as second item to transform the first sequence to the second one.+  -> (b, Edits a)  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /normal/ order as second item to transform the first sequence to the second one. genericLevenshtein' eq ad rm sw xs' = second reverse . genericReversedLevenshtein' eq ad rm sw xs'  -- | A function to determine the /Levenshtein distance/ together with a list of 'Edit's@@ -248,7 +254,7 @@   -> (a -> a -> b)  -- ^ The cost that it takes to replace an item of the first parameter with one of the second parameter. The return value should be positive.   -> f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /normal/ order as second item to transform the first sequence to the second one.+  -> (b, Edits a)  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /normal/ order as second item to transform the first sequence to the second one. genericLevenshtein = genericLevenshtein' (==)  -- | A function to determine the /Levenshtein distance/ together with a list of 'Edit's@@ -263,7 +269,7 @@   -> (a -> a -> b)  -- ^ The cost that it takes to replace an item of the first parameter with one of the second parameter. The return value should be positive.   -> f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /reversed/ order as second item to transform the first 'Foldable' (as list) to the second 'Foldable' (as list).+  -> (b, Edits a)  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /reversed/ order as second item to transform the first 'Foldable' (as list) to the second 'Foldable' (as list). genericReversedLevenshtein' eq ad rm sw xs' ys' = last (foldl (nextRow tl) row0 xs')   where     row0 = scanl (\(w, is) i -> (w+ad i, Add i: is)) (0, []) tl@@ -290,5 +296,5 @@   -> (a -> a -> b)  -- ^ The cost that it takes to replace an item of the first parameter with one of the second parameter. The return value should be positive.   -> f a  -- ^ The given original sequence.   -> g a  -- ^ The given target sequence.-  -> (b, [Edit a])  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /reversed/ order as second item to transform the first 'Foldable' (as list) to the second 'Foldable' (as list).+  -> (b, Edits a)  -- ^ A 2-tuple with the edit score as first item, and a list of modifications in /reversed/ order as second item to transform the first 'Foldable' (as list) to the second 'Foldable' (as list). genericReversedLevenshtein = genericReversedLevenshtein' (==)
test/Data/Foldable/LevenshteinSpec.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE ExistentialQuantification, RankNTypes, TypeApplications #-}+{-# LANGUAGE ExistentialQuantification, RankNTypes, ScopedTypeVariables, TypeApplications #-}  module Data.Foldable.LevenshteinSpec (     spec   ) where -import Data.Foldable.Levenshtein(applyEdits, levenshtein, levenshteinDistance)+import Data.Foldable.Levenshtein(Edits, applyEdits, genericLevenshteinDistance, genericLevenshteinDistance', levenshteinDistance, levenshteinDistance', genericLevenshtein, genericLevenshtein', levenshtein, levenshtein', genericReversedLevenshtein, genericReversedLevenshtein', reversedLevenshtein, reversedLevenshtein')  import Test.Hspec(Spec, it) import Test.QuickCheck(maxSuccess, property, quickCheckWith, stdArgs)@@ -20,18 +20,38 @@   it "test triangle inequality" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (triangleInequality @ Int)))   it "test applying edits" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (testApplyingEdits @ Int)))   it "Hamming distance bound" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (hammingDistanceBound @ Int)))+  it "Test if all distance metrics report the same" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (allDistancesSame @ Int)))   it "lowerbound string difference" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (lowerBoundLengthDiff @ Char)))   it "upperbound largest string" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (upperBoundLengthDiff @ Char)))   it "if zero then same list" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (ifZeroThenSame @ Char)))   it "test triangle inequality" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (triangleInequality @ Char)))   it "test applying edits" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (testApplyingEdits @ Char)))   it "Hamming distance bound" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (hammingDistanceBound @ Char)))+  it "Test if all distance metrics report the same" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (allDistancesSame @ Char)))   it "lowerbound string difference" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (lowerBoundLengthDiff @ Bool)))   it "upperbound largest string" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (upperBoundLengthDiff @ Bool)))   it "if zero then same list" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (ifZeroThenSame @ Bool)))   it "test triangle inequality" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (triangleInequality @ Bool)))   it "test applying edits" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (testApplyingEdits @ Bool)))   it "Hamming distance bound" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (hammingDistanceBound @ Bool)))+  it "Test if all distance metrics report the same" (quickCheckWith stdArgs { maxSuccess = ntimes } (property (allDistancesSame @ Bool)))++allDistancesSame :: forall a . Eq a => [a] -> [a] -> Bool+allDistancesSame xs ys = da == db && db == dc && dc == dd && dd == de && de == df && df == dg && dg == dh+                      && dh == di && di == dj && dj == dk && dk == dl && efa == efb && efb == efc+                      && efc == efd && reverse efd == era && era == erb && erb == erc && erc == erd+  where da = genericLevenshteinDistance (const 1) (const 1) (const (const 1)) xs ys :: Int+        db = genericLevenshteinDistance' (==) (const 1) (const 1) (const (const 1)) xs ys :: Int+        dc = levenshteinDistance xs ys :: Int+        dd = levenshteinDistance' (==) xs ys :: Int+        (de, efa) = genericLevenshtein (const 1) (const 1) (const (const 1)) xs ys :: (Int, Edits a)+        (df, efb) = genericLevenshtein' (==) (const 1) (const 1) (const (const 1)) xs ys :: (Int, Edits a)+        (dg, efc) = levenshtein xs ys :: (Int, Edits a)+        (dh, efd) = levenshtein' (==) xs ys :: (Int, Edits a)+        (di, era) = genericReversedLevenshtein (const 1) (const 1) (const (const 1)) xs ys :: (Int, Edits a)+        (dj, erb) = genericReversedLevenshtein' (==) (const 1) (const 1) (const (const 1)) xs ys :: (Int, Edits a)+        (dk, erc) = reversedLevenshtein xs ys :: (Int, Edits a)+        (dl, erd) = reversedLevenshtein' (==) xs ys :: (Int, Edits a)   lowerBoundLengthDiff :: forall a . Eq a => [a] -> [a] -> Bool