CoffeeScriptstream.js的用途和认证(中文)

streams是什么?

Streams
是三个操作轻易的数据结构,很像数组或链接表,但附加了某些非凡的能力。

它们有何样特别之处?

跟数组不平等,streams是3个有法力的数据结构。它能够装载用不完多的成分。是的,你没听错。他的那种吸引力来自于具有延后(lazily)执行的手艺。这大约的术语完全能申明它们得以加载无穷多的因素。

入门

假定您愿意花拾分钟的年华来阅读这篇作品,你对编制程序的认识有非常的大恐怕会被全然的改动(除非您有函数式编制程序的阅历!)。请稍有耐心,让本人来先介绍一下streams扶助的跟数组或链接表很相近的基本功效操作。然后作者会像你介绍部分它具有的不胜风趣的表征。

Stream
是一种容器。它能包容成分。你能够使用 Stream.make 来让三个stream加载一些成分。只须要把想要的因素当成参数传进去:

  1. var s = Stream.make( 10, 20, 30 ); // s is now a stream containing 10, 20, and 30  

足足简单吗,以往 s 是1个具有一个因素的stream: 10, 20, and 30;
有各类的。我们可以利用 s.length() 来查阅这一个stream的长度,用 s.item( i
) 通过索引抽取在那之中的某些成分。你仍是能够经过调用 s.head() 来得到那几个stream
的首先个因素。让大家实际操作一下:

  1. var s = Stream.make( 10, 20, 30 );  
  2. console.log( s.length() );  // outputs 3  
  3. console.log( s.head() );    // outputs 10  
  4. console.log( s.item( 0 ) ); // exactly equivalent to the line above  
  5. console.log( s.item( 1 ) ); // outputs 20  
  6. console.log( s.item( 2 ) ); // outputs 30  

本页面已经加载了这一个 stream.js
类库。假如您想运维那个事例或协调写几句,张开你的浏览器的Javascript调节台直接运营就行了。

小编们承袭,我们也得以采取 new Stream() 或
直接选取 Stream.make() 来组织多个空的stream。你能够行使 s.tail() 方法来博取stream里除了头个要素外的结余全体因素。若是您在一个空stream上调用 s.head() 或 s.tail() 方法,会抛出贰个足够。你能够应用 s.empty() 来检查四个stream是不是为空,它回到 true 或 false。

  1. var s = Stream.make( 10, 20, 30 );  
  2. var t = s.tail();         // returns the stream that contains two items: 20 and 30  
  3. console.log( t.head() );  // outputs 20  
  4. var u = t.tail();         // returns the stream that contains one item: 30  
  5. console.log( u.head() );  // outputs 30  
  6. var v = u.tail();         // returns the empty stream  
  7. console.log( v.empty() ); // prints true  

那般做能够打字与印刷出三个stream里的具备因素:

  1. var s = Stream.make( 10, 20, 30 );  
  2. while ( !s.empty() ) {  
  3.     console.log( s.head() );  
  4.     s = s.tail();  
  5. }  

大家有个轻便的点子来落到实处这些: s.print() 将会打字与印刷出stream里的装有因素。

用它们还能做什么?

另2个便当的功力是 Stream.range( min, max
) 函数。它会重返贰个含有有从 min 到 max 的自然数的stream。

  1. var s = Stream.range( 10, 20 );  
  2. s.print(); // prints the numbers from 10 to 20  

在那个stream上,你能够选择 map, filter, 和 walk 等效果。 s.map( f
) 接受1个参数 f,它是二个函数,
stream里的保有因素都将会被f处理一次;它的重临值是透过那些函数处理过的stream。所以,举个例证,你能够用它来成功让你的
stream 里的数字翻倍的职能:

  1. function doubleNumber( x ) {  
  2.     return 2 * x;  
  3. }  
  4.   
  5. var numbers = Stream.range( 10, 15 );  
  6. numbers.print(); // prints 10, 11, 12, 13, 14, 15  
  7. var doubles = numbers.map( doubleNumber );  
  8. doubles.print(); // prints 20, 22, 24, 26, 28, 30  

很酷,不是啊?相似的, s.filter( f
) 也经受三个参数f,是2个函数,stream里的具备因素都将经过这么些函数处理;它的再次来到值也是个stream,但只包含能让f函数重回true的成分。所以,你能够用它来过滤到您的stream里有个别特定的要素。让我们来用那些法子在头里的stream基础上构建1个只含有奇数的新stream:

  1. function checkIfOdd( x ) {  
  2.     if ( x % 2 == 0 ) {  
  3.         // even number  
  4.         return false;  
  5.     }  
  6.     else {  
  7.         // odd number  
  8.         return true;  
  9.     }  
  10. }  
  11. var numbers = Stream.range( 10, 15 );  
  12. numbers.print();  // prints 10, 11, 12, 13, 14, 15  
  13. var onlyOdds = numbers.filter( checkIfOdd );  
  14. onlyOdds.print(); // prints 11, 13, 15  

很得力,不是啊?最终的3个s.walk( f
)方法,也是接受一个参数f,是一个函数,stream里的全数因素都要透过这几个函数处理,但它并不会对这么些stream做别的的熏陶。我们打印stream里全体因素的想法有了新的兑现情势:

  1. function printItem( x ) {  
  2.     console.log( ‘The element is: ‘ + x );  
  3. }  
  4. var numbers = Stream.range( 10, 12 );  
  5. // prints:  
  6. // The element is: 10  
  7. // The element is: 11  
  8. // The element is: 12  
  9. numbers.walk( printItem );  

再有二个很有用的函数: s.take( n
),它回到的stream只包含原始stream里第前n个成分。当用来截取stream时,那很有用:

  1. var numbers = Stream.range( 10, 100 ); // numbers 10…100  
  2. var fewerNumbers = numbers.take( 10 ); // numbers 10…19  
  3. fewerNumbers.print();  

其它壹些使得的事物:s.scale( factor
) 会用factor(因子)乘以stream里的富有因素; s.add( t ) 会让
stream s 每一种成分和stream t里对应的因素相加,再次来到的是相加后的结果。让我们来看几个例子:

  1. var numbers = Stream.range( 1, 3 );  
  2. var multiplesOfTen = numbers.scale( 10 );  
  3. multiplesOfTen.print(); // prints 10, 20, 30  
  4. numbers.add( multiplesOfTen ).print(); // prints 11, 22, 33  

即使我们当前看到的都是对数字进行操作,但stream里能够装载别的的东西:字符串,布尔值,函数,对象;甚至其余的数组或stream。但是,请留心早晚,stream里不可能装载一些奇特的值:null 和 undefined。

想作者显得你的魔力!

今后,让我们来处理无穷多。你不供给往stream增多无穷多的要素。例如,在Stream.range(
low, high ) 那一个法子中,你能够忽略掉它的第3个参数,写成 Stream.range(
low ) ,
那种状态下,数据尚未了上限,于是那一个stream里就装载了独具从 low
到无穷大的自然数。你也能够把low 参数也不经意掉,这一个参数的缺省值是一。那种气象中,Stream.range()
再次来到的是持有的自然数。

这亟需用上你无穷多的内部存款和储蓄器/时间/处理技能呢?

不,不会。那是最卓绝的部分。你能够运作这个代码,它们跑的丰盛快,如同一个平常的数组。上面是2个打字与印刷从
壹 到 10 的例证:

  1. var naturalNumbers = Stream.range(); // returns the stream containing all natural numbers from 1 and up  
  2. var oneToTen = naturalNumbers.take( 10 ); // returns the stream containing the numbers 1…10  
  3. oneToTen.print();  

您在骗人

毋庸置疑,小编在骗人。关键是你能够把这个构造想成无穷大,那就引入了一种新的编制程序范式,一种致力于轻易的代码,让您的代码比普通的命令式编制程序更易于理解、更靠近自然数学的编制程序范式。那一个Javascript类库本身就异常的短小;它是比照那种编制程序范式设计出来的。让大家来多用一用它;大家组织八个stream,分别装载全数的奇数和装有的偶数。

  1. var naturalNumbers = Stream.range(); // naturalNumbers is now 1, 2, 3, …  
  2. var evenNumbers = naturalNumbers.map( function ( x ) {  
  3.     return 2 * x;  
  4. } ); // evenNumbers is now 2, 4, 6, …  
  5. var oddNumbers = naturalNumbers.filter( function ( x ) {  
  6.     return x % 2 != 0;  
  7. } ); // oddNumbers is now 1, 3, 5, …  
  8. evenNumbers.take( 3 ).print(); // prints 2, 4, 6  
  9. oddNumbers.take( 3 ).print(); // prints 1, 3, 5  

很酷,不是啊?小编没吹牛,stream比数组的效益越来越强硬。今后,请容忍本人几分钟,让自家来多介绍1些有关stream的作业。你能够动用 new
Stream() 来创设二个空的stream,用 new Stream( head,
functionReturningTail
) 来创制1个非空的stream。对于那些非空的stream,你传入的第1个参数成为那个stream的头成分,而第二个参数是贰个函数,它回到stream的尾巴(二个含有有多余全数因素的stream),很恐怕是一个空的stream。质疑吗?让大家来看1个例子:

  1. var s = new Stream( 10, function () {  
  2.     return new Stream();  
  3. } );  
  4. // the head of the s stream is 10; the tail of the s stream is the empty stream  
  5. s.print(); // prints 10  
  6. var t = new Stream( 10, function () {  
  7.     return new Stream( 20, function () {  
  8.         return new Stream( 30, function () {  
  9.             return new Stream();  
  10.         } );  
  11.     } );  
  12. } );  
  13. // the head of the t stream is 10; its tail has a head which is 20 and a tail which  
  14. // has a head which is 30 and a tail which is the empty stream.  
  15. t.print(); // prints 10, 20, 30  

没事找事吗?直接用Stream.make( 拾, 20, 30
)就可以做那么些。然而,请小心,这种方法大家得以轻便的构建大家的无边大stream。让我们来做贰个可以无穷数不完的stream:

  1. function ones() {  
  2.     return new Stream(  
  3.         // the first element of the stream of ones is 1…  
  4.         1,  
  5.         // and the rest of the elements of this stream are given by calling the function ones() (this same function!)  
  6.         ones  
  7.     );  
  8. }  
  9.   
  10. var s = ones();      // now s contains 1, 1, 1, 1, …  
  11. s.take( 3 ).print(); // prints 1, 1, 1  

请留意,要是你在叁个可是大的stream上利用 s.print(),它会无休无止的打字与印刷下去,最终耗尽你的内部存款和储蓄器。所以,你最佳在行使s.print()前先s.take(
n
)。在三个无穷大的stream上行使s.length()也是抽象的,全部,不要做那一个操作;它会形成3个成千上万的循环(试图达到一个成千上万的stream的数不清)。然则对于无穷大stream,你可以利用s.map(
f ) 和 s.filter( f )。但是,s.walk( f
)对于无穷大stream也是糟糕用。全数,有个别业务你要切记;
对于无穷大的stream,一定要选用s.take( n )收取有限的1对。

让大家看看能否做一些更加好玩的事情。还有一个妙趣横生的能创立包涵自然数的stream方式:

  1. function ones() {  
  2.     return new Stream( 1, ones );  
  3. }  
  4. function naturalNumbers() {  
  5.     return new Stream(  
  6.         // the natural numbers are the stream whose first element is 1…  
  7.         1,  
  8.         function () {  
  9.             // and the rest are the natural numbers all incremented by one  
  10.             // which is obtained by adding the stream of natural numbers…  
  11.             // 1, 2, 3, 4, 5, …  
  12.             // to the infinite stream of ones…  
  13.             // 1, 1, 1, 1, 1, …  
  14.             // yielding…  
  15.             // 2, 3, 4, 5, 6, …  
  16.             // which indeed are the REST of the natural numbers after one  
  17.             return ones().add( naturalNumbers() );  
  18.         }   
  19.     );  
  20. }  
  21. naturalNumbers().take( 5 ).print(); // prints 1, 2, 3, 4, 5  

细心的读者会意识怎么新布局的stream的第二参数是多少个再次来到尾巴部分的函数、而不是尾巴部分本人的缘由了。那种办法得以由此延迟尾部截取的操作来防护进行进入无穷尽的实行周期。

让大家来看一个更扑朔迷离的例子。上面包车型客车是给读者留下的贰个演练,请提议上面那段代码是做什么样的?

  1. function sieve( s ) {  
  2.     var h = s.head();  
  3.     return new Stream( h, function () {  
  4.         return sieve( s.tail().filter( function( x ) {  
  5.             return x % h != 0;  
  6.         } ) );  
  7.     } );  
  8. }  
  9. sieve( Stream.range( 2 ) ).take( 10 ).print();  

请一定要花些时间能精通那段代码的用处。除非有函数式编制程序经验,大许多的程序员都会意识那段代码很难知晓,所以,借使你不可能马上看出来,不要感觉消沉。给您或多或少提示:寻找被打字与印刷的stream的头成分是如何。然后寻觅第3个因素是何等(余下的因素的头成分);然后第伍个要素,然后第六个。这一个函数的称谓也能给您有的提示。假若你对那种难点感兴趣,此刻还有一部分

如果你真的想不出那段代码是做怎样的,你就运转一下它,本人看壹看!那样你就很轻便驾驭它是如何是好的了。

致敬

Streams
实际上不是多个新的想法。好些个的函数式的编制程序语言都帮助那种特点。所谓‘stream’是Scheme语言里的叫法,Scheme是LISP语言的一种方言。Haskell语言也支撑不过大列表(list)。那么些’take’,
‘tail’, ‘head’, ‘map’ 和 ‘filter’
名字都来自于Haskell语言。Python和其它众多中语言中也存在纵然分歧但很相似的那种概念,它们都被称作”发生器(generators)”。

那个思虑来函数式编制程序社区里曾经流传了很久了。可是,对于大繁多的Javascript程序员来说却是2个很新的概念,尤其是那个未有函数式编程经验的人。

此地繁多的事例和创新意识都以源于 Structure and Interpretation of Computer
Programs
 那本书。假若您欣赏那些想法,小编中度推荐你读1读它;那本书可以在网上无偿获得。它也是自家付出那几个Javascript类库的新意来源于。

一旦您喜爱此外语法格局的stream,你能够试一下linq.js,大概,假若你利用
node.js, node-lazy 只怕更切合您。

就算您若是喜欢 CoffeeScript 的话, 迈克尔 Blume 正在把 stream.js 移植到
CoffeeScript
上,创立出 coffeestream

多谢您的读书!

本身梦想你能有所收获,并欣赏上
stream.js。这一个类库是免费的,所以,假使你喜悦它,或它能在某地点提供了支持,你能够思索替本人买一杯热巧克力饮料 (小编不喝咖啡)
或许 写信给笔者。借使您打算那样做,请写清你是哪个地方人,做哪些的。笔者很欢娱搜聚世界外省的图纸,所以,信中请附上你在你的城市里拍的肖像!