我想比较函数编码(Church / Scott)与传统编码(汇编器/ C)数据结构的性能。
但是在我这样做之前,我需要知道函数在内存中的表示效率。该功能当然可以部分应用(也称为闭包)。
我对当前流行的功能性语言(Haskell,ML)使用的编码算法以及可以实现的最有效的一种语言都感兴趣。
奖金点:有这样的编码该地图函数编码整数天然整数(short
,int
等,在C)。可能吗
我根据效率来评估效率。换句话说,编码越有效,对功能数据结构的计算性能的影响就越小。
我想比较函数编码(Church / Scott)与传统编码(汇编器/ C)数据结构的性能。
但是在我这样做之前,我需要知道函数在内存中的表示效率。该功能当然可以部分应用(也称为闭包)。
我对当前流行的功能性语言(Haskell,ML)使用的编码算法以及可以实现的最有效的一种语言都感兴趣。
奖金点:有这样的编码该地图函数编码整数天然整数(short
,int
等,在C)。可能吗
我根据效率来评估效率。换句话说,编码越有效,对功能数据结构的计算性能的影响就越小。
Answers:
问题是,在函数编码方面确实没有多少回旋余地。主要选项如下:
术语重写:您将函数存储为它们的抽象语法树(或其某种编码。在调用函数时,您手动遍历语法树以用参数替换其参数。这很简单,但在时间和空间方面效率低下。
闭包:您可以通过某种方式表示函数,例如语法树,更有可能表示机器代码。在这些函数中,您以某种方式通过引用来引用您的参数。它可以是指针偏移量,可以是整数或De Bruijn索引,也可以是名称。然后,您将函数表示为闭包:函数“指令”(树,代码等)与包含函数所有自由变量的数据结构配对。在实际应用函数时,它会以某种方式知道如何使用环境,指针算术等在其数据结构中查找自由变量。
我敢肯定还有其他选择,但这是基本的选择,我怀疑几乎所有其他选择都是基本闭包结构的变体或优化。
因此,就性能而言,闭包在总体上比术语重写要好。在这些变体中,哪个更好?那在很大程度上取决于您的语言和体系结构,但是我怀疑“带有包含免费变量的结构的机器代码”是最有效的。它具有函数需要的所有内容(指令和值),仅此而已,并且调用并不会最终进行长期遍历。
我对当前两种流行的功能语言(Haskell,ML)的编码算法都感兴趣
我不是专家,但我99%的大多数ML口味都使用了我描述的闭包的某些变体,尽管可能会进行一些优化。请参阅此内容(可能已过时)。
由于懒惰的评估,Haskell所做的事情有些复杂:它使用了Spineless Tagless Graph Rewriting。
也是最有效的方法
什么是最有效的?没有一种实现在所有输入上都是最高效的,因此您会获得平均而言效率很高的实现,但是每种实现在不同的场景下都非常出色。因此,没有确定最高效率或最低效率的排名。
这里没有魔术。要存储函数,您需要以某种方式存储其自由值,否则编码的信息少于函数本身的信息。也许您可以通过部分评估来优化一些自由值,但这会影响性能,因此您必须小心确保始终停止运行。
而且,也许您可以使用某种压缩或巧妙的算法来提高空间效率。但是,然后您要么在空间上进行交易,要么就处于针对某些情况进行了优化而针对其他情况进行了放慢的情况。
您可以优化常见的情况,但什么常见的情况是可以在语言,应用领域等类型的代码是快速的视频游戏改变(数字运算,具有较大的输入密集的循环),可能比不同编译器最快的速度(遍历树,工作列表等)。
加分点:是否存在将函数编码的整数映射到本机整数(C中的short,int等)的编码?可能吗
不,这是不可能的。问题在于lambda演算不允许您对术语进行自省。当函数采用与教堂数字类型相同的参数时,它必须能够调用它,而无需检查该数字的确切定义。教会编码就是这样:您可以使用它们进行唯一的操作就是调用它们,并且可以模拟所有有用的东西,但这并非没有代价。
更重要的是,整数占据了每种可能的二进制编码。因此,如果将lambda表示为它们的整数,则无法表示非教会数字的lambda!或者,您将引入一个标志来表示lambda是否为数字,但是您想要的任何效率都可能超出标准。
编辑:自编写此文档以来,我已经意识到实现高阶函数的第三个选择:defunctionalization。在这里,每个函数调用都会变成一个大switch
语句,具体取决于将哪个lambda抽象作为函数给出。这里的权衡是它是整个程序的转换:您不能单独编译各个部分,然后以这种方式链接在一起,因为您需要提前获得完整的lambda抽象集。