我目前正在为一种编程语言开发一个简单的解释器,并且我的数据类型如下:
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
我有许多函数可以执行简单的操作,例如:
-- Substitute a value for a variable
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = go
where
go (Variable x)
| x == name = Number newValue
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
-- Replace subtraction with a constant with addition by a negative number
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = go
where
go (Sub x (Number y)) =
Add [go x, Number (-y)]
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
但是在所有这些函数中,我都必须对函数的一部分进行很小的更改来重复递归调用代码的部分。有没有更通用的现有方法?我宁愿不必复制并粘贴此部分:
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
而且每次都只更改一个大小写,因为复制这样的代码似乎效率低下。
我唯一能想到的解决方案是拥有一个函数,该函数首先在整个数据结构上调用一个函数,然后对结果进行递归,如下所示:
recurseAfter :: (Expr -> Expr) -> Expr -> Expr
recurseAfter f x =
case f x of
Add xs ->
Add $ map (recurseAfter f) xs
Sub x y ->
Sub (recurseAfter f x) (recurseAfter f y)
other -> other
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue =
recurseAfter $ \case
Variable x
| x == name -> Number newValue
other -> other
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd =
recurseAfter $ \case
Sub x (Number y) ->
Add [x, Number (-y)]
other -> other
但是我觉得应该已经可以有一种更简单的方法来做到这一点。我想念什么吗?
Add :: Expr -> Expr -> Expr
而不是Add :: [Expr] -> Expr
,并Sub
完全摆脱。
recurseAfter
是ana
在变相。您可能想看看变形和recursion-schemes
。话虽如此,我认为您的最终解决方案要尽可能地短。切换到官方recursion-schemes
变形将不会节省太多。