编辑:虽然我显然错过了实际问题的靶心,但我认为我的答案非常好,所以我保留它:-)(见下文)。
我想用更简洁的方式来表达这个问题可能是:纯函数式语言可以计算任何命令式语言可以吗?
首先,假设您采用了像 C 这样的命令式语言并制作了它,这样您就不能在定义变量后更改它们。例如:
int i;
for (i = 0; // okay, that's one assignment
i < 10; // just looking, that's all
i++) // BUZZZ! Sorry, can't do that!
好吧,你的for
循环就到这里了。我们可以保持我们的while
循环吗?
while (i < 10)
当然可以,但它不是很有用。 i
不能改变,所以它要么永远运行,要么根本不运行。
递归呢?是的,你可以继续递归,它仍然很有用:
int sum(int *items, unsigned int count)
{
if (count) {
// count the first item and sum the rest
return *items + sum(items + 1, count - 1);
} else {
// no items
return 0;
}
}
现在,有了函数,我们不会改变状态,但是变量可以,嗯,变化。一旦变量传入我们的函数,它就会被锁定。但是,我们可以再次调用该函数(递归),这就像获得一组全新的变量(旧变量保持不变)。items
尽管and有多个实例,但count
总是sum((int[]){1,2,3}, 3)
会计算为6
,因此您可以根据需要将该表达式替换为6
。
我们还能做任何我们想做的事吗?我不是 100% 确定,但我认为答案是“是”。不过,如果你有闭包,你当然可以。
你说得对。这个想法是,一旦定义了变量,就不能重新定义它。给定相同变量的引用透明表达式总是产生相同的结果值。
我建议研究 Haskell,一种纯粹的函数式语言。严格来说,Haskell 没有“赋值”运算符。例如:
my_sum numbers = ??? where
i = 0
total = 0
在这里,您不能编写一个“for 循环”来递增 i 和 total 。不过,一切都没有丢失。只需使用递归来不断获取新i
的 s 和total
s:
my_sum numbers = f 0 0 where
f i total =
if i < length numbers
then f i' total'
else total
where
i' = i+1
total' = total + (numbers !! i)
(请注意,这是在 Haskell 中对列表求和的一种愚蠢方法,但它演示了一种处理单个赋值的方法。)
现在,考虑一下这个看起来非常命令式的代码:
main = do
a <- readLn
b <- readLn
print (a + b)
它实际上是语法糖:
main =
readLn >>= (\a ->
readLn >>= (\b ->
print (a + b)))
这个想法是,main 不是由语句列表组成的函数,main 是 Haskell 执行的 IO 操作,并且操作被定义并与绑定操作链接在一起。此外,可以使用该return
函数定义不执行任何操作、产生任意值的操作。
请注意,绑定和返回并非特定于操作。它们可以与任何自称为 Monad 的类型一起使用,以做各种时髦的事情。
为了澄清,考虑readLn
。 readLn
是一个动作,如果执行,它将从标准输入中读取一行并产生其解析值。要使用该值做某事,我们不能将其存储在变量中,因为这会违反引用透明性:
a = readLn
如果允许这样做,a 的值将取决于世界,并且每次调用时都会有所不同readLn
,这意味着readLn
不会是引用透明的。
相反,我们将 readLn 操作绑定到一个处理该操作的函数,从而产生一个新操作,如下所示:
readLn >>= (\x -> print (x + 1))
这个表达式的结果是一个动作值。如果 Haskell 从沙发上下来并执行此操作,它会读取一个整数、递增它并打印它。通过将动作的结果绑定到对结果执行某些操作的函数,我们可以在状态世界中玩耍时保持引用透明性。