Je travaille actuellement sur un interpréteur simple pour un langage de programmation et j'ai un type de données comme celui-ci:
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
Et j'ai de nombreuses fonctions qui font des choses simples comme:
-- 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
Mais dans chacune de ces fonctions, je dois répéter la partie qui appelle le code récursivement avec juste un petit changement à une partie de la fonction. Existe-t-il un moyen existant de procéder de manière plus générique? Je préfère ne pas avoir à copier et coller cette partie:
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
Et il suffit de changer un seul cas à chaque fois car il semble inefficace de dupliquer du code comme celui-ci.
La seule solution que je pourrais trouver est d'avoir une fonction qui appelle d'abord une fonction sur toute la structure de données, puis récursivement sur le résultat comme ceci:
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
Mais je pense qu'il devrait probablement déjà y avoir un moyen plus simple de le faire. Suis-je en train de manquer quelque chose?
Add :: Expr -> Expr -> Expr
au lieu de Add :: [Expr] -> Expr
et supprimez-les Sub
complètement.
recurseAfter
est - ana
déguisé. Vous voudrez peut-être regarder les anamorphismes et recursion-schemes
. Cela étant dit, je pense que votre solution finale est aussi courte que possible. Passer aux recursion-schemes
anamorphismes officiels ne fera pas beaucoup d'économies.