lab42_streams 0.1.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d09f7787dde8a103ab385e107f3edfb91cab85cb
4
- data.tar.gz: 2fcdf2c0ea44003310d0e05d7df2716a4d1d5ff5
2
+ SHA256:
3
+ metadata.gz: e8f831b639a52dd52db85790b4fa4f7a558d4141b8deaf0ba7cd406f59970578
4
+ data.tar.gz: 58bfe3bfe2d31b694061a704ec6fbb30bbf901fe0e4134f91026edaceac22913
5
5
  SHA512:
6
- metadata.gz: 3d87e2f2e764b300379b879ce4a2f7716abfab0d6b56d5894e25e13d7d663d8a571c8e9623a092c22de34315624b6398becac64e3ac29cfa9445453f46d6f999
7
- data.tar.gz: df8e4190c2c63dfb1e238e6c60483af3c5b90385248a0117411f40cd77aed7c6f0a274ec102f89b5943caa5ee641e9dea7ef151fb7c4ae8ed8c7af24830baf0b
6
+ metadata.gz: 17bb41d95481d19badab24f90ec1935f4450c19b65435000ba93721b602934506c665a239f524241ab4cff7ef127a84b7cdf4312cba74bb7b4b0eab2ac4c3ba2
7
+ data.tar.gz: 726f43fcf1c28fc99de3b1792fda975bdb4b9d7ed349aa380b95192af53b048def7717193065d6917a7e235cbddf36eb4ad63e2aa617eceeaa74bb15bd873f00
data/README.md CHANGED
@@ -1,16 +1,136 @@
1
+ [![Build Status](https://travis-ci.org/RobertDober/lab42_streams.svg?branch=master)](https://travis-ci.org/RobertDober/lab42_streams)
2
+ [![Code Climate](https://codeclimate.com/github/RobertDober/lab42_streams/badges/gpa.svg)](https://codeclimate.com/github/RobertDober/lab42_streams)
3
+ [![Issue Count](https://codeclimate.com/github/RobertDober/lab42_streams/badges/issue_count.svg)](https://codeclimate.com/github/RobertDober/lab42_streams)
4
+ [![Test Coverage](https://codeclimate.com/github/RobertDober/lab42_streams/badges/coverage.svg)](https://codeclimate.com/github/RobertDober/lab42_streams)
5
+ [![Gem Version](https://badge.fury.io/rb/lab42_streams.svg)](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
- [![Build Status](https://travis-ci.org/RobertDober/lab42_streams.svg?branch=master)](https://travis-ci.org/RobertDober/lab42_streams)
8
- [![Code Climate](https://codeclimate.com/github/RobertDober/lab42_streams/badges/gpa.svg)](https://codeclimate.com/github/RobertDober/lab42_streams)
9
- [![Test Coverage](https://codeclimate.com/github/RobertDober/lab42_streams/badges/coverage.svg)](https://codeclimate.com/github/RobertDober/lab42_streams)
10
- [![Gem Version](https://badge.fury.io/rb/lab42_streams.svg)](http://badge.fury.io/rb/lab42_streams)
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
11
123
 
124
+ ```ruby :example
125
+ expect(digits.drop(9).head).to eq(9)
126
+ ```
12
127
 
128
+ ```ruby :example
129
+ expect(digits.drop(10)).to be_empty
130
+ ```
13
131
 
14
- Please see the [QED](http://rubyworks.github.io/qed/) demos [here](https://github.com/RobertDober/lab42_streams/blob/master/demo)
132
+ or alternatively
15
133
 
16
- for details.
134
+ ```ruby :example
135
+ expect(digits.drop(10)).to eq(EmptyStream)
136
+ ```
@@ -4,8 +4,5 @@ Stream = Lab42::Stream
4
4
  EmptyStream = Lab42::Stream::EmptyStream
5
5
 
6
6
  module Kernel
7
- def iterate *args, &blk
8
- Stream.iterate( *args, &blk )
9
- end
10
7
  alias_method :stream_by, :iterate
11
8
  end # module Kernel
@@ -0,0 +1,62 @@
1
+ require 'lab42/core/hash'
2
+
3
+ module Lab42
4
+ class Stream
5
+ class Behavior
6
+
7
+ def initialize( &blk )
8
+ @behavior = blk
9
+ end
10
+
11
+ def call( *args )
12
+ @behavior.( *args )
13
+ end
14
+
15
+ class << self
16
+
17
+ def const(const_rval)
18
+ __const_hash__.fetch!(const_rval, new{ |*_| const_rval})
19
+ end
20
+
21
+ def make(*args, &blk)
22
+ if blk
23
+ raise ArgumentError, "cannot specify behavior with block and args: #{args.inspect}" unless args.empty?
24
+ blk
25
+ else
26
+ _make_from_args( args )
27
+ end
28
+ end
29
+
30
+ def make1(*args, &blk)
31
+ if blk
32
+ blk
33
+ else
34
+ _make_from_args( args )
35
+ end
36
+ end
37
+ private
38
+
39
+ def _make_from_args( args )
40
+ if args.first.respond_to?( :call )
41
+ _curry( args )
42
+ else
43
+ -> (rcv, *a) do
44
+ rcv.send(*(args + a))
45
+ end
46
+ end
47
+ end
48
+
49
+ def _curry(args)
50
+ -> ( *a ) do
51
+ args.first.(*(args.drop(1)+a))
52
+ end
53
+ end
54
+
55
+ def __const_hash__
56
+ @__const_hash__ ||= {}
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -2,7 +2,7 @@ module Lab42
2
2
  class Stream
3
3
  module ClassMethods
4
4
  def iterate arg, beh=nil, &blk
5
- beh = blk.make_behavior beh
5
+ beh = Behavior.make(beh, &blk)
6
6
  __iterate__ arg, beh
7
7
  end
8
8
 
@@ -1,17 +1,11 @@
1
1
  module Enumerable
2
- def ordered_by *beh, &blk
3
- raise ArgumentError, 'need exactly one of block and behavior' if !blk == beh.empty?
4
-
5
- beh = blk || (Symbol === beh.first ? sendmsg( *beh ) : beh.first )
2
+ def ordered_by beh
6
3
 
7
-
8
4
  sort do | a, b |
9
5
  a == b ? 0 : (
10
6
  beh.(a, b) ? -1 : 1
11
7
  )
12
8
  end
13
- end
14
9
 
15
- def to_stream; finite_stream self end
10
+ end
16
11
  end
17
-
@@ -1,7 +1,4 @@
1
1
  require 'forwarder'
2
-
3
- require_relative 'empty/enumerable'
4
-
5
2
  module Lab42
6
3
  class Stream
7
4
  class Empty < Stream
@@ -36,20 +33,17 @@ module Lab42
36
33
  def inject *args; args.first end
37
34
  alias_method :__inject__, :inject
38
35
 
39
- # TODO: Move this into lab42/core as Object#itself
40
- def itself *args, &blk; self end
36
+ def itself *; self end
41
37
 
42
- def scan initial, *args, &blk
38
+ def scan( initial, * )
43
39
  [initial]
44
40
  end
45
41
 
46
- def scan1 *args, &blk
42
+ def scan1( * )
47
43
  []
48
44
  end
49
45
 
50
46
 
51
- private
52
-
53
47
  def self.new
54
48
  @__instance__ ||= allocate
55
49
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'core/enumerable'
2
+ require_relative 'behavior'
2
3
 
3
4
  module Lab42
4
5
  class Stream
@@ -7,19 +8,17 @@ module Lab42
7
8
  module Enumerable
8
9
 
9
10
  def drop_until *bhv, &blk
10
- bhv = blk.make_behavior( *bhv )
11
- s = self
12
- loop do
13
- return s if bhv.(s.head)
14
- s = s.tail
15
- end
16
- empty_stream
11
+ bhv = Behavior.make( *bhv, &blk )
12
+ __drop_while__ bhv.not
17
13
  end
18
14
 
19
- # N.B. Not implemented as drop_until( bhv.not )
20
- # for performance reasons
21
15
  def drop_while *bhv, &blk
22
- bhv = blk.make_behavior( *bhv )
16
+ bhv = Behavior.make( *bhv, &blk )
17
+ __drop_while__ bhv
18
+ end
19
+
20
+
21
+ def __drop_while__ bhv
23
22
  s = self
24
23
  loop do
25
24
  return s unless bhv.(s.head)
@@ -31,6 +30,7 @@ module Lab42
31
30
  def each
32
31
  t = self
33
32
  loop do
33
+ return if t.empty?
34
34
  yield t.head
35
35
  t = t.tail
36
36
  end
@@ -64,7 +64,7 @@ module Lab42
64
64
  end
65
65
 
66
66
  def lazy_take n=1
67
- raise ArgumentError, "need a non negative Fixnum" if !(Fixnum === n) || n < 0
67
+ raise ArgumentError, "need a non negative Integer" if !(Integer === n) || n < 0
68
68
  __lazy_take__ n
69
69
  end
70
70
 
@@ -74,7 +74,7 @@ module Lab42
74
74
  end
75
75
 
76
76
  def lazy_take_until *bhv, &blk
77
- bhv = blk.make_behavior( *bhv )
77
+ bhv = Behavior.make( *bhv, &blk )
78
78
  __lazy_take_until__ bhv
79
79
  end
80
80
 
@@ -86,7 +86,7 @@ module Lab42
86
86
  end
87
87
 
88
88
  def lazy_take_while *bhv, &blk
89
- bhv = blk.make_behavior( *bhv )
89
+ bhv = Behavior.make( *bhv, &blk )
90
90
  __lazy_take_while__ bhv
91
91
  end
92
92
 
@@ -98,30 +98,30 @@ module Lab42
98
98
  end
99
99
 
100
100
  def reduce red=nil, &reducer
101
- red = reducer.make_behavior( red )
101
+ red = Behavior.make( red, &reducer)
102
102
  tail.__inject__ head, red
103
103
  end
104
104
 
105
105
  def inject agg, *red, &reducer
106
- __inject__ agg, reducer.make_behavior( *red )
106
+ __inject__ agg, Behavior.make( *red, &reducer )
107
107
  end
108
108
 
109
109
  def filter *args, &blk
110
- __filter__ self, blk.make_behavior( *args )
110
+ __filter__ self, Behavior.make( *args, &blk )
111
111
  end
112
112
 
113
113
  def reject *args, &blk
114
- __filter__ self, blk.make_behavior( *args ).not
114
+ __filter__ self, Behavior.make( *args, &blk ).not
115
115
  end
116
116
 
117
117
  def flatmap *args, &blk
118
- __flatmap__ blk.make_behavior( *args )
118
+ __flatmap__ Behavior.make( *args, &blk )
119
119
  end
120
120
 
121
121
  def __flatmap__ a_proc
122
- # require 'pry'
123
- # binding.pry
124
122
  hh = a_proc.( head )
123
+ raise ArgumentError, "flatmap can only map on streams, use flatmap_with_each to map over streams and enumerables" unless
124
+ Lab42::Stream === hh
125
125
  if hh.empty?
126
126
  tail.__flatmap__ a_proc
127
127
  else
@@ -130,18 +130,18 @@ module Lab42
130
130
  end
131
131
 
132
132
  def flatmap_with_each *args, &blk
133
- __flatmap_with_each__ blk.make_behavior( *args )
133
+ __flatmap_with_each__ Behavior.make( *args, &blk )
134
134
  end
135
135
 
136
136
  def __flatmap_with_each__ a_proc, rest_of_enum = []
137
137
  # Process expanded values
138
138
  return cons_stream( rest_of_enum.first ){ __flatmap_with_each__ a_proc, rest_of_enum.drop( 1 ) } unless
139
- rest_of_enum.empty?
139
+ rest_of_enum.empty?
140
140
 
141
141
  # Map a scalar value
142
142
  hh = a_proc.( head )
143
143
  return cons_stream( hh ){ tail.__flatmap_with_each__ a_proc } unless
144
- hh.respond_to? :each
144
+ hh.respond_to? :each
145
145
 
146
146
  # Start a new expansion...
147
147
  # ... consider an empty expansion
@@ -153,10 +153,8 @@ module Lab42
153
153
 
154
154
  def scan initial, *args, &blk
155
155
  cons_stream initial do
156
- __scan__ initial, blk.make_behavior( *args )
156
+ __scan__ initial, Behavior.make( *args, &blk )
157
157
  end.tap{ |r|
158
- # require 'pry'
159
- # binding.pry
160
158
  }
161
159
  end
162
160
 
@@ -170,16 +168,16 @@ module Lab42
170
168
  end
171
169
 
172
170
  def take_until *bhv, &blk
173
- bhv = blk.make_behavior( *bhv )
174
- x = []
175
- each do | ele |
176
- return x if bhv.( ele )
177
- x << ele
178
- end
179
- x
171
+ bhv = Behavior.make( *bhv, &blk )
172
+ __take_while__ bhv.not
180
173
  end
174
+
181
175
  def take_while *bhv, &blk
182
- bhv = blk.make_behavior( *bhv )
176
+ bhv = Behavior.make( *bhv, &blk )
177
+ __take_while__ bhv
178
+ end
179
+
180
+ def __take_while__ bhv
183
181
  x = []
184
182
  each do | ele |
185
183
  return x unless bhv.( ele )
@@ -189,8 +187,9 @@ module Lab42
189
187
  end
190
188
 
191
189
  def to_a
192
- take_while :true
190
+ take_while Behavior.const( true )
193
191
  end
192
+
194
193
  alias_method :entries, :to_a
195
194
 
196
195
  def make_cyclic
@@ -200,9 +199,8 @@ module Lab42
200
199
  end
201
200
 
202
201
  def map *args, &blk
203
- # TODO: Get this check and a factory to create a proc for this into core/fn
204
202
  raise ArgumentError, "use either a block or arguments" if args.empty? && !blk || !args.empty? && blk
205
- __map__ blk.make_behavior( *args )
203
+ __map__ Behavior.make( *args, &blk )
206
204
  end
207
205
 
208
206
  def __map__ prc
@@ -216,7 +214,7 @@ module Lab42
216
214
  end
217
215
 
218
216
  def take n=1
219
- raise ArgumentError, "need a non negative Fixnum" if !(Fixnum === n) || n < 0
217
+ raise ArgumentError, "need a non negative Integer" if !(Integer === n) || n < 0
220
218
  x = []
221
219
  each do | ele |
222
220
  return x if n.zero?
@@ -235,12 +233,12 @@ module Lab42
235
233
 
236
234
  def zip_as_ary *other_streamables
237
235
  zip( *other_streamables )
238
- .map( &:entries )
236
+ .map( &:entries )
239
237
  end
240
238
 
241
239
  def __zip__ streams
242
- cons_stream( [head] + streams.map(:head) ){
243
- tail.__zip__ streams.map(:tail)
240
+ cons_stream( [head] + streams.map(&:head) ){
241
+ tail.__zip__ streams.map(&:tail)
244
242
  }
245
243
  end
246
244
 
@@ -1,16 +1,14 @@
1
+ require_relative 'behavior'
1
2
  module Lab42
2
3
  class Stream
3
4
  module HigherOrder
4
5
  def combine *streams_and_op, &operation
5
6
  op = streams_and_op.pop unless self.class === streams_and_op.last
6
- op = operation.make_behavior op
7
- # TODO: Decide what to do if op.arity and streams_and_op.size.succ do not match????
7
+ op = Behavior.make1( op, &operation )
8
8
  __combine__( op, *streams_and_op )
9
9
  end
10
10
 
11
11
  def __combine__ op, *streams
12
- # TODO: Decide if we can continue if one of the streams is empty iff op.arity < 0
13
- # for now no!
14
12
  return empty_stream if streams.any?( &:empty? )
15
13
  values = streams.map( &:head )
16
14
  new_head = op.(head, *values)
@@ -23,7 +21,7 @@ module Lab42
23
21
  indexed = with_index
24
22
  n.times.map do | i |
25
23
  indexed
26
- .filter{ |e, idx| idx % n == i }
24
+ .filter{ |_, idx| idx % n == i }
27
25
  .map( :first )
28
26
  end
29
27
  end
@@ -1,4 +1,6 @@
1
1
  require 'lab42/core/kernel'
2
+ require_relative 'behavior'
3
+
2
4
  module Kernel
3
5
  def binop_streams op, stream1, stream2
4
6
  combine_streams stream1, stream2 do |e1, e2|
@@ -33,18 +35,24 @@ module Kernel
33
35
  end
34
36
 
35
37
  def finite_stream enum
36
- e = enum.lazy
37
- cons_stream( e.peek ){ finite_stream e.drop( 1 ) }
38
- rescue StopIteration
39
- empty_stream
38
+ case enum
39
+ when Range
40
+ _finite_stream_from_range enum
41
+ when Array
42
+ _finite_stream_from_ary enum
43
+ when Hash
44
+ _finite_stream_from_hash enum
45
+ when Enumerator
46
+ _finite_stream_from_enumerator! enum.to_enum
47
+ else
48
+ raise TypeError, "cannot create a finite stream from type #{enum.class.inspect}"
49
+ end
40
50
  end
41
51
 
42
52
  def flatmap stream, *args, &blk
43
53
  stream.flatmap( *args, &blk )
44
54
  end
45
55
 
46
- # TODO: Reimplement with a cursor into streams to avoid
47
- # the (potentially) costly array arithm in the tail def
48
56
  def merge_streams *streams
49
57
  s = streams.reject( &:empty? )
50
58
  return empty_stream if s.empty?
@@ -54,7 +62,7 @@ module Kernel
54
62
  end
55
63
 
56
64
  def merge_streams_by *streams_and_beh, &blk
57
- beh = blk || streams_and_beh.pop
65
+ beh = Lab42::Stream::Behavior.make1( blk || streams_and_beh.pop, &blk )
58
66
  __merge_streams_by__ beh, streams_and_beh
59
67
  end
60
68
 
@@ -69,27 +77,56 @@ module Kernel
69
77
 
70
78
  def iterate_without_block args
71
79
  rest = args.drop 1
72
- if Method === rest.first
80
+ if rest.first && rest.first.respond_to?( :call )
73
81
  cons_stream( args.first ){ iterate( rest.first.(*([args.first] + rest.drop(1))), *rest ) }
74
82
  else
75
- cons_stream( args.first ){ iterate( sendmsg(*rest).(args.first), *rest ) }
83
+ cons_stream( args.first ){ iterate( args.first.send(*rest), *rest ) }
76
84
  end
77
85
  end
78
86
 
79
- private
80
- def __merge_streams_by__ beh, streams
81
- still_there = streams.reject( &:empty? )
82
- return empty_stream if still_there.empty?
83
- __merge_streams_by_with_present__ beh, still_there, streams
84
- end
87
+ private
85
88
 
86
- def __merge_streams_by_with_present__ beh, still_there, streams
87
- ordered_heads = still_there
88
- .map( &:head )
89
- .ordered_by( beh )
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
90
93
 
91
- cons_stream_n( *ordered_heads ){
92
- __merge_streams_by__ beh, still_there.map( &:tail )
93
- }
94
- end
94
+ def _finite_stream_from_boundaies fst, lst
95
+ return empty_stream if fst > lst
96
+ cons_stream(fst){ _finite_stream_from_boundaies fst.succ, lst }
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
103
+
104
+ def _finite_stream_from_enumerator! enum
105
+ cons_stream( enum.next ){ _finite_stream_from_enumerator! enum }
106
+ rescue StopIteration
107
+ empty_stream
108
+ end
109
+
110
+ def _finite_stream_from_range range
111
+ fst = range.first
112
+ lst = range.last
113
+ lst = lst.pred if range.exclude_end?
114
+ _finite_stream_from_boundaies fst, lst
115
+ end
116
+
117
+ def __merge_streams_by__ beh, streams
118
+ still_there = streams.reject( &:empty? )
119
+ return empty_stream if still_there.empty?
120
+ __merge_streams_by_with_present__ beh, still_there
121
+ end
122
+
123
+ def __merge_streams_by_with_present__ beh, still_there
124
+ ordered_heads = still_there
125
+ .map( &:head )
126
+ .ordered_by( beh )
127
+
128
+ cons_stream_n( *ordered_heads ){
129
+ __merge_streams_by__ beh, still_there.map( &:tail )
130
+ }
131
+ end
95
132
  end
@@ -12,28 +12,9 @@ class Proc
12
12
  }
13
13
  end
14
14
 
15
- # TODO: Use this from core/fn as soon as available
16
- def make_behavior *args
17
- raise ArgumentError, "cannot specify behavior with block and args: #{args.inspect}" unless args.compact.empty?
18
- self
19
- end
20
-
21
15
  def not
22
16
  -> (*args, &blk) {
23
17
  ! self.(*args, &blk)
24
18
  }
25
19
  end
26
20
  end
27
-
28
- class NilClass
29
- # TODO: Use this from core/fn as soon as available
30
- def make_behavior *args
31
- return args.first if args.size == 1 && args.first.respond_to?( :call )
32
-
33
- return ->(*a){
34
- args.first(*(args.drop(1)+a))
35
- } if args.first.respond_to?( :call )
36
-
37
- sendmsg( *args )
38
- end
39
- end
@@ -3,7 +3,7 @@ module Lab42
3
3
  module Utility
4
4
 
5
5
  def segment *args, &blk
6
- __segment__ blk.make_behavior( *args )
6
+ __segment__ Behavior.make( *args, &blk )
7
7
  end
8
8
 
9
9
  def __segment__ beh
@@ -1,5 +1,5 @@
1
1
  module Lab42
2
2
  class Stream
3
- Version = "0.1.3"
3
+ Version = "0.3.0"
4
4
  end # class Stream
5
5
  end # module Lab42
data/lib/lab42/stream.rb CHANGED
@@ -5,12 +5,10 @@ require_relative './stream/kernel'
5
5
  require_relative './stream/array'
6
6
  require_relative './stream/enumerable'
7
7
  require_relative './stream/higher_order'
8
- require_relative './stream/hash'
9
8
  require_relative './stream/proc'
10
9
  require_relative './stream/class_methods'
11
10
  require_relative './stream/utility'
12
11
 
13
- # TODO: This should rather be implemented in lab_42/core/fn
14
12
  require_relative './stream/kernel/extensions'
15
13
 
16
14
  module Lab42
@@ -31,7 +29,7 @@ module Lab42
31
29
  def combine_streams *args, &operation
32
30
  op = args.shift unless self.class === args.first
33
31
  raise ArgumentError, "Missing stream parameters" if args.empty?
34
- __combine_streams__ operation.make_behavior( op ), args
32
+ __combine_streams__ Behavior.make( op, &operation), args
35
33
  end
36
34
 
37
35
  def drop n = 1
@@ -53,10 +51,10 @@ module Lab42
53
51
  def to_stream; self end
54
52
 
55
53
  def __combine_streams__ op, args
56
- return empty_stream if args.any?(&sendmsg(:empty?))
54
+ return empty_stream if args.any?(&:empty?)
57
55
 
58
- new_head = op.(head, *args.map(&sendmsg(:head)))
59
- cons_stream( new_head ){ tail.__combine_streams__(op, args.map(&sendmsg(:tail))) }
56
+ new_head = op.(head, *args.map(&:head))
57
+ cons_stream( new_head ){ tail.__combine_streams__(op, args.map(&:tail)) }
60
58
  end
61
59
 
62
60
  private
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.1.3
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: 2015-05-09 00:00:00.000000000 Z
11
+ date: 2024-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: forwarder2
@@ -30,98 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.0'
33
+ version: '0.4'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.0'
41
- - !ruby/object:Gem::Dependency
42
- name: pry
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.9'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.9'
55
- - !ruby/object:Gem::Dependency
56
- name: pry-nav
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '0.2'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '0.2'
69
- - !ruby/object:Gem::Dependency
70
- name: rspec
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '2.14'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '2.14'
83
- - !ruby/object:Gem::Dependency
84
- name: qed
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '2.9'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '2.9'
97
- - !ruby/object:Gem::Dependency
98
- name: ae
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '1.8'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '1.8'
111
- - !ruby/object:Gem::Dependency
112
- name: byebug
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '3.1'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '3.1'
40
+ version: '0.4'
125
41
  description: Lazy Evaluation, Streams, Enumerator#Lazy
126
42
  email: robert.dober@gmail.com
127
43
  executables: []
@@ -133,13 +49,12 @@ files:
133
49
  - lib/lab42/stream.rb
134
50
  - lib/lab42/stream/array.rb
135
51
  - lib/lab42/stream/auto_import.rb
52
+ - lib/lab42/stream/behavior.rb
136
53
  - lib/lab42/stream/class_methods.rb
137
54
  - lib/lab42/stream/core/enumerable.rb
138
55
  - lib/lab42/stream/delayed.rb
139
56
  - lib/lab42/stream/empty.rb
140
- - lib/lab42/stream/empty/enumerable.rb
141
57
  - lib/lab42/stream/enumerable.rb
142
- - lib/lab42/stream/hash.rb
143
58
  - lib/lab42/stream/higher_order.rb
144
59
  - lib/lab42/stream/kernel.rb
145
60
  - lib/lab42/stream/kernel/extensions.rb
@@ -150,7 +65,7 @@ homepage: https://github.com/RobertDober/lab42_streams
150
65
  licenses:
151
66
  - MIT
152
67
  metadata: {}
153
- post_install_message:
68
+ post_install_message:
154
69
  rdoc_options: []
155
70
  require_paths:
156
71
  - lib
@@ -158,16 +73,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
158
73
  requirements:
159
74
  - - ">="
160
75
  - !ruby/object:Gem::Version
161
- version: 2.1.0
76
+ version: 3.3.5
162
77
  required_rubygems_version: !ruby/object:Gem::Requirement
163
78
  requirements:
164
79
  - - ">="
165
80
  - !ruby/object:Gem::Version
166
81
  version: '0'
167
82
  requirements: []
168
- rubyforge_project:
169
- rubygems_version: 2.4.6
170
- signing_key:
83
+ rubygems_version: 3.5.16
84
+ signing_key:
171
85
  specification_version: 4
172
- summary: Streams for Ruby 2.0
86
+ summary: Streams for Ruby 3.3.5
173
87
  test_files: []
File without changes
@@ -1,10 +0,0 @@
1
- class Hash
2
- def to_stream
3
- values = to_a
4
- (0...values.size)
5
- .to_stream
6
- .map{ |i|
7
- Hash[*values[i]]
8
- }
9
- end
10
- end