简化lambda表达式的使用并在State Monad中记忆Map

时间:2012-04-01 08:39:02

标签: haskell memoization

此函数比递归版本快得多:

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 ()?

1 个答案:

答案 0 :(得分:1)

insertionPCostdeletionPCostsubstitutionPCostsubstitutionCostIfNEQ仅相互调用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);嗯, MapMonoid个,mappend与原始union中使用的editDistance相同,所以一切正常。)< / p>