ฉันกำลังทำงานกับล่ามง่าย ๆ สำหรับภาษาการเขียนโปรแกรมและฉันมีชนิดข้อมูลเช่นนี้:
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
ในการปลอมตัว คุณอาจต้องการที่จะดูที่ anamorphisms recursion-schemes
และ ที่ถูกกล่าวว่าฉันคิดว่าทางออกสุดท้ายของคุณสั้นที่สุดเท่าที่จะทำได้ การเปลี่ยนไปใช้แอนrecursion-schemes
มอร์ฟิซึมอย่างเป็นทางการจะไม่ประหยัดมากนัก