什么是去功能化
去功能化是一种编译时转换,它通过用单个一阶应用函数替换高阶函数来移除高阶函数。
这种方法首先由 John C. Reynolds 在 1972 年提出。它在 John C. Reynolds 的论文“高阶编程语言的定义解释器”中有所体现。他的检查是:
- 给定程序中的函数抽象是有限的,因此这些函数抽象被还原并由唯一标识符分配。
- 在程序中,该函数的每个应用程序然后通过调用 apply函数以函数标识符作为第一个参数来恢复。
- 这个应用函数的任务是在第一个参数上执行,然后执行指令,这由剩余参数上的函数标识符指示。
去功能化思想的一个困难是:
- 自由变量由函数抽象引用。在这种情况下,应该通过闭包转换或 lambda 提升来引入去功能化(它是一个元过程,它重构程序,使函数在全局范围内相互独立定义),以便如果函数抽象有任何自由变量,然后它们作为额外的参数传递来应用。
- 除此之外,如果闭包转换被认为是一等值,那么通过构造数据结构来表示这些绑定就变得强制性了。
在程序中,在所有函数抽象上执行,而不是具有单个应用函数。为了确定每个函数应用站点应该调用哪些函数,有不同种类的控制流分析(包括基于类型签名的简单差异),也有一个专门的应用函数可供选择。相反,目标语言支持通过函数指针进行的间接调用,与基于调度的方法相比,目标语言可能组织良好且可扩展。
去功能化的步骤:
去功能化高阶函数(HOF) 有两个步骤:
- 对于每一个在高阶函数(HOF)的使用和应用到的函数的位置,恢复函数与指定的数据类型的值,以构成该函数。这被称为“去功能化符号”。
- 根据 HOF 的定义,如果函数参数应用于任何地方,则通过调用专用的一阶函数来恢复该应用的函数参数,该函数将解释该参数现在代表的去功能化符号。通常,此函数称为apply ,有时称为eval 。
函数式编程思想是在 Defunctionalization 的帮助下提出的。在已经具有高阶功能的语言,即函数式语言中,去功能化有很多价值。
尽管删除了更高阶的功能,去功能化是一种将解释器机械地转换为抽象机器的方式,它还通过面向对象编程语言的函数对象来表示函数。
下面是 Olivier Danvy 给出的例子,它被翻译成 Haskell :
下面是树数据类型:
data Tree a = Leaf a
| Node (Tree a) (Tree a)
现在,在这里我们将不得不对下面给出的程序进行去功能化:
cons :: a -> [a] -> [a]
cons x xs = x : xs
o :: (b -> c) -> (a -> b) -> a -> c
o f g x = f (g x)
flatten :: Tree t -> [t]
flatten t = walk t []
walk :: Tree t -> [t] -> [t]
walk (Leaf x) = cons x
walk (Node t1 t2) = o (walk t1) (walk t2)
现在,为了取消上述程序的功能,我们将用lam数据类型值替换所有高阶函数(即o是这里唯一的高阶函数),而不是直接调用它们,我们将启动一个简化数据类型的应用函数——
data Lam a = LamCons a
| LamO (Lam a) (Lam a)
apply :: Lam a -> [a] -> [a]
apply (LamCons x) xs = x : xs
apply (LamO f1 f2) xs = apply f1 (apply f2 xs)
cons_def :: a -> Lam a
cons_def x = LamCons x
o_def :: Lam a -> Lam a -> Lam a
o_def f1 f2 = LamO f1 f2
flatten_def :: Tree t -> [t]
flatten_def t = apply (walk_def t) []
walk_def :: Tree t -> Lam t
walk_def (Leaf x) = cons_def x
walk_def (Node t1 t2) = o_def (walk_def t1) (walk_def t2)
想要从精选的视频和练习题中学习,请查看C 基础到高级C 基础课程。