lab42_streams 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +128 -6
- data/lib/lab42/stream/auto_import.rb +0 -3
- data/lib/lab42/stream/class_methods.rb +1 -1
- data/lib/lab42/stream/kernel.rb +12 -5
- data/lib/lab42/stream/version.rb +1 -1
- metadata +8 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e8f831b639a52dd52db85790b4fa4f7a558d4141b8deaf0ba7cd406f59970578
|
4
|
+
data.tar.gz: 58bfe3bfe2d31b694061a704ec6fbb30bbf901fe0e4134f91026edaceac22913
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17bb41d95481d19badab24f90ec1935f4450c19b65435000ba93721b602934506c665a239f524241ab4cff7ef127a84b7cdf4312cba74bb7b4b0eab2ac4c3ba2
|
7
|
+
data.tar.gz: 726f43fcf1c28fc99de3b1792fda975bdb4b9d7ed349aa380b95192af53b048def7717193065d6917a7e235cbddf36eb4ad63e2aa617eceeaa74bb15bd873f00
|
data/README.md
CHANGED
@@ -1,14 +1,136 @@
|
|
1
|
+
[](https://travis-ci.org/RobertDober/lab42_streams)
|
2
|
+
[](https://codeclimate.com/github/RobertDober/lab42_streams)
|
3
|
+
[](https://codeclimate.com/github/RobertDober/lab42_streams)
|
4
|
+
[](https://codeclimate.com/github/RobertDober/lab42_streams)
|
5
|
+
[](http://badge.fury.io/rb/lab42_streams)
|
6
|
+
|
1
7
|
# lab42\_streams
|
2
8
|
|
3
|
-
Bringing Streams to Ruby
|
9
|
+
## Bringing Streams to Ruby
|
4
10
|
|
5
11
|
An excellent introduction into `Streams` can be found [here](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/6a-streams-part-1/)
|
6
12
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
## Basic Stream Tutorial
|
14
|
+
|
15
|
+
Streams are lazy, immutable lists, or for the purists, lazy cons cells (well the tail/cdr is lazy, the head/car is not).
|
16
|
+
|
17
|
+
A first example
|
18
|
+
|
19
|
+
### Infinite Streams
|
20
|
+
|
21
|
+
Given the following definition
|
22
|
+
|
23
|
+
```ruby :include
|
24
|
+
def fibs a=0, b=1
|
25
|
+
cons_stream a do
|
26
|
+
fibs b, a+b
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
The following spec will be satisfied
|
32
|
+
|
33
|
+
```ruby :example
|
34
|
+
expect(fibs.drop(1000).head).
|
35
|
+
to eq(43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875)
|
36
|
+
```
|
37
|
+
|
38
|
+
There are several things to remember here:
|
39
|
+
|
40
|
+
* The tail of a stream is *always* provided as block or lambda, the only way in ruby to
|
41
|
+
implement a normal order parameter.
|
42
|
+
|
43
|
+
* The result of the tail (that is when the delay or promise the tail defines is forced or realised)
|
44
|
+
must be a `Stream`. This has to be automated into your reasoning about `Streams` lest you will
|
45
|
+
have difficulties to come up with stream based solutions.
|
46
|
+
|
47
|
+
* When the promise of the tail is forced the stack frame of the `cons_stream` call is not active
|
48
|
+
any more, there will be no stack overflow.
|
49
|
+
|
50
|
+
|
51
|
+
### Transformation Chain
|
52
|
+
|
53
|
+
One major advantage of streams (and lazy evaluation in general) is that transformations can be composed without any performance penality.
|
54
|
+
|
55
|
+
While for example the following code would be terribly inefficent
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
elements = { 2 => "two", 4 => "four" }
|
59
|
+
list = 1..2 # but imagine a very large value of 2
|
60
|
+
list.map{ |x| x * 2 }.map{ |x| elements[x] }.map(&:reverse)
|
61
|
+
```
|
62
|
+
|
63
|
+
the following stream based code is not.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
|
67
|
+
translation = { [true,true] => "fizzbuzz", [true, false] => "fizz", [false, true] => "buzz" }
|
68
|
+
integers = Stream.iterate 0, :succ
|
69
|
+
fizzbuzz = integers
|
70
|
+
.reject{ |x| (x%100).zero? }
|
71
|
+
.map{ |i| [(i%3).zero?,(i%5).zero?,i] }
|
72
|
+
.map{ |f,b,i| translation.fetch([f,b],i) }
|
73
|
+
```
|
74
|
+
|
75
|
+
The reason for this is that, up to now, no single computation has been done, but _promises_ for doing so
|
76
|
+
have been registered. Only when we eventually force values these computations will be executed and then
|
77
|
+
it will make little difference if we execute one complex computation or five simple ones.
|
78
|
+
|
79
|
+
And as we operate on **infinite** streams it becomes obvious that the implementation must delay up to the end.
|
80
|
+
|
81
|
+
### Memoization
|
82
|
+
|
83
|
+
The fourth point to know about `Streams` is that:
|
84
|
+
|
85
|
+
* All promises are **memoized**.
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
Only for that reason the following naïve, but elegant implementation of the fibonacci sequence has O(N) runtime
|
90
|
+
characteristics, and the result can be computed:
|
91
|
+
|
92
|
+
```ruby :include
|
93
|
+
|
94
|
+
let(:fibs1) do
|
95
|
+
cons_stream(0){
|
96
|
+
cons_stream(1){
|
97
|
+
combine_streams fibs1, fibs1.tail, :+
|
98
|
+
}
|
99
|
+
}
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
```ruby :example
|
104
|
+
expect(fibs1.drop(1000).head)
|
105
|
+
.to eq(43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875)
|
106
|
+
|
107
|
+
```
|
108
|
+
|
109
|
+
|
110
|
+
## Finite Streams
|
111
|
+
|
112
|
+
Finite Streams are implemented the same way LISP imlements lists, by providing an _End_Marker_. What is `nil` in LISP
|
113
|
+
is `empty_stream` in Ruby. As a matter of fact the `empty_stream` method returns a singleton called `Lab42::Stream::Empty` which
|
114
|
+
is also accessible via `EmptyStream` if you required the lib with `require 'lab42/stream/auto_import'` which is true for the demos.
|
115
|
+
|
116
|
+
Here is an example of a finite stream
|
117
|
+
|
118
|
+
```ruby :include
|
119
|
+
let(:digits){ finite_stream( 0..9 ) }
|
120
|
+
```
|
121
|
+
|
122
|
+
Now the following all hold
|
123
|
+
|
124
|
+
```ruby :example
|
125
|
+
expect(digits.drop(9).head).to eq(9)
|
126
|
+
```
|
11
127
|
|
128
|
+
```ruby :example
|
129
|
+
expect(digits.drop(10)).to be_empty
|
130
|
+
```
|
12
131
|
|
132
|
+
or alternatively
|
13
133
|
|
14
|
-
|
134
|
+
```ruby :example
|
135
|
+
expect(digits.drop(10)).to eq(EmptyStream)
|
136
|
+
```
|
data/lib/lab42/stream/kernel.rb
CHANGED
@@ -39,11 +39,9 @@ module Kernel
|
|
39
39
|
when Range
|
40
40
|
_finite_stream_from_range enum
|
41
41
|
when Array
|
42
|
-
|
43
|
-
cons_stream(enum.first){ finite_stream(enum.drop(1)) }
|
42
|
+
_finite_stream_from_ary enum
|
44
43
|
when Hash
|
45
|
-
|
46
|
-
cons_stream(enum.first){ finite_stream(enum.without(enum.first.first)) }
|
44
|
+
_finite_stream_from_hash enum
|
47
45
|
when Enumerator
|
48
46
|
_finite_stream_from_enumerator! enum.to_enum
|
49
47
|
else
|
@@ -51,7 +49,6 @@ module Kernel
|
|
51
49
|
end
|
52
50
|
end
|
53
51
|
|
54
|
-
|
55
52
|
def flatmap stream, *args, &blk
|
56
53
|
stream.flatmap( *args, &blk )
|
57
54
|
end
|
@@ -89,10 +86,20 @@ module Kernel
|
|
89
86
|
|
90
87
|
private
|
91
88
|
|
89
|
+
def _finite_stream_from_ary ary
|
90
|
+
return empty_stream if ary.empty?
|
91
|
+
cons_stream(ary.first){ finite_stream(ary.drop(1)) }
|
92
|
+
end
|
93
|
+
|
92
94
|
def _finite_stream_from_boundaies fst, lst
|
93
95
|
return empty_stream if fst > lst
|
94
96
|
cons_stream(fst){ _finite_stream_from_boundaies fst.succ, lst }
|
95
97
|
end
|
98
|
+
|
99
|
+
def _finite_stream_from_hash hsh
|
100
|
+
return empty_stream if hsh.empty?
|
101
|
+
cons_stream(hsh.first){ finite_stream(hsh.without(hsh.first.first)) }
|
102
|
+
end
|
96
103
|
|
97
104
|
def _finite_stream_from_enumerator! enum
|
98
105
|
cons_stream( enum.next ){ _finite_stream_from_enumerator! enum }
|
data/lib/lab42/stream/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lab42_streams
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: forwarder2
|
@@ -38,76 +38,6 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.4'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: pry-byebug
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 3.4.2
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 3.4.2
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '3.5'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '3.5'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: qed
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '2.9'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '2.9'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: ae
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '1.8'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '1.8'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: simplecov
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0.13'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0.13'
|
111
41
|
description: Lazy Evaluation, Streams, Enumerator#Lazy
|
112
42
|
email: robert.dober@gmail.com
|
113
43
|
executables: []
|
@@ -135,7 +65,7 @@ homepage: https://github.com/RobertDober/lab42_streams
|
|
135
65
|
licenses:
|
136
66
|
- MIT
|
137
67
|
metadata: {}
|
138
|
-
post_install_message:
|
68
|
+
post_install_message:
|
139
69
|
rdoc_options: []
|
140
70
|
require_paths:
|
141
71
|
- lib
|
@@ -143,16 +73,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
73
|
requirements:
|
144
74
|
- - ">="
|
145
75
|
- !ruby/object:Gem::Version
|
146
|
-
version:
|
76
|
+
version: 3.3.5
|
147
77
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
78
|
requirements:
|
149
79
|
- - ">="
|
150
80
|
- !ruby/object:Gem::Version
|
151
81
|
version: '0'
|
152
82
|
requirements: []
|
153
|
-
|
154
|
-
|
155
|
-
signing_key:
|
83
|
+
rubygems_version: 3.5.16
|
84
|
+
signing_key:
|
156
85
|
specification_version: 4
|
157
|
-
summary: Streams for Ruby
|
86
|
+
summary: Streams for Ruby 3.3.5
|
158
87
|
test_files: []
|