此函数比递归版本快得多:
crossSubstrings :: String -> String -> [(String,String)]
crossSubstrings string1 string2 = [(substr1,substr2) | substr1 <- inits string1,
substr2 <- inits string2]
type Distances = Map.Map (String,String) Int
editDistanceMemoized :: String -> String -> Int
editDistanceMemoized s1 s2 =
let
substrings = s1 `crossSubstrings` s2
distances = foldl (editDistance) emptyMap substrings
in
distances Map.! (s1,s2)
where
emptyMap = Map.fromList []
editDistance :: Distances -> (String,String) -> Distances
editDistance map ([],s1) = map `Map.union` getMap [] s1 (length s1)
editDistance map (s1,[]) = map `Map.union` getMap s1 [] (length s1)
editDistance map (s1,s2) = map `Map.union` getMap s1 s2 (cost map s1 s2)
getMap s1 s2 d = Map.fromList [((s1,s2),d)]
insertionPCost = \m -> \s1 -> \s2 -> m Map.! (s1, init s2) + 1
deletionPCost = \m -> \s1 -> \s2 -> m Map.! (init s1, s2) + 1
substitutionPCost = \m -> \s1 -> \s2 -> m Map.! (init s1, init s2)
+ substitutionCostIfNEQ s1 s2
substitutionCostIfNEQ = \s1 -> \s2 -> if (last s1 == last s2) then 0 else 2
cost = \m -> \s1 -> \s2 -> minimum [insertionPCost m s1 s2,
deletionPCost m s1 s2,
substitutionPCost m s1 s2]
然而(第一个问题),我觉得可以避免一些lambdas(它看起来不重复吗?特别注意cost
)。有没有办法撰写minimum
?
此外,State Monad可用于传播地图(而不是使用foldl
?)。尽管阅读了State.>>=
和State.id
的行为,但我并不是100%确定签名的样子(第二个问题)。
我想到了这个,状态是“下一对要测量的字符串”,距离包含记忆距离。
editDistance :: State Distances (String,String) -> State Distances ()?
答案 0 :(得分:1)
insertionPCost
,deletionPCost
,substitutionPCost
和substitutionCostIfNEQ
仅相互调用cost
,并始终使用相同的参数(保存substitutionCostIfNEQ
1}}不接受m
);所以我们可以像这样重新安排它们:
cost = \m -> \s1 -> \s2 -> minimum [insertionPCost, deletionPCost, substitutionPCost]
where insertionPCost = m Map.! (s1, init s2) + 1
deletionPCost = m Map.! (init s1, s2) + 1
substitutionPCost = m Map.! (init s1, init s2) + substitutionCostIfNEQ
substitutionCostIfNEQ = if (last s1 == last s2) then 0 else 2
明确的lambdas并没有给你任何东西,所以重写得更清楚:
cost m s1 s2 = minimum [insertionPCost, deletionPCost, substitutionPCost]
where insertionPCost = m Map.! (s1, init s2) + 1
deletionPCost = m Map.! (init s1, s2) + 1
substitutionPCost = m Map.! (init s1, init s2) + substitutionCostIfNEQ
substitutionCostIfNEQ = if (last s1 == last s2) then 0 else 2
要回答你的第二个问题,目前你有
editDistance :: Distances -> (String,String) -> Distances
如果你改用State
,那就是
editDistance :: (String,String) -> State Distances ()
也就是说,editDistance
将是一个带(String,String)
的函数,并产生与Distances
状态交互的东西,而没有其他有意义的结果。
但是
首先,我没有看到您使用foldl
时出现任何问题。
其次,你永远不会真正使用累积值,状态是什么。您可以使用它来创建一个新值,但不要在其中查找任何内容。因此,您不需要State
,只需Writer
。
editDistance :: (String,String) -> Writer Distances ()
也就是说,editDistance
将是一个带(String,String)
的函数,并产生一些添加到Distances
累加器的函数,而没有其他有意义的结果。
(这里有一个细微之处:Writer
的第一个参数必须是Monoid
,它必须使用对您有用的组合操作(mappend
);嗯, Map
是Monoid
个,mappend
与原始union
中使用的editDistance
相同,所以一切正常。)< / p>