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),两者对象的代码大致是这样的:
  

 class eager_meta(type):
def __init__(clz, name, bases, dic):
super(eager_meta, clz).__init__(name, bases, dic)
clz._instance = clz() class singleton_eager(object):
__metaclass__ = eager_meta @classmethod
def instance(clz):
return clz._instance class singleton_lazy(object):
__instance = None
@classmethod
def instance(clz):
if clz.__instance is None:
clz.__instance = singleton_lazy()
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函数:
     ret = any(self.calc_and_ret(e) for e in elements)
def self.calc_and_ret(self, e):
# do a lot of calc here which effect self
return True(or False)
    本意是希望对所有的element都计算,然后返回一个结果,但事实上由于短路求值, 可能后面很多的元素都不会再调用calc_and_ret
 

generator:

  在python和javascript语言中都有generator,generator与普通的函数相比,可以多次(甚至无限次)返回,而且返回值是在需要的时候才生成。在python中,下面两段代码非常相似,但事实上差异非常大:
     for x in [i*i for i in xrange(10000)]
      # do sth with i     for x in (i*i for i in xrange(10000)]
      # do sth with i
 
  generator更广泛的应用可以参见《python yield generator 详解》。javascript中generator的语法和使用与python都非常类似,可以参见这篇文章
 

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

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

cache:

  cache也是一种lazy思想,如果之前有计算结果,那么直接复用之前的结果就行了,干嘛还要重新计算呢?而且最开始的缓存内容, 也是在需要的时候才计算的,而不是一开始就计算好。wiki上有python实现的简单例子:
   

 class Fruit:
def __init__(self, item):
self.item = item class Fruits:
def __init__(self):
self.items = {} def get_fruit(self, item):
if item not in self.items:
self.items[item] = Fruit(item) return self.items[item] if __name__ == '__main__':
fruits = Fruits()
print(fruits.get_fruit('Apple'))
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 ideas in programming(编程中的惰性思想)的更多相关文章

  1. lazy ideas in programming

    lazy形容词,懒惰的,毫无疑问是一个贬义词.但是,对于计算机领域,lazy却是非常重要的优化思想:把任务推迟到必须的时刻,好处是避免重复计算,甚至不计算.本文的目的是抛砖引玉,总结一些编程中的laz ...

  2. 编程提取字符串"Java is a programming language"中的各个单词,并打印输出。

    import java.lang.String; import java.util.StringTokenizer; public class StringGetWord{ /* 编程提取字符串&qu ...

  3. 03 Comments in C Programming C编程中的注释

    Comments 注释简介 Let's take a quick break from programming and talk about comments. Comments help progr ...

  4. TCP/IP网络编程中socket的行为

    一. read/write的语义:为什么会阻塞? 先从write说起: #include <unistd.h>ssize_t write(int fd, const void *buf, ...

  5. 浅谈TCP/IP网络编程中socket的行为

    我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统 ...

  6. (转载)Linux 套接字编程中的 5 个隐患

    在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系统的标准特性.事实上,很难找到一种不支持 Sockets API 的现代语言.该 API 相当简单,但新的开 ...

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

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

  8. 在 JNI 编程中避免内存泄漏

    JAVA 中的内存泄漏 JAVA 编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏:JVM 内存中 native memory 的内存泄漏. Java H ...

  9. 【转】资源文件在Delphi编程中的应用

    段东宁 计亚南 (郴州职业技术学院, 湖南 郴州  423000) 摘要: 资源文件是一种能有效地组织.管理和使用资源的文件形式,在软件开发中有着广泛的应用.本文详细介绍了在Delphi编程中资源文件 ...

随机推荐

  1. 正确理解Mysql的列索引和多列索引

    MySQL数据库提供两种类型的索引,如果没正确设置,索引的利用效率会大打折扣却完全不知问题出在这. CREATE TABLE test ( id         INT NOT NULL, last_ ...

  2. ELK系列~log4-nxlog-Fluentd-elasticsearch写json数据需要注意的几点

    经验与实践 前两篇文章里我们介绍了nxlog的日志收集和转发<ELK系列~Nxlog日志收集加转发(解决log4日志换行导致json转换失败问题)>,今天我们主要总结一下,在与log4和f ...

  3. excel中添加拼接行

    Sub 万途标签()Dim iFor i = 1 To Sheets.Count    If Sheets(i).Name = "数据表" Then        If MsgBo ...

  4. 树莓派.安装Samba环境

    适用于树莓派3 树莓派装好系统后, 为了方便传文件到树莓派, 建议使用Samba这类文件夹级别的应用, 比ftp方便多了 如果你想把树莓派变成Nas, Samba也是不可或缺的应用 通过samba服务 ...

  5. 5分钟看懂svg path 路径的所有命令(更有API解释、有图、有图文对比解析)

    友情提示:更多详情.每个命令的例子.参数变化对比图文详解,欢迎关注九十七度的博客:SVG<Path>命令详解 M = moveto M x y 移动到指定坐标,xy分别为x轴和y轴的坐标点 ...

  6. Linux上安装二进制文件MySQL详解

    前言:昨天晚上搞了很久,终于搞清楚mysql的安装配置了,我真是太low了.当我在云服务器上登进Mysql时,真是高兴哈哈,咱一步一步来,彻底搞懂Mysql的安装配置. 我的安装环境: 阿里云服务器 ...

  7. 使用SimpleXML解析xml文件数据

    最近工作要求从一个XML文档中批量读取APK应用数据,自然想到用SimpleXML.经过一段时间摸索,终于成功解析,现在将思路以及代码做下记录: xml文件格式大致如下: <?xml versi ...

  8. Eddy's picture(最小生成树)

    Eddy's picture Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Tota ...

  9. C++图形编程之graphics.h头文件

    graphics.h是Turbo C的针对DOS下的一个C语言图形库,如果要用的话应该用TC的编译器来编译,但是如果需要在vc及vs环境中使用graphics.h的功能,则可以选择下载EasyX图形库 ...

  10. SpringMVC , Spring , MyBatis 文件上传

    学习一下文件上传下载,为图片上传做准备,感觉有一个世纪没玩过上传下载了,边敲代码边记录,请各路大神指教: 参考:http://blog.csdn.net/wjycgl/article/details/ ...