我已经阅读了这里的一些讨论,以及其他解释的链接,但我仍然无法理解“改变状态”和“不改变状态”之间的数学联系,因为它与我们有关函数式编程与非FP辩论。据我所知,基本论证可以追溯到函数的纯数学定义,即函数将域成员映射到只有一个范围成员。然后将其与计算机代码函数给定特定输入进行比较,它将始终产生相同的输出,即,从使用到使用不变,即,函数的状态,如在其域到范围映射行为,将不会改变
然后它在我脑海里变得模糊。这是一个例子。假设我想在x-y场上显示闭合的块状多边形。在GIS软件中,我理解一切都存储为有向的闭合图形,即正方形是四个向量,它们的头部和末端相连。原始数据表示只是每个向量的各个笛卡尔起点和终点。当然,软件中可能存在“处理”所有这些坐标集的功能。好。但是以数学方式表示每个多边形怎么样,例如,正x,负y象限中的矩形可能是:
Z = {(x,y) | 3 <= x <= 5, -2 <= y <= -1}
因此,我们有许多类似Z的函数,每个函数都表示一个单独的多边形 - 而不是我的矩阵数学的高手,也许这些“函数”可以表示为矩阵。 。 。但我离题了。
因此,使用通常的原始矢量数据方法,我的代码中有一个函数“更改状态”,因为它处理每组坐标,然后绘制每个多边形(然后处理多边形更改),而每个多边形方法只有一个且只有一个类似Z的函数似乎完全符合“不要改变状态”的规则。对?还是我离开这里?似乎老式的,单功能处理原始坐标数据也没有改变域范围纯度定律。我很困惑....
我的部分灵感来自阅读有关图像处理的新概念,而不是猛击像素机架,每个“框架”将由一个能够“整理图像”整个图像,边缘,颜色的大功能代表,渐变等。这是密切相关吗?我想我想知道为什么我想要以某种方式代表多边形(例如城市街区)的街道地图。我一直听到功能性语言倡导者围绕着这样一种观念跳舞,即数学函数是纯粹的,安全的,良好的,最终是乌托邦式的,而非FP软件功能则是某种邋k的kludge让我们从博格式的幸福中恢复过来。
但更令人困惑的是内存管理与FP相比非FP。我一直听到的(例如并行编程)是FP并没有像C / C ++程序那样改变“内存状态”。这就像谷歌文件系统,其中一切只是坐在虚拟内存池中,而不是数据移入和移出数据库和内存位置?不知何故,所有这些都是相关的。因此,看起来完美的FP程序只是一个功能(可能由许多子功能组成)完成一项任务 - 尽管快速浏览一下任何一个elisp代码似乎都是对这一计数精神分裂症编程的研究。
答案 0 :(得分:3)
Referential transparency是这样一个原则,即表达式的含义或值可以在不需要任何非局部上下文的情况下确定,并且表达式的值不会改变。代码如
int x = 0;
int nextX() {
return x++;
}
违反了引用透明度,因为nextX()
会在某一时刻返回32
,并在下一次调用返回33
时,并且没有办法,仅基于本地分析, nextX()
将在任何给定位置返回。在许多情况下,通过向过程添加参数,可以轻松地将非引用透明的过程转换为引用透明的函数。例如,在刚刚给出的示例中,添加参数currentX
会使nextX
引用透明:
int nextX( int currentX ) {
return currentX+1;
}
当然,这需要每次调用nextX
时,前一个值都可用。
对于完全目的是修改状态(例如,屏幕状态)的程序,这没有多大意义。例如,虽然我们可以编写一个方法print
,它在某种意义上是引用透明的:
int print( int x ) {
printf( "%d", x );
return x;
}
还存在一个问题,即系统的状态被修改了。例如,询问屏幕状态的方法在调用print
之前和之后会有不同的结果。为了使这些类型的过程具有参考透明性,可以使用表示系统状态的参数来扩充它们。例如:
// print x to screen, and return the new screen that results
Screen print( int x, Screen screen ) {
...
}
// return the contents of screen
ScreenContents returnContentsOfScreen( Screen screen ) {
...
}
现在我们有参考透明度,但代价是必须传递Screen
个对象。例如:
Screen screen0 = getInitialScreen();
Screen screen1 = print( 2, screen0 );
Screen screen2 = print( 3, screen1 );
...
这对于使用IO可能感觉有些过分,因为毕竟修改某些状态(即屏幕,文件系统或......)的意图。因此,大多数编程语言都不会使IO方法具有透明性。然而,有些像Haskell一样。由于刚刚显示它是相当麻烦的,因此这些语言通常会有一些语法使事情变得更加干净。在Haskell中,这是通过Monad
和do
表示法完成的(这个答案实际上超出了范围)。如果您对如何使用Monad
概念感兴趣,可能会对本文You Could Have Invented Monads! (And Maybe You Already Have.)