Şu anda bir programlama dili için basit bir tercüman üzerinde çalışıyorum ve böyle bir veri türü var:
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
Ve basit işler yapan birçok fonksiyonum var:
-- 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
Ancak bu işlevlerin her birinde, kodu yineleyen işlevin bir bölümüne sadece küçük bir değişiklikle tekrarlayan kısmı tekrarlamak zorundayım. Bunu daha genel olarak yapmanın mevcut bir yolu var mı? Bu bölümü kopyalayıp yapıştırmak istemem:
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
Ve her seferinde tek bir vakayı değiştirin, çünkü böyle bir kodu kopyalamak verimsiz görünüyor.
Gelebileceğim tek çözüm, önce tüm veri yapısında bir işlevi çağıran ve daha sonra bu sonuç üzerinde özyineli olarak bir işlevi çağıran bir işleve sahip olmaktır:
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
Ama muhtemelen bunu yapmanın daha basit bir yolu olması gerektiğini hissediyorum. Bir şey mi kaçırıyorum?
Add :: Expr -> Expr -> Expr
yerine Add :: [Expr] -> Expr
ve kurtulmak Sub
tamamen.
recurseAfter
IS ana
kılık. Anamorfizmlere bakmak isteyebilirsiniz ve recursion-schemes
. Bununla birlikte, son çözümünüzün olabildiğince kısa olduğunu düşünüyorum. Resmi recursion-schemes
anamorfizmlere geçmek fazla tasarruf etmeyecektir.