Saat ini saya sedang mengerjakan penerjemah sederhana untuk bahasa pemrograman dan saya memiliki tipe data seperti ini:
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
Dan saya memiliki banyak fungsi yang melakukan hal-hal sederhana seperti:
-- 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
Tetapi di masing-masing fungsi ini, saya harus mengulangi bagian yang memanggil kode secara rekursif hanya dengan perubahan kecil ke satu bagian dari fungsi. Apakah ada cara yang ada untuk melakukan ini secara lebih umum? Saya lebih suka tidak perlu menyalin dan menempel bagian ini:
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
Dan hanya mengubah satu kasus setiap kali karena tampaknya tidak efisien untuk menduplikasi kode seperti ini.
Satu-satunya solusi yang saya bisa buat adalah memiliki fungsi yang memanggil fungsi pertama pada seluruh struktur data dan kemudian secara rekursif pada hasil seperti ini:
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
Tapi saya merasa mungkin harus ada cara yang lebih sederhana untuk melakukan ini. Apakah saya melewatkan sesuatu?
Add :: Expr -> Expr -> Expr
alih-alih Add :: [Expr] -> Expr
, dan singkirkan Sub
sama sekali.
recurseAfter
sedang ana
menyamar. Anda mungkin ingin melihat anamorphisms dan recursion-schemes
. Yang sedang berkata, saya pikir solusi akhir Anda sesingkat mungkin. Beralih ke recursion-schemes
anamorfisme resmi tidak akan banyak menghemat.