Haskell 将函数自动应用于子结构
假设我正在抽象语法树数据类型上编写一个“替换”函数:Haskell 将函数自动应用于子结构,haskell,recursion,algebraic-data-types,Haskell,Recursion,Algebraic Data Types,假设我正在抽象语法树数据类型上编写一个“替换”函数: data Term = Id String | If Term Term Term | Let String Term Term ... subst :: String -- name of identifier to replace -> Term -- term to replace the identifier with -> Term
data Term = Id String
| If Term Term Term
| Let String Term Term
...
subst :: String -- name of identifier to replace
-> Term -- term to replace the identifier with
-> Term -- body in which to perform the replacements
-> Term
subst identifier term = go
where go (Id id') = if identifier == id' then term else Id id'
go (If t1 t2 t3) = If (go t1) (go t2) (go t3)
go (Let id' term' body) = Let id' (go term') (go body)
...
(忽略阴影问题)。请注意编写
If
分支是多么乏味。我必须进行模式匹配,命名这3个部分,然后重构一个If
将go
明确应用于这3个部分中的每一个。对于Let
,我必须进行模式匹配,命名这3个部分,并重构一个Let
,将go
明确应用于相关的2个部分。有没有一种更简单(无点?)的方法来编写它而不必详细说明每一个细节?这里最好的方法是使用数据类型泛型编程,它正是为类似这样的任务而设计的。下面是一个使用标准库的示例:
这直接表示转换(在本例中,将函数
go
应用于Term
类型的任何子级)应以自底向上的方式应用于整个树。这不仅更加简洁,而且只要基本递归方案保持不变,无论向术语添加多少构造,相同的代码都会继续工作。ehird回答了一般问题;您可能会发现更好地回答了特定问题,将第一行go
简化为“go(Id Id');identifier==Id'=term
”。
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Generics
data Term = Id String
| If Term Term Term
| Let String Term Term
deriving (Eq, Show, Typeable, Data)
subst :: String -- name of identifier to replace
-> Term -- term to replace the identifier with
-> Term -- body in which to perform the replacements
-> Term
subst identifier term = everywhere (mkT go)
where go (Id id') = if identifier == id' then term else Id id'
go x = x