<script src=''></script>
download
2Kb minified
What are streams?
Streams is a simple data structure that is much like an array or a linked table, but with some extraordinary abilities attached.
What's special about them?
Unlike arrays, streams is a magical data structure. It can load infinite elements. Yes, you heard it right. This magic of his comes from the ability to lazily execute. This simple term fully indicates that they can load infinite number of elements.
getting Started
If you are willing to spend 10 minutes reading this article, your understanding of programming may be completely changed (unless you have experience in functional programming!). Please be a little patient, let me first introduce the basic functions and operations supported by streams that are very similar to arrays or linked tables. Then I will introduce some of the very interesting features it has like you.
A Stream is a container. It can hold elements. You can use . to get a stream to load some elements. Just pass the desired element into it as a parameter:
var s = ( 10, 20, 30 ); // s is now a stream containing 10, 20, and 30
Simple enough, now s is a stream with 3 elements: 10, 20, and 30; in order. We can use () to view the length of this stream and use (i) to extract an element inside through the index. You can also get the first element of this stream by calling (). Let's do it in practice:
var s = ( 10, 20, 30 );
( () ); // outputs 3
( () ); // outputs 10
( ( 0 ) ); // exactly equivalent to the line above
( ( 1 ) ); // outputs 20
( ( 2 ) ); // outputs 30
This class library has been loaded on this page. If you want to run these examples or write a few sentences yourself, just open the Javascript console of your browser and run it directly.
Let's go ahead and we can also use new Stream() or directly use () to construct an empty stream. You can use the () method to get all the remaining elements in the stream except the first element. If you call the () or () method on an empty stream, an exception will be thrown. You can use () to check if a stream is empty, it returns true or false.
var s = ( 10, 20, 30 );
var t = (); // returns the stream that contains two items: 20 and 30
( () ); // outputs 20
var u = (); // returns the stream that contains one item: 30
( () ); // outputs 30
var v = (); // returns the empty stream
( () ); // prints true
Doing so prints out all elements in a stream:
var s = ( 10, 20, 30 );
while ( !() ) {
( () );
s = ();
}
We have an easy way to achieve this: () will print out all the elements in the stream.
What else can you do with them?
Another simple function is the (min, max ) function. It returns a stream containing natural numbers from min to max.
var s = ( 10, 20 );
(); // prints the numbers from 10 to 20
On this stream, you can use map, filter, and walk. (f) Accepts a parameter f, which is a function, and all elements in the stream will be processed by f; its return value is the stream processed by this function. So, for example, you can use it to do the function of double the number in your stream:
function doubleNumber( x ) {
return 2 * x;
}
var numbers = ( 10, 15 );
(); // prints 10, 11, 12, 13, 14, 15
var doubles = ( doubleNumber );
(); // prints 20, 22, 24, 26, 28, 30
Very cool, isn't it? Similarly, (f) also accepts a parameter f, which is a function, and all elements in the stream will be processed by this function; its return value is also a stream, but only contains elements that can make the f function return true. So, you can use it to filter certain elements in your stream. Let's use this method to build a new stream that contains only odd numbers based on the previous stream:
function checkIfOdd( x ) {
if ( x % 2 == 0 ) {
// even number
return false;
}
else {
// odd number
return true;
}
}
var numbers = ( 10, 15 );
(); // prints 10, 11, 12, 13, 14, 15
var onlyOdds = ( checkIfOdd );
(); // prints 11, 13, 15
Very effective, isn't it? The last (f) method also accepts a parameter f, which is a function. All elements in the stream must be processed by this function, but it will not have any impact on the stream. Our idea of printing all elements in the stream has a new implementation method:
function printItem( x ) {
( 'The element is: ' + x );
}
var numbers = ( 10, 12 );
// prints:
// The element is: 10
// The element is: 11
// The element is: 12
( printItem );
There is also a very useful function: (n), the stream it returns only contains the first n elements in the original stream. This is useful when used to intercept streams:
var numbers = ( 10, 100 ); // numbers 10...100
var fewerNumbers = ( 10 ); // numbers 10...19
();
Some other useful things: ( factor ) will multiply the factor (factor) by all elements in the stream; (t ) will add each element of stream s and the corresponding elements in stream t, and the result after addition is returned. Let's look at a few examples:
var numbers = ( 1, 3 );
var multiplesOfTen = ( 10 );
(); // prints 10, 20, 30
( multiplesOfTen ).print(); // prints 11, 22, 33
Although we currently see operations on numbers, the stream can load anything: strings, booleans, functions, objects, and even other arrays or streams. However, please be careful that some special values cannot be loaded in the stream: null and undefined.
I want me to show your magic!
Now, let's deal with infinite amounts. You don't need to add infinite elements to the stream. For example, in the (low, high) method, you can ignore its second parameter and write it as (low). In this case, the data has no upper limit, so all natural numbers from low to infinite are loaded in this stream. You can also ignore the low parameter, the default value of this parameter is 1. In this case, () returns all natural numbers.
Does this require you to use your infinite amount of memory/time/processing capabilities?
No, no. This is the most exciting part. You can run these codes, and they run very fast, just like a normal array. Here is an example of printing from 1 to 10:
var naturalNumbers = (); // returns the stream containing all natural numbers from 1 and up
var oneToTen = ( 10 ); // returns the stream containing the numbers 1...10
();
You're lying to someone
Yes, I'm lying. The key is that you can think of these structures as infinity, which introduces a new programming paradigm, a programming paradigm dedicated to concise code that makes your code easier to understand and closer to natural mathematics than usual imperative programming. This Javascript class library is very short in itself; it is designed according to this programming paradigm. Let's use it more; we construct two streams, loading all odd numbers and all even numbers respectively.
var naturalNumbers = (); // naturalNumbers is now 1, 2, 3, ...
var evenNumbers = ( function ( x ) {
return 2 * x;
} ); // evenNumbers is now 2, 4, 6, ...
var oddNumbers = ( function ( x ) {
return x % 2 != 0;
} ); // oddNumbers is now 1, 3, 5, ...
( 3 ).print(); // prints 2, 4, 6
( 3 ).print(); // prints 1, 3, 5
Very cool, isn't it? I didn't say anything big, stream is more powerful than arrays. Now, please tolerate me for a few minutes and let me introduce a little more about stream. You can use new Stream() to create an empty stream, and use new Stream( head, functionReturningTail ) to create a non-empty stream. For this non-empty stream, the first parameter you pass in becomes the head element of the stream, while the second parameter is a function that returns the tail of the stream (a stream containing all the remaining elements), which is likely an empty stream. Confused? Let's look at an example:
var s = new Stream( 10, function () {
return new Stream();
} );
// the head of the s stream is 10; the tail of the s stream is the empty stream
(); // prints 10
var t = new Stream( 10, function () {
return new Stream( 20, function () {
return new Stream( 30, function () {
return new Stream();
} );
} );
} );
// the head of the t stream is 10; its tail has a head which is 20 and a tail which
// has a head which is 30 and a tail which is the empty stream.
(); // prints 10, 20, 30
Are you looking for trouble if you have nothing to do? You can do this directly with (10, 20, 30). However, please note that this way we can easily build our infinity stream. Let's make a stream that can be endless:
function ones() {
return new Stream(
// the first element of the stream of ones is 1...
1,
// and the rest of the elements of this stream are given by calling the function ones() (this same function!)
ones
);
}
var s = ones(); // now s contains 1, 1, 1, 1, ...
( 3 ).print(); // prints 1, 1, 1
Note that if you use () on an infinite stream, it will print endlessly and eventually run out of your memory. So, you'd better ( n ) before using (). It is also meaningless to use() on an infinite stream, all, don't do these operations; it will cause an endless loop (trying to reach the end of an endless stream). But for infinity streams you can use (f) and (f). However, (f) is also not easy to use for infinity streams. All, there are some things you have to remember; for infinite streams, be sure to use (n) to remove the limited parts.
Let's see if we can do something more interesting. There is also an interesting way to create streams containing natural numbers:
function ones() {
return new Stream( 1, ones );
}
function naturalNumbers() {
return new Stream(
// the natural numbers are the stream whose first element is 1...
1,
function () {
// and the rest are the natural numbers all incremented by one
// which is obtained by adding the stream of natural numbers...
// 1, 2, 3, 4, 5, ...
// to the infinite stream of ones...
// 1, 1, 1, 1, 1, ...
// yielding...
// 2, 3, 4, 5, 6, ...
// which indeed are the REST of the natural numbers after one
return ones().add( naturalNumbers() );
}
);
}
naturalNumbers().take( 5 ).print(); // prints 1, 2, 3, 4, 5
Careful readers will find out why the second parameter of the newly constructed stream is a function that returns the tail, not the tail itself. This method prevents the infinity of execution cycles by delaying the operation of tail intercept.
Let's look at a more complex example. The following is an exercise left for readers. Please point out what the following code does?
function sieve( s ) {
var h = ();
return new Stream( h, function () {
return sieve( ().filter( function( x ) {
return x % h != 0;
} ) );
} );
}
sieve( ( 2 ) ).take( 10 ).print();
Please take some time to understand the purpose of this code. Unless you have experience in functional programming, most programmers will find this code difficult to understand, so don't feel frustrated if you can't see it right away. Here is a tip for you: find out what the header element of the printed stream is. Then find out what the second element is (the header element of the remaining element); then the third element, then the fourth one. The name of this function can also give you some hints.
If you are interested in this kind of problem,There are some more here。
If you really can't think of what this code does, just run it and take a look at it yourself! This way you can easily understand how it is done.
pay tribute
Streams isn't actually a new idea. Many functional programming languages support this feature. The so-called ‘stream’ is called in the Scheme language, which is a dialect in the LISP language. The Haskell language also supports unlimited lists. These 'take', 'tail', 'head', 'map' and 'filter' names all come from the Haskell language. There are also different but very similar concepts in Python and many other Chinese languages, and they are all called "generators".
These ideas have been circulating in the functional programming community for a long time. However, it is a very new concept for most Javascript programmers, especially those who do not have experience in functional programming.
Many examples and ideas here come fromStructure and Interpretation of Computer Programs This book. If you like these ideas, I highly recommend you to read it; this book is available for free online. It is also the creative source for me to develop this Javascript class library.
download
2Kb minified
What are streams?
Streams is a simple data structure that is much like an array or a linked table, but with some extraordinary abilities attached.
What's special about them?
Unlike arrays, streams is a magical data structure. It can load infinite elements. Yes, you heard it right. This magic of his comes from the ability to lazily execute. This simple term fully indicates that they can load infinite number of elements.
getting Started
If you are willing to spend 10 minutes reading this article, your understanding of programming may be completely changed (unless you have experience in functional programming!). Please be a little patient, let me first introduce the basic functions and operations supported by streams that are very similar to arrays or linked tables. Then I will introduce some of the very interesting features it has like you.
A Stream is a container. It can hold elements. You can use . to get a stream to load some elements. Just pass the desired element into it as a parameter:
var s = ( 10, 20, 30 ); // s is now a stream containing 10, 20, and 30
Simple enough, now s is a stream with 3 elements: 10, 20, and 30; in order. We can use () to view the length of this stream and use (i) to extract an element inside through the index. You can also get the first element of this stream by calling (). Let's do it in practice:
Copy the codeThe code is as follows:
var s = ( 10, 20, 30 );
( () ); // outputs 3
( () ); // outputs 10
( ( 0 ) ); // exactly equivalent to the line above
( ( 1 ) ); // outputs 20
( ( 2 ) ); // outputs 30
This class library has been loaded on this page. If you want to run these examples or write a few sentences yourself, just open the Javascript console of your browser and run it directly.
Let's go ahead and we can also use new Stream() or directly use () to construct an empty stream. You can use the () method to get all the remaining elements in the stream except the first element. If you call the () or () method on an empty stream, an exception will be thrown. You can use () to check if a stream is empty, it returns true or false.
Copy the codeThe code is as follows:
var s = ( 10, 20, 30 );
var t = (); // returns the stream that contains two items: 20 and 30
( () ); // outputs 20
var u = (); // returns the stream that contains one item: 30
( () ); // outputs 30
var v = (); // returns the empty stream
( () ); // prints true
Doing so prints out all elements in a stream:
Copy the codeThe code is as follows:
var s = ( 10, 20, 30 );
while ( !() ) {
( () );
s = ();
}
We have an easy way to achieve this: () will print out all the elements in the stream.
What else can you do with them?
Another simple function is the (min, max ) function. It returns a stream containing natural numbers from min to max.
Copy the codeThe code is as follows:
var s = ( 10, 20 );
(); // prints the numbers from 10 to 20
On this stream, you can use map, filter, and walk. (f) Accepts a parameter f, which is a function, and all elements in the stream will be processed by f; its return value is the stream processed by this function. So, for example, you can use it to do the function of double the number in your stream:
function doubleNumber( x ) {
return 2 * x;
}
var numbers = ( 10, 15 );
(); // prints 10, 11, 12, 13, 14, 15
var doubles = ( doubleNumber );
(); // prints 20, 22, 24, 26, 28, 30
Very cool, isn't it? Similarly, (f) also accepts a parameter f, which is a function, and all elements in the stream will be processed by this function; its return value is also a stream, but only contains elements that can make the f function return true. So, you can use it to filter certain elements in your stream. Let's use this method to build a new stream that contains only odd numbers based on the previous stream:
Copy the codeThe code is as follows:
function checkIfOdd( x ) {
if ( x % 2 == 0 ) {
// even number
return false;
}
else {
// odd number
return true;
}
}
var numbers = ( 10, 15 );
(); // prints 10, 11, 12, 13, 14, 15
var onlyOdds = ( checkIfOdd );
(); // prints 11, 13, 15
Very effective, isn't it? The last (f) method also accepts a parameter f, which is a function. All elements in the stream must be processed by this function, but it will not have any impact on the stream. Our idea of printing all elements in the stream has a new implementation method:
Copy the codeThe code is as follows:
function printItem( x ) {
( 'The element is: ' + x );
}
var numbers = ( 10, 12 );
// prints:
// The element is: 10
// The element is: 11
// The element is: 12
( printItem );
There is also a very useful function: (n), the stream it returns only contains the first n elements in the original stream. This is useful when used to intercept streams:
Copy the codeThe code is as follows:
var numbers = ( 10, 100 ); // numbers 10...100
var fewerNumbers = ( 10 ); // numbers 10...19
();
Some other useful things: ( factor ) will multiply the factor (factor) by all elements in the stream; (t ) will add each element of stream s and the corresponding elements in stream t, and the result after addition is returned. Let's look at a few examples:
Copy the codeThe code is as follows:
var numbers = ( 1, 3 );
var multiplesOfTen = ( 10 );
(); // prints 10, 20, 30
( multiplesOfTen ).print(); // prints 11, 22, 33
Although we currently see operations on numbers, the stream can load anything: strings, booleans, functions, objects, and even other arrays or streams. However, please be careful that some special values cannot be loaded in the stream: null and undefined.
I want me to show your magic!
Now, let's deal with infinite amounts. You don't need to add infinite elements to the stream. For example, in the (low, high) method, you can ignore its second parameter and write it as (low). In this case, the data has no upper limit, so all natural numbers from low to infinite are loaded in this stream. You can also ignore the low parameter, the default value of this parameter is 1. In this case, () returns all natural numbers.
Does this require you to use your infinite amount of memory/time/processing capabilities?
No, no. This is the most exciting part. You can run these codes, and they run very fast, just like a normal array. Here is an example of printing from 1 to 10:
Copy the codeThe code is as follows:
var naturalNumbers = (); // returns the stream containing all natural numbers from 1 and up
var oneToTen = ( 10 ); // returns the stream containing the numbers 1...10
();
You're lying to someone
Yes, I'm lying. The key is that you can think of these structures as infinity, which introduces a new programming paradigm, a programming paradigm dedicated to concise code that makes your code easier to understand and closer to natural mathematics than usual imperative programming. This Javascript class library is very short in itself; it is designed according to this programming paradigm. Let's use it more; we construct two streams, loading all odd numbers and all even numbers respectively.
Copy the codeThe code is as follows:
var naturalNumbers = (); // naturalNumbers is now 1, 2, 3, ...
var evenNumbers = ( function ( x ) {
return 2 * x;
} ); // evenNumbers is now 2, 4, 6, ...
var oddNumbers = ( function ( x ) {
return x % 2 != 0;
} ); // oddNumbers is now 1, 3, 5, ...
( 3 ).print(); // prints 2, 4, 6
( 3 ).print(); // prints 1, 3, 5
Very cool, isn't it? I didn't say anything big, stream is more powerful than arrays. Now, please tolerate me for a few minutes and let me introduce a little more about stream. You can use new Stream() to create an empty stream, and use new Stream( head, functionReturningTail ) to create a non-empty stream. For this non-empty stream, the first parameter you pass in becomes the head element of the stream, while the second parameter is a function that returns the tail of the stream (a stream containing all the remaining elements), which is likely an empty stream. Confused? Let's look at an example:
Copy the codeThe code is as follows:
var s = new Stream( 10, function () {
return new Stream();
} );
// the head of the s stream is 10; the tail of the s stream is the empty stream
(); // prints 10
var t = new Stream( 10, function () {
return new Stream( 20, function () {
return new Stream( 30, function () {
return new Stream();
} );
} );
} );
// the head of the t stream is 10; its tail has a head which is 20 and a tail which
// has a head which is 30 and a tail which is the empty stream.
(); // prints 10, 20, 30
Are you looking for trouble if you have nothing to do? You can do this directly with (10, 20, 30). However, please note that this way we can easily build our infinity stream. Let's make a stream that can be endless:
Copy the codeThe code is as follows:
function ones() {
return new Stream(
// the first element of the stream of ones is 1...
1,
// and the rest of the elements of this stream are given by calling the function ones() (this same function!)
ones
);
}
var s = ones(); // now s contains 1, 1, 1, 1, ...
( 3 ).print(); // prints 1, 1, 1
Note that if you use () on an infinite stream, it will print endlessly and eventually run out of your memory. So, you'd better ( n ) before using (). It is also meaningless to use() on an infinite stream, all, don't do these operations; it will cause an endless loop (trying to reach the end of an endless stream). But for infinity streams you can use (f) and (f). However, (f) is also not easy to use for infinity streams. All, there are some things you have to remember; for infinite streams, be sure to use (n) to remove the limited parts.
Let's see if we can do something more interesting. There is also an interesting way to create streams containing natural numbers:
Copy the codeThe code is as follows:
function ones() {
return new Stream( 1, ones );
}
function naturalNumbers() {
return new Stream(
// the natural numbers are the stream whose first element is 1...
1,
function () {
// and the rest are the natural numbers all incremented by one
// which is obtained by adding the stream of natural numbers...
// 1, 2, 3, 4, 5, ...
// to the infinite stream of ones...
// 1, 1, 1, 1, 1, ...
// yielding...
// 2, 3, 4, 5, 6, ...
// which indeed are the REST of the natural numbers after one
return ones().add( naturalNumbers() );
}
);
}
naturalNumbers().take( 5 ).print(); // prints 1, 2, 3, 4, 5
Careful readers will find out why the second parameter of the newly constructed stream is a function that returns the tail, not the tail itself. This method prevents the infinity of execution cycles by delaying the operation of tail intercept.
Let's look at a more complex example. The following is an exercise left for readers. Please point out what the following code does?
Copy the codeThe code is as follows:
function sieve( s ) {
var h = ();
return new Stream( h, function () {
return sieve( ().filter( function( x ) {
return x % h != 0;
} ) );
} );
}
sieve( ( 2 ) ).take( 10 ).print();
Please take some time to understand the purpose of this code. Unless you have experience in functional programming, most programmers will find this code difficult to understand, so don't feel frustrated if you can't see it right away. Here is a tip for you: find out what the header element of the printed stream is. Then find out what the second element is (the header element of the remaining element); then the third element, then the fourth one. The name of this function can also give you some hints.
If you are interested in this kind of problem,There are some more here。
If you really can't think of what this code does, just run it and take a look at it yourself! This way you can easily understand how it is done.
pay tribute
Streams isn't actually a new idea. Many functional programming languages support this feature. The so-called ‘stream’ is called in the Scheme language, which is a dialect in the LISP language. The Haskell language also supports unlimited lists. These 'take', 'tail', 'head', 'map' and 'filter' names all come from the Haskell language. There are also different but very similar concepts in Python and many other Chinese languages, and they are all called "generators".
These ideas have been circulating in the functional programming community for a long time. However, it is a very new concept for most Javascript programmers, especially those who do not have experience in functional programming.
Many examples and ideas here come fromStructure and Interpretation of Computer Programs This book. If you like these ideas, I highly recommend you to read it; this book is available for free online. It is also the creative source for me to develop this Javascript class library.
If you like other syntax forms of streams, you can try it, or, if you use ,node-lazyMaybe it's more suitable for you.
If you like CoffeeScript, Michael Blume is porting it to CoffeeScript to create coffeestream.
Thank you for reading!
I hope you can gain something and like it. This library is free, so if you like it, or it can help in some way, you can consider buying me a hot chocolate drink (I don't drink coffee) or writing me. If you plan to do this, please write down where you are and what you do. I like collecting pictures from all over the world, so please attach the photos you took in your city in the letter!