一昨日のお題を、Haskell でやってみた。
module Main where import System.Environment (getArgs) mValue :: [[a]] -> Int -> Int -> a mValue m i j = (m !! i) !! j mReplace :: [[a]] -> Int -> Int -> a -> [[a]] mReplace m i j v = bi ++ [bj ++ [v] ++ aj] ++ ai where bi = take i m ji = m !! i ai = drop (i + 1) m bj = take j ji aj = drop (j + 1) ji ldInit :: String -> String -> [[Int]] ldInit s1 s2 = [[0 | j <- [0..n]] | i <- [0..m]] where m = length s1 n = length s2 ldBuild :: ([[Int]], String, String) -> (Int, Int) -> ([[Int]], String, String) ldBuild (m, s1, s2) (i, j) = (mReplace m i j v, s1, s2) where v = ldCalc m s1 s2 i j ldCalc :: [[Int]] -> String -> String -> Int -> Int -> Int ldCalc m s1 s2 i j = if i == 0 && j == 0 then 0 else if i == 0 then j else if j == 0 then i else let x = if (s1 !! (i-1)) == (s2 !! (j-1)) then 0 else 1 in minimum [(mValue m i (j-1)) + 1, (mValue m (i-1) j) + 1, (mValue m (i-1) (j-1)) + x] levenshteinDistance :: String -> String -> Int levenshteinDistance s1 s2 = mValue ldM (length s1) (length s2) where m = ldInit s1 s2 ij = [(i, j) | i <- [0..(length s1)], j <- [0..(length s2)]] (ldM, _, _) = foldl ldBuild (m, s1, s2) ij main :: IO () main = do args <- getArgs let s1 = args !! 0 let s2 = args !! 1 print $ levenshteinDistance s1 s2
0 で初期化した二次元リスト(マトリックス)から foldl を使ってレーベンシュタイン距離のマトリックスを作る、というアイデアは出たんだけど、マトリックスを 0 で初期化する(ldInit)のと要素を置き換えたマトリックスを作る(mReplace)のがなかなか難産だった。結果として ldInit はリスト内包表記を使って簡潔にかけたけど、mReplace のほうはなんか泥臭くなってしまった。もっとエレガントにいかないもんだろうか。 実行例:
takatoh@nightschool $ runhaskell ld.hs apple play 4 takatoh@nightschool $ runhaskell ld.hs perl pearl 1
「文字列間のレーベンシュタイン距離を求める(2)Haskell版」への1件のフィードバック