那么答案很简单,程序消耗的 CPU 周期越少,它运行的速度就越快... ... 如果一大块处理指令对程序的结果没有影响,那么就没有必要(因此是浪费时间)无论如何都要执行它们... ..。
如果 otoh,你指的是我所知道的“懒惰初始化器”,比如:
class Employee
{
private int supervisorId;
private Employee supervisor;
public Employee(int employeeId)
{
// code to call database and fetch employee record, and
// populate all private data fields, EXCEPT supervisor
}
public Employee Supervisor
{
get
{
return supervisor?? (supervisor = new Employee(supervisorId));
}
}
}
foo x y = if condition1
then some (complicated set of combinators) (involving bigscaryexpression)
else if condition2
then bigscaryexpression
else Nothing
where some x y = ...
bigscaryexpression = ...
condition1 = ...
condition2 = ...
这使您能够通过对函数体的理解来“自顶向下”地工作。类 ML 语言强制您使用严格求值的 let。因此,您不敢将 let 子句“提升”到函数的主体,因为如果它代价高昂(或者有副作用) ,您不希望它总是被求值。Haskell 可以明确地将细节“推迟”到 where 子句,因为它知道该子句的内容只会在需要时进行评估。
在实践中,我们倾向于使用警卫,并进一步崩溃:
foo x y
| condition1 = some (complicated set of combinators) (involving bigscaryexpression)
| condition2 = bigscaryexpression
| otherwise = Nothing
where some x y = ...
bigscaryexpression = ...
condition1 = ...
condition2 = ...
懒惰的一个巨大好处是能够编写具有合理分摊界限的不可变数据结构。一个简单的例子是不可变堆栈(使用 F #) :
type 'a stack =
| EmptyStack
| StackNode of 'a * 'a stack
let rec append x y =
match x with
| EmptyStack -> y
| StackNode(hd, tl) -> StackNode(hd, append tl y)
代码是合理的,但是附加两个堆栈 x 和 y 在最佳、最差和平均情况下需要 O (x 的长度)时间。附加两个堆栈是一个整体操作,它涉及堆栈 x 中的所有节点。
我们可以将数据结构重写为惰性堆栈:
type 'a lazyStack =
| StackNode of Lazy<'a * 'a lazyStack>
| EmptyStack
let rec append x y =
match x with
| StackNode(item) -> Node(lazy(let hd, tl = item.Force(); hd, append tl y))
| Empty -> y
假设您有一个数字 S 和数字 N 的列表。您需要从列表 S 中找到与数字 N 最接近的数字 M。可以有两个上下文: 单个 N 和一些 N 的列表 L (例如,对于 L 中的每个 N,查找 S 中最接近的 M)。如果使用延迟计算,可以对 S 进行排序,并应用二进制搜索找到最接近 M 到 N 的值。对于好的延迟排序,对于单个 N 和 O (ln (size (S)) * (size (S) + size (L))步骤,都需要 O (size (S) + size (L))步骤。如果没有延迟计算来获得最佳效率,那么必须为每个上下文实现算法。
它运行良好的示例: sum . take 10 $ [1..10000000000]。我们不介意把它减少到10个数字之和,而不仅仅是一个简单的直接的数字计算。当然,如果没有惰性计算,仅仅为了使用前10个元素,就会在内存中创建一个巨大的列表。它肯定会非常慢,并可能导致内存不足错误。
例如,它没有我们想要的那么好: sum . take 1000000 . drop 500 $ cycle [1..20]。即使是在循环中而不是在列表中,它实际上也会对1000000个数字求和; ,但是它 应该仍然被简化为只有一个直接的数值计算,只有很少的条件和公式。哪个 会比100万个数字加起来好多了。即使是在一个循环中,而不是在一个列表中(即在森林砍伐优化之后)。