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. Windows下Mysql5.7开启binlog步骤及注意事项

    1.查看是否开启了binlog:show binary logs; 默认情况下是不开启的. 2.开启binlog:修改mysql的配置文件my.ini.添加如下配置: 该文件默认不允许修改,需要右键“ ...

  2. 【UML 建模】类图介绍

    1.类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础.类图主要是用来显示系统中的类.接口以及它们之间的静态结构和关系的一种静态模型. 2.类的关系有泛化(Generalization). ...

  3. Akka(32): Http:High-Level-Api,Route exception handling

    Akka-http routing DSL在Route运算中抛出的异常是由内向外浮出的:当内层Route未能捕获异常时,外一层Route会接着尝试捕捉,依次向外扩展.Akka-http提供了Excep ...

  4. struts2系列(四):struts2国际化的多种方式

    一.struts2国际化原理 根据不同的Locale读取不同的文本. 例如有两个资源文件: 第一个:message_zh_CN.properties 第二个:message_en_US.propert ...

  5. TreeView简单的动态加载数据

    简单的小记录,省得去看控件属性详情了,基本常用的属于就几个 先是判断根节点是否存在控件中,如果不存在则创建,之前要添加了节点同样的方法 把根节点传到子节点的方法中,再判断是否在根节点里存在子节点,如果 ...

  6. SEO诊断之关于网站收录(转)

    如何让网站被搜索引擎收录?我的网站有收录无排名怎么办?这些网站收录问题估计是seo最应关心的根本问题之一,网站收录都没有何来排名?我整理了每天咨询最多最具代表性的 8 个关于网站收录的问题及其答案统一 ...

  7. C# join子句

    join 子句可用于将来自不同源序列并且在对象模型中没有直接关系的元素相关联. 唯一的要求是每个源中的元素需要共享某个可以进行比较以判断是否相等的值. 例如,食品经销商可能拥有某种产品的供应商列表以及 ...

  8. WebService--axis

    axis WebService虽然现在已经很少使用,但是还是把它的配置过程写出来,开发环境jdk 1.6 服务端: 1,导入需要jar包,自行下载 2,创建WebService接口 public in ...

  9. ContextLoaderListener - 运行原理

    基本概念: servletContext:http://blog.csdn.net/yjw757174266/article/details/45072975 1.  使用ContextLoaderL ...

  10. 在foreach的判断条件里执行方法会有效率问题吗?

    楼猪平时一有空就有看别人代码的习惯,从许多优秀规范的代码中学习到了很多简约高效的写法和画龙点睛的思想精华.但是有的时候也会觉得某些写法很值得玩味.比如刚看到一段代码,在foreach的条件判断里加了一 ...