lazy形容词,懒惰的,毫无疑问是一个贬义词。但是,对于计算机领域,lazy却是非常重要的优化思想:把任务推迟到必须的时刻,好处是避免重复计算,甚至不计算。本文的目的是抛砖引玉,总结一些编程中的lazy idea,以期有一些启发。google “lazy”这个单词,在计算机领域高频出现三个词:lazy loading(惰性加载)、lazy initializing(惰性初始化)、lazy evaluation(惰性求值),本文并不刻意区分,因为不管是loading、initializing还是evaluation都需要耗费计算机的运算资源,而且,loading(initializing)也是某种意义上的evaluation。

lazy ideas:

在GOF的设计模式中,并没有一个叫“lazy loading”之类的设计模式,但是其思想贯穿在很多设计模式中。其中比较明显的就是singleton和proxy模式。

singleton

单例模式的实现一般都有两种方式,要么在调用之前就创建好单例对象(eager way),要么在第一次调用的时候生成单例对象(lazy way),两者对象的代码大致是这样的:
 1 class eager_meta(type):
 2     def __init__(clz, name, bases, dic):
 3         super(eager_meta, clz).__init__(name, bases, dic)
 4         clz._instance = clz()
 5
 6 class singleton_eager(object):
 7     __metaclass__ = eager_meta
 8
 9     @classmethod
10     def instance(clz):
11         return clz._instance
12
13
14 class singleton_lazy(object):
15     __instance = None
16     @classmethod
17     def instance(clz):
18         if clz.__instance is None:
19             clz.__instance = singleton_lazy()
20         return clz.__instance

  PS:在python中,这样使用单例模式不是很pythonic,更好的办法可见在stackoverflow上的这篇文章《creating-a-singleton-in-python》。另外在多线程环境下,要实现线程安全的单例还是很复杂的,具体讨论可参见iteye上的分析。

proxy:

代理模式属于责任型模式, 使得一个对象代表另一个对象进行各种操作,常用场景包括

  • remote proxy(远程代理),如RMI, RPC
  • virtual proxy(虚代理),根据需要创建开销很大的对象,如文档中图片的加载
  • (保护代理):控制对原始对象的访问, 如智能指针
其中 viatual proxy是使用lazy loading很好的例子

Short-circuit evaluation:

短路求值在绝大多数编程语言都有实现,比较常见的语法如下:
x and y(x && y)
x or y(x || y)
x if bool else y(bool? x : y )
短路求值基本上都是数学逻辑的体现,如果第一个参数已经能推导出这个表达式的意义,那么后面的参数是无需计算的。短路求值非常有用,不仅能避免无用的计算,对于逻辑判断也非常有用。比如在python中,判断一个对象的is_ok属性为True,我们一般这么写
if(obj.is_ok)
如果obj被赋值成了None,那么就会报一个异常,所以可以写成
if(obj is not None and obj.is_ok)
python中,一些函数也有短路求值的特性,比如在这篇文章中提到的any函数:
1     ret = any(self.calc_and_ret(e) for e in elements)
2     def self.calc_and_ret(self, e):
3         # do a lot of calc here which effect self
4         return True(or False)

本意是希望对所有的element都计算,然后返回一个结果,但事实上由于短路求值, 可能后面很多的元素都不会再调用calc_and_ret

generator:

在python和javascript语言中都有generator,generator与普通的函数相比,可以多次(甚至无限次)返回,而且返回值是在需要的时候才生成。在python中,下面两段代码非常相似,但事实上差异非常大:
1     for x in [i*i for i in xrange(10000)]
2       # do sth with i
3
4     for x in (i*i for i in xrange(10000)]
5       # do sth with i

generator更广泛的应用可以参见《python yield generator 详解》。javascript中generator的语法和使用与python都非常类似,可以参见这篇文章。

函数式编程语言中的应用:

lazy evaluation在函数式编程语言中使用得非常频繁,python也可以当做函数式编程语言来使用,而更为明显的是haskell,在其首页的features介绍里面就有大大的“lazy”

cache:

cache也是一种lazy思想,如果之前有计算结果,那么直接复用之前的结果就行了,干嘛还要重新计算呢?而且最开始的缓存内容, 也是在需要的时候才计算的,而不是一开始就计算好。wiki上有python实现的简单例子:
 1 class Fruit:
 2     def __init__(self, item):
 3         self.item = item
 4
 5 class Fruits:
 6     def __init__(self):
 7         self.items = {}
 8
 9     def get_fruit(self, item):
10         if item not in self.items:
11             self.items[item] = Fruit(item)
12
13         return self.items[item]
14
15 if __name__ == '__main__':
16     fruits = Fruits()
17     print(fruits.get_fruit('Apple'))
18     print(fruits.get_fruit('Lime'))

Dirty Flag:

在《Dirty Flag模式及其应用》一文中,列举了Dirty Flag模式的诸多应用场景。Dirty Flag显然也是很明显的lazy evaluation。比如《game programming pattern》中的例子:子模型的世界坐标取决于父模型的世界坐标以及子模型在父模型坐标空间的相对坐标,如果父模型的世界坐标变化时就主动去重新计算子模型的坐标,因为两帧之间父模型的坐标可能多次变换,往往会造成冗余的计算。所以Dirty Flag只是在父模型坐标变化的时候标记,绘制的时候再计划所有受影响的模型的世界坐标。

CopyOnWrite:

CopyOnWrite即写时复制,如果大家对一份资源只有读请求时,那么资源是可以共享的,当某个访问者需要修改资源(写操作)时,就将资源拷贝一份给该访问者使用。即资源的拷贝被延迟到了第一次"写"的时候。CopyOnWrite最广为人知的两个应用场景,一个是Unix like系统fork调用产生的子进程共享父进程的地址空间,知道写操作才会拷贝一份。另一个是java中的copyonwrite容器,用于多线程并发情况下的高效访问,cookshell上有对copyonwrite容器的详细介绍。

web开发中的惰性加载与惰性预加载:

在web前端和APP开发中,当提到惰性加载或者动态加载,大家往往会想到分页、轮播图、瀑布流,这些都体现了惰性加载的思想。其中,瀑布流在出诸多图片分享网站中使用非常广泛,比如花瓣网,当滑动到屏幕底部的时候才会去加载新的内容。为什么要使用惰性加载,第一个是用户体验的问题,图片资源流量比较大,一次加载太多对服务器和浏览器压力都很大,对带宽要求也很高;另外,可能用户根本就不会滑动到最下面,多加载的内容就白白浪费了。 
当然太”Lazy”了也是不好的,总不能让玩家滑动到底部才一边显示loading icon,一边开始加载。为了提高用户体验,这类网站也会做预加载(predictive loading),即多准备一两页的内容,或者在滑屏到一定程度时开始加载新的一页,不过这样的预加载也是惰性预加载(lazy predictive loading)。

总结:

本文列举了惰性计算在编程中的一些具体例子,希望能给自己以及大家有所启发,在以后遇到问题的时候多一种解决思路。由于本人编程领域以及编程语言的局限性,肯定还有诸多遗漏,欢迎大家在评论里补充。
references:
lazy loading
lazy initializing
lazy evaluation
Short-circuit_evaluation
CopyOnWrite
Dirty Flag模式及其应用
python yield generator 详解

lazy ideas in programming(编程中的惰性思想)相关推荐

  1. python惰性求值的特点_C#教程之C#函数式编程中的惰性求值详解

    https://www.xin3721.com/eschool/python.html 惰性求值 在开始介绍今天要讲的知识之前,我们想要理解严格求值策略和非严格求值策略之间的区别,这样我们才能够深有体 ...

  2. 函数式编程中的副作用概念

    前言 为了清楚起见,请记住,副作用不是必需的坏事,有时副作用是有用的(尤其是在函数式编程范式之外). 今天聊一聊函数式编程中的隔离思想,它所想隔离的就是"副作用" 我们先从其他角度 ...

  3. 2010年《斯坦福大学开放课程: 编程方法》(Open Stanford Course : Programming Methodology)[中英双语字幕][RMVB]更新中

    <斯坦福大学开放课程: 编程方法>(Open Stanford Course : Programming Methodology)[中英双语字幕][RMVB] http://www.ver ...

  4. C#中面向对象编程中的函数式编程

    目录 介绍 面向对象编程中仿真的函数式编程技术 粒度不匹配 面向对象的函数式编程构造 相互关系函数式编程/面向对象程序设计 C#中的函数式编程集成 函数级别的代码抽象 操作组合 函数部分应用和局部套用 ...

  5. c语言面向对象编程中的类_C ++中的面向对象编程

    c语言面向对象编程中的类 Object oriented programming, OOP for short, aims to implement real world entities like ...

  6. Linux下高性能网络编程中的几个TCP/IP选项

    Linux下高性能网络编程中的几个TCP/IP选项 转自:http://blog.chinaunix.net/u/12592/showart.php?id=2064847 最近在新的平台上测试程序,以 ...

  7. 编程中python怎么读-编程语言如何在Python中读写文件

    从文件读取和写入文件是任何编程语言的常见需求.任何文件在读写之前都需要打开.大多数编程语言都使用open()方法来打开文件,以便使用文件对象(file object)读写.可以使用不同类型的文件访问模 ...

  8. Linux 编程中的API函数和系统调用的关系【转】

    转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...

  9. 并行编程中的“锁”难题

    在并行程序中,锁的使用会主要会引发两类难题:一类是诸如死锁.活锁等引起的多线程Bug:另一类是由锁竞争引起的性能瓶颈.本文将介绍并行编程中因为锁引发的这两类难题及其解决方案. 1. 用锁来防止数据竞跑 ...

最新文章

  1. 高清视频实时对讲SDK源码
  2. 业务工作流平台设计(七)
  3. chgrp 简明笔记
  4. centos中bash占用cpu_Docker 多种维度限制容器可用的 CPU
  5. 如何给腾讯云域名申请免费的SSL证书
  6. omnicppcomplete php,VIM 常用法 (三)
  7. linux 创建ll,两台linux建立GRE隧道
  8. 【SPFA】重建道路(jzoj 1212)
  9. SonarQube 8.3.x中的Maven项目的测试覆盖率报告
  10. php webp decode.h,HCTF两道web题目
  11. 矩阵快速幂的学习(系统的学习)
  12. java jdbc 表存在_JDBC / Java – 如何检查数据库中是否存在表和列?
  13. Linux下硬盘分区的最佳方案
  14. linux文件权限的设置命令
  15. 使用AVR-GCC编程Arduino
  16. debian配置JDK环境变量
  17. #芯片# R8025(RX-8025T)
  18. Windows 10 创建 删除 合并磁盘分区
  19. 中英文标点符号切换的组合键_电脑键盘常用组合键(快捷键)之——Ctrl键组合...
  20. 8行代码实现发微信撩妹(汉语哦)

热门文章

  1. java1.6 linux_linux java1.6 安装
  2. Pathlib获取当前文件绝对路径
  3. c调用其他类的方法_Java 的 Native 方法——今天又进步了
  4. xgboost算法 c语言,xgboost与sklearn的接口
  5. 在c++使用文件流(初学者必看)
  6. ajax firefox,Jquery ajax不在firefox中工作
  7. linux系统程序PPT,Linux 系统应用与程序设计 问题.ppt
  8. 网页设计简约_简约设计指南
  9. 【业务知识】档案数字化加工处理过程
  10. 笔记-信息化与系统集成技术-供应链管理