3

我已经阅读了这里的一些讨论,以及其他解释的链接,但我仍然无法理解“改变状态”和“不改变状态”之间的数学联系,因为它与我们的函数式编程与非 FP 辩论。据我了解,基本论点可以追溯到函数的纯数学定义,即函数仅将域成员映射到一个范围成员。然后将其与给定某个输入的计算机代码函数进行比较,它始终会产生相同的输出,即不会因使用而异,即函数的状态,如在其域到范围的映射行为一样,不会改变.

然后它在我的脑海中变得模糊不清。这是一个例子。假设我想在 xy 字段上显示封闭的块状多边形。在 GIS 软件中,我了解所有内容都存储为有向封闭图,即一个正方形是四个向量,它们的头和尾相连。原始数据表示只是每个向量的各个笛卡尔起点和终点。当然,软件中可能有一个函数可以“处理”所有这些坐标集。好的。但是以数学方式表示每个多边形怎么样,例如,正 x、负 y 象限中的矩形可能是:

Z = {(x,y) | 3 <= x <= 5, -2 <= y <= -1}

所以我们会有很多类似 Z 的函数,每个函数都表示一个单独的多边形——而不是我的矩阵数学专家,也许这些“函数”可以表示为矩阵。. . 但我离题了。

因此,使用通常的原始矢量数据方法,我的代码中有一个函数“改变状态”,因为它处理每组坐标,然后绘制每个多边形(然后处理多边形的变化),而一个和-only-one-one-Z-like-function-per-polygon 方法似乎完全遵守“不改变状态”规则。正确的?还是我离这儿很远?似乎老式的单功能处理原始坐标数据也没有改变域范围纯度法。我很困惑....

我的部分灵感来自于阅读有关图像处理的新想法,其中每个“帧”将由一个能够“绘制”整个图像、边缘、颜色、渐变、等等。这是密切相关的吗?我想我试图理解为什么我要以一种或另一种方式表示多边形(例如城市街区)的街道地图。我一直听到函数式语言的拥护者围绕这样的想法跳舞,即数学函数是纯粹的、安全的、好的,最终是乌托邦式的,而非 FP 软件函数是某种马虎的杂物,使我们远离博格式的幸福。

但更令人困惑的是内存管理相对于 FP 与非 FP。我一直听到的(例如并行编程)是 FP 不会像 C/C++ 程序那样改变“内存状态”。这是否像 Google 文件系统一样,实际上所有内容都只是放在虚拟内存池中,而不是数据移入和移出数据库和内存位置?不知何故,所有这些事情都是相关的。因此,看起来完美的 FP 程序只是一个单一的函数(可能由许多子函数组成)执行一个单一的任务——尽管快速浏览任何 elisp 代码似乎是对精神分裂症编程的研究。

4

1 回答 1

3

编程(以及数学、逻辑等)中的引用透明性是一种原则,即无需任何非本地上下文即可确定表达式的含义或值,并且表达式的值不会改变。像这样的代码

int x = 0;

int nextX() {
  return x++;
}

违反了引用透明性,因为nextX()将在某一时刻 return 32,在下一次调用 return 33,并且仅基于局部分析,没有办法在任何给定位置返回什么。 在许多情况下,通过向过程添加参数,nextX()可以很容易地将非引用透明过程转换为引用透明函数。例如,在刚刚给出的示例中,添加一个参数,使得引用透明:currentXnextX

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 中,这是通过Monads 和do符号来完成的(这实际上超出了这个答案的范围)。如果你对如何使用这个Monad概念来实现这一点感兴趣,你可能会对这篇文章感兴趣,你可能已经发明了 Monads!(也许你已经有了。)

于 2013-05-21T22:14:57.297 回答