我想在elm中制作一个有效的LCS算法版本。 我喜欢这个ocaml版本,但它使用副作用来缓存结果。
let lcs xs ys =
let cache = Hashtbl.create 16 in
let rec lcs xs ys =
try Hashtbl.find cache (xs, ys) with
| Not_found ->
let result =
match xs, ys with
| [], _ -> []
| _, [] -> []
| x :: xs, y :: ys when x = y ->
x :: lcs xs ys
| _ :: xs_rest, _ :: ys_rest ->
let a = lcs xs_rest ys in
let b = lcs xs ys_rest in
if (List.length a) > (List.length b) then a else b
in
Hashtbl.add cache (xs, ys) result;
result
in
lcs xs ys
如果我想在榆树中使用记忆,该怎么办?
答案 0 :(得分:1)
您可能希望查看使用自动记忆的Lazy package或使用显式记忆的elm-lazy package。
通过将内部递归函数包装在Lazy
中,可以减少评估次数。在https://ellie-app.com/4hXx2X753wfa1/0的示例中,懒惰版本有大约300 Debug
个日志条目,非懒惰版本大约有700个条目。
答案 1 :(得分:0)
slack上的Gilbert Kennen给了我一个似乎更好的版本:
lcs : List a -> List a -> List a
lcs xs ys =
lcsHelper xs ys ( 0, 0 ) Dict.empty
|> Dict.get ( 0, 0 )
|> Maybe.map Tuple.second
|> Maybe.withDefault []
lcsHelper : List a -> List a -> ( Int, Int ) -> Dict ( Int, Int ) ( Int, List a ) -> Dict ( Int, Int ) ( Int, List a )
lcsHelper xs ys position memo =
case ( Dict.get position memo, xs, ys ) of
( Nothing, x :: xRest, y :: yRest ) ->
let
nextYPos =
Tuple.mapSecond ((+) 1) position
nextXPos =
Tuple.mapFirst ((+) 1) position
newMemo =
memo
|> lcsHelper xs yRest nextYPos
|> lcsHelper xRest ys nextXPos
best =
maxListTuple
(get nextXPos newMemo)
(get nextYPos newMemo)
|> consIfEqual x y
in
Dict.insert position best newMemo
_ ->
memo
get : ( Int, Int ) -> Dict ( Int, Int ) ( Int, List a ) -> ( Int, List a )
get position memo =
Dict.get position memo |> Maybe.withDefault ( 0, [] )
maxListTuple : ( Int, List a ) -> ( Int, List a ) -> ( Int, List a )
maxListTuple ( xLen, xs ) ( yLen, ys ) =
if yLen > xLen then
( yLen, ys )
else
( xLen, xs )
consIfEqual : a -> a -> ( Int, List a ) -> ( Int, List a )
consIfEqual x y ( listLen, list ) =
if x == y then
( listLen + 1, x :: list )
else
( listLen, list )
它使用字典来缓存结果。