浅谈 Lazy
fri3nds 原创
 2018/07/22  技术博文  javascript  clojure  function programming  lazy  observable  lodash《功夫》里火云邪神有一句台词“天下武功,无坚不破,唯快不破!”
李寻欢的飞刀很快,从来没有人看到他出手,因为看到的都已经变成了死人,他神乎其神的刀法,几乎成为传说。
阿飞的剑也很快,出手从来不讲究什么技巧,就是极快的刺出去,很多高手都栽到他的剑下,就是因为低估了他出剑的速度。
那天下武功真的唯快不破?
我们来看看宇宙最权威的解答: 天下武功真的唯快不破?
其实有时候,恰恰相反。
1. Lazy
Lazy evaluation(惰性求值)
在 编程语言理论 中,惰性求值,又译为惰性计算、懒惰求值,也称为传需求调用(call-by-need),是一个 计算机编程 中的一个概念,它的目的是要最小化计算机 要做的工作。它有两个相关而又有区别的含意,可以表示为“延迟求值”和“最小化求值”,本条目专注前者,后者请参见 最小化计算 条目。除可以得到性能的提升外,惰性计算的最重要的好处是它可以构造一个无限的数据类型。
The benefits of lazy evaluation include:
- The ability to define control flow (structures) as abstractions instead of primitives.
- The ability to define potentially infinite data structures. This allows for more straightforward implementation of some algorithms.
- Performance increases by avoiding needless calculations, and error conditions in evaluating compound expressions.
- 大部分的编程语言都是
严格求值
的,例如 Python、C、C++、Java 等。这些语言很多也都可以通过第三方库来实现 lazy。 - Haskell 是默认支持
lazy evaluation
, 是纯lazy,但其有自己的 策略,得以兼顾了 strict evaluation 和 lazy evaluation。 - 再者是 Ocaml、Scheme、Clojure、Scala、C# 这种语言层面支持 lazy 的。
- 其次就是一些支持 fp 的语言,来实现 lazy 的。
2. JavaScript
js 支持 fp 的编程范式,就很容易实现 lazy,
2.1 Lodash
lodash 就用了 lazy 来优化性能;
Strict evaluation, 如下图:
Lazy evaluation, 如下图:
简单的 benchmark
通过一个例子看看 lodash 是如何实现 lazy 的
const _ = require('lodash');
const tt = _.chain([1, 2, 3, 4, 5, 6, 7, 8])
.map(x => x + 1)
.filter(x => x & 1)
.take(3)
.value();
console.log(tt, '=============== tt '); // [ 3, 5, 7 ]
返回的 result 是一个 LodashWrapper
的实例。紧接着调用 map 方法。
其中 result 变成了一个 LazyWrapper
的实例,有转化成 LodashWrapper
实例。
filter 和 take 方法调用和 map 方法大致相同。
调用 take 方法时
两层 while 循环,求解过程就变成上面 Lazy evaluation 的过程了。
2.2 transducer
Rich Hickey,Clojure 的作者在 Clojure1.7 版本正式加入了 Transducer 的概念。
Transduce 可以看成两个动词的组合,即 transform 和 reduce,而 transducer 就是两者的结合。
(reduce + (filter odd? (map inc (range 100000000))))
(def xf
(comp
(map inc)
(filter odd?)))
(transduce xf + 0 (range 100000000))
而 clojure 的源码也是很简单的
(defn transduce
([xform f coll] (transduce xform f (f) coll))
([xform f init coll]
(let [f (xform f)
ret (if (instance? clojure.lang.IReduceInit coll)
(.reduce ^clojure.lang.IReduceInit coll f init)
(clojure.core.protocols/coll-reduce coll f init))]
(f ret))))
这里是不是好像 lodash 里面的 compose 函数啊?
本质上就是的。
2.3 Rx Observable
什么是 rxjs
Rx 是 ReactiveX 的缩写。
ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.
It extends the observer pattern to support sequences of data and/or events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety, concurrent data structures, and non-blocking I/O.
It is sometimes called “functional reactive programming” but this is a misnomer. ReactiveX may be functional, and it may be reactive, but “functional reactive programming” is a different animal. One main point of difference is that functional reactive programming operates on values that change continuously over time, while ReactiveX operates on discrete values that are emitted over time. (See Conal Elliott’s work for more-precise information on functional reactive programming.)
rxjs 的相关概念
Observable & Subject
rxjs 的 lazy
Observable 的 hot/cold
相关文章
Rx 和 Angular2 方面的文章: https://segmentfault.com/blog/caolixiang33
Understanding Transducers in JavaScript: https://medium.com/@roman01la/understanding-transducers-in-javascript-3500d3bd9624
Deep dive into a clojure transducer: https://medium.com/@chpill_/deep-dive-into-a-clojure-transducer-3d4117784fa6
Performance Optimization in Clojure Using Reducers and Transducers — A FORMCEPT Exclusive: https://medium.com/formcept/performance-optimization-in-clojure-using-reducers-and-transducers-a-formcept-exclusive-375955673547
Rxjs 和 React 相关的文章: https://medium.com/@fahad19