以下是迄今为止我在实现 Левенште́йн 时使用“矩阵记忆”共同破解的内容。我现在正在尝试将 Haskell 用于几乎所有事情,以便我真正学习它。我还没有真正掌握的概念包括单子转换器、状态单子(正在研究它)和镜头。
import Data.Matrix
import Control.Monad.State
import Control.Applicative
type RecState = Int
-- Set up the first row
setLeftCol :: String -> Matrix Int -> Maybe (Matrix Int)
setLeftCol str mat = let strLength = length str + 1
in foldr helper (Just mat) [1..strLength]
where
helper :: Int -> Maybe (Matrix Int) -> Maybe (Matrix Int)
helper value matrixMon = (\m -> safeSet (value-1) (value,1) m) =<< matrixMon
-- Encapsulate a transposition in a Maybe context
transposeM :: Matrix a -> Maybe (Matrix a)
transposeM mat = Just (transpose mat)
-- Set up the first column
setTopRow :: String -> Matrix Int -> Maybe (Matrix Int)
setTopRow str mat = let mat' = return mat
in mat' >>= transposeM >>= (setLeftCol str) >>= transposeM
-- Generate coordinates
coords :: Int -> Int -> [(Int,Int)]
coords width height = [(x,y) | x <- [1..(width+1)], y <- [1..(height+1)]]
safeFst :: Maybe (Int,Int) -> Maybe Int
safeFst tuple = case tuple of
Just (x,y) -> Just x
Nothing -> Nothing
safeSnd :: Maybe (Int,Int) -> Maybe Int
safeSnd tuple = case tuple of
Just (x,y) -> Just y
Nothing -> Nothing
distance :: Matrix Int -> State RecState (Matrix Int)
distance matrix = do
index <- get
let coordinate = coordinates !! index
i = fst coordinate
j = snd coordinate
if index == size then
put matrix
return $ getElem i j matrix
else do
put (index + 1)
let ch1 = w1 !! (i - 1)
ch2 = w2 !! (j - 1)
cost = if ch1 /= ch2 then 1 else 0
entry1 = (getElem (i - 1) j matrix) + 1
entry2 = (getElem i (j - 1) matrix) + 1
entry3 = (getElem (i - 1) (j - 1) matrix) + cost
return $ distance $ setElem (minimum [entry1,entry2,entry3]) coordinate matrix
-- Compute the Levenshtein distance on two strings.
levenshtein :: String -> String -> Int
levenshtein "" "" = 0
levenshtein "" w2 = length w2
levenshtein w1 "" = length w1
levenshtein w1 w2 = let lenW1 = length w1
lenW2 = length w2
size = lenW1 * lenW2
matrix = Just $ zero (lenW1 + 1) (lenW2 + 1)
matrix' = matrix >>= setLeftCol w1 >>= setTopRow w2
coordinates = coords lenW1 lenW2
in execState (distance <$> matrix') (lenW1 + 2)
showResults :: Show r => r -> IO ()
showResults = putStrLn . show
showLevenshtein :: String -> String -> IO ()
showLevenshtein = showResults . levenshtein
我的第一个问题是如何组织distance函数levenshtein?我首先将它放在以 .where开头的行之后的一个子句中in execState...。但是,我发现在这个函数中既size不能访问也不能访问,因为它们是在.coordinatesletlevenshtein
也可以随意评论我在这里尝试过的任何其他想法。