현재 프로그래밍 언어에 대한 간단한 인터프리터로 작업하고 있으며 다음과 같은 데이터 유형이 있습니다.
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
IS를 ana
변장. 아나 몰 피즘을보고 싶을 수도 있습니다 recursion-schemes
. 즉, 최종 솔루션이 가능한 한 짧다고 생각합니다. 공식 recursion-schemes
아나 모피 즘으로 전환 해도 많은 비용이 절약되지는 않습니다.