2.2.5 计算 (Computation by calculation)

728 篇文章 1 订阅
349 篇文章 0 订阅

2.2.5 计算 (Computation bycalculation)

 

[

Computation,calculation,都翻成了计算,大概强调的重点不同吧。

Computation by calculation,也还是翻成计算吧。

evaluate,多数时候也翻译成计算,有时也译成分析。

]

我们在前两节中讨论了思考有关程序执行的新方法。要了解命令式程序的执行,就必须理解是如何改变状态的。使用面向对象的命令式语言编写程序,状态不仅包括所有对象的内部状态,而且包括当前正在执行的语句(在每个线程中),以及在每个堆栈中所有局部变量的状态。当前正在执行的语句是状态的一部分,了解这一点非常重要,因当你在纸上写程序执行时,很难跟踪状态。

在函数编程中,我们可以使用一种叫“计算的计算”的方法(即,computation by calculation,计算所调用的计算方法,Computation,似乎强调计算的结果,calculation是计算的方法,亦或相反。以下就简称计算)。这种方法对于 Haskell 来说特别重要(参补充材料“Haskell 中的数学纯”),Haskell 在《The Haskell School of Expression》[Hudak, 2000] 中有更详细地讲述。使用计算,我们从原始的表达表开始(比如函数调用),执行一步(比如,用函数体替换调用,或者计算基本数学运算的结果)。就这样多次重复,我们可以很轻松地分析出程序是如何计算的。

如果我们想要也解函数在边界上的行为,这种方法特别有用。在清单 2.2 中,我们用它来分析 SumNumber 的行为,把区间的上、下边界取相同的数。

我们先分析调用 SumNumbers(5, 5):

 

清单 2.2 用函数方式分析表达式 SumNumbers(5,5)

 

SumNumbers(5, 5)

 

用函数体去展开函数调用,把函数中所有的参数用指定值替换(from = 5, to = 5):

 

(5 > 5) ? 0 : {

  varsumRest = SumNumbers(5 + 1, 5);

  5 +sumRest; };

 

化简条件运算表达式。首先,分析条件(5 > 5),然后,继续分析条件为假的分支:

 

var sumRest = SumNumbers(5 + 1, 5);

5 + sumRest;

 

计算赋给变量 sumRest 的值。这时,我们要展开 SumNumbers(6, 5),计算函数调用参数的值:

 

var sumRest =

  return(6 > 5) ? 0 : {

    varsumRest = SumNumbers(6 + 1, 5);

    6+ sumRest; };

5 + sumRest

 

继续计算 sumRest 的值。分析条件 (6 > 5),用 then 分支的子表达式替换初始表达式:

 

var sumRest = 0

5 + sumRest

 

计算出变量的值以后,就用实际值去替换在表达式中所有出现这个变量的位置:

 

5 + 0

 

计算基本的加法运行符(+)的调用:

 

5

 

正如你所看到的,用这种方法写出函数代码的计算过程,是很容易的。虽然函数程序员不会真的花时间写出程序的运行过程,但是,了解计算的过程,还是有用的,因为这种思考函数代码的方式是很强大的。

当然,由于这个示例很简单,我们没有讨论很多重要细节。但请放心,我们会在下一章涉及到所有这些问题。清单 2.2 展示了计算另一个有意义的方面,是决定下一步应该分析表达式的哪一部分。在这个示例中,我们使用了最里面的子表达式,因此,我们分析了函数调用的所有参数,以及运算符的使用(有了条件运算符的意外,这会以不同方式处理)。这一策略称为,严格或热情(strict or eager),许多函数语言都使用这种策略,包括 F#,它类似于一句一句地执行代码。

 

Haskell 中的数学纯

 

Haskell 出现于 1990 年,已在学术界流行。在这一节,我们已经看到,在函数语言中,我们使用不可变的数据结构和不可变的值,而不用可变的变量。F# 没有严格遵守,因为仍然可以声明可变的值。这种非严格的方法对于 .NET 的互操作特别有用,因为大多数的 .NET 库依赖于可变的状态,它是为命令式的、面向对象语言,如 C# 和 VB.NET 而设计的。

相反,Haskell 严格执行数学纯。这样,在程序执行的顺序上,可以很灵活。在前面的示例中,我们提到 F# 首先计算表达式的最内层部分。而在 Haskell 中,由于没有副作用,所以计算的顺序(可能)不重要。由于重新排序没有相互依赖的代码,也就不会改变程序的含义。因此,Haskell 使用一种称为延迟计算(lazy evaluation)的技术,直到实际需要时(例如,输出到控制台),才会计算表达式的结果。

这种改变程序而不改变其含义的能力,在 F# 中也非常重要,我们将在第十一章学习如何用它来重构 F# 程序,将看到延迟计算也可以用在 F# 中,这是一种很有价值的优化方案。

 

在后面几节,我们讨论了程序状态,用递归写计算。我们敢保证,你一定想学习如何以可重用的方式,编写代码中较难的部分,这就是我们的下一节的主题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值