惰性求值

维基百科,自由的百科全书
跳转至: 导航搜索
求值策略

及早求值
惰性求值
部分求值
远程求值
短路求值

惰性求值Lazy Evaluation),又称惰性计算懒惰求值,是一个计算机编程中的一个概念,它的目的是要最小化计算机要做的工作。它有两个相关而又有区别的含意,可以表示为“延迟求值”和“最小化求值”,本条目专注前者,后者请参见最小化计算条目。除可以得到性能的提升外,惰性计算的最重要的好处是它可以构造一个无限的数据类型

惰性求值的相反是及早求值,这是一个大多数编程语言所拥有的普通计算方式。

延迟求值[编辑]

延迟求值特别用于函数式编程语言中。在使用延迟求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在該值被取用的时候求值,也就是说,语句如 x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。

某些编程语言缺省延迟表达式的求值,另一些提供函数或特殊语法来延迟求值。在 MirandaHaskell 中,缺省延迟函数实际参数的求值。在很多其他语言中,可以使用特殊语法明确悬置计算来延迟求值(比如 Scheme 的 "delay" 或 "force"),更一般的通过把一个表达式包装在 thunk 中。表示这种明确延迟求值的对象叫做预期或承诺

延迟求值的一个好处是能够建立可计算的无限列表而没有妨碍计算的无限循环或大小问题。例如,可以建立生成无限斐波那契数列表的函数(经常叫做“流”)。第 n 个斐波那契数的计算仅是从这个无限列表上提取出这个元素,它只强迫这个列表的前 n 个成员的计算。

例如,在 Haskell 中,斐波纳契数的列表可以写为

  fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

在 Haskell 语法中,":" 向列表头部添加一个元素,tail 返回去掉第一个元素的一个列表,而 zipWith 使用指定的函数(这里是加法)来组合两个列表的对应元素生成第三个列表。

假定编程者是仔细的,只有生成特定结果所需要的值才被求值。但是特定计算可能导致程序尝试对无限数目个元素进行求值;例如,求列表的长度或使用fold运算对列表的元素求和将导致程序不能终止或耗尽内存。

语言实现[编辑]

C++[编辑]

在典型的大规模计算(如矩阵运算)中,并不是所有的结果值都是必须求出的,很多结果值并没有被用到。因此,C++中实现延迟求值也是现实需求。一般把运算符函数函数重载,返回一个对象保存了计算所需的各种信息(如实参)等。例如,对矩阵加法的延迟求值:

struct matrix{
    double values[100][100]  ;
    double* operator[](int i){return values[i];}
};
 
struct matrix_add {
   mutable struct rowEntry{ 
       double operator [](int k){
           matrix_add* pthis=(matrix_add*)this;
           return (pthis->a)[_currentRow][k]+(pthis->b)[_currentRow][k];
        }
       void setRow(int v){_currentRow=v;}
   private:
       int _currentRow;
   } _rowEntry;
   matrix_add(matrix & a, matrix & b) : a(a), b(b) { }
   rowEntry& operator[](int i)const {
       _rowEntry.setRow(i); 
       return _rowEntry;
    }
private:
    matrix & a;
    matrix  & b;
};
 
matrix_add operator +(matrix & a, matrix & b) {
    return matrix_add(a, b);
}
 
int main ()
{
    matrix A,B;
    A[3][4]=101; B[3][4]=102;
    matrix_add R=A+B;
    int result = R[3][4];
    return 0;
}

参见[编辑]

外部链接[编辑]