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. session多服务器共享的方案

    session的存储了解以前是怎么做的,搞清楚了来龙去脉,才会明白进行共享背后的思想和出发点.我喜欢按照这样的方式来问(或者去搞清楚):为什么要session要进行共享,不共享会什么问题呢? php中 ...

  2. 【转】ATA Secure Erase

    ATA Secure Erase     This procedure describes how to use the hdparm command to issue a Secure Erase  ...

  3. ASP.NET没有魔法——ASP.NET MVC 直连路由(特性路由)

    之前对Controller创建的分析中,知道了Controller的创建是有两个步骤组成,分别是Controller的类型查找以及根据类型创建Controller实例. 在查询Controller的类 ...

  4. 423. Reconstruct Original Digits from English (leetcode)

    Given a non-empty string containing an out-of-order English representation of digits 0-9, output the ...

  5. LeetCode 205. Isomorphic Strings (同构字符串)

    Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the chara ...

  6. java时间格式

    Calendar startdate = Calendar.getInstance(); startdate.setTime(new Date()); //当前时间 startdate.add(Cal ...

  7. [板子]segTree

    segTree 参考:http://www.cnblogs.com/TenosDoIt/p/3453089.html#c 初学者建议先参考上面“一步一步理解线段树”学习理论. 在这里Code分别为区间 ...

  8. CodeForces - 853A Planning (优先队列,贪心)

    Helen works in Metropolis airport. She is responsible for creating a departure schedule. There are n ...

  9. HDU1150 Machine Schedule(二分图最大匹配、最小点覆盖)

    As we all know, machine scheduling is a very classical problem in computer science and has been stud ...

  10. Linux系列教程(十二)——Linux软件包管理之yum在线管理

    上一篇博客我们介绍了rpm包管理之rpm命令管理,我们发现在使用rpm命令手动安装rpm包的时候,会发现安装遇到到的依赖让你痛不欲生,安装一个rpm时会要先先安装某个依赖的rpm,而安装这个依赖的rp ...