lab42_streams 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bccbce995004e65382114ca3073c25956bfd782d
4
+ data.tar.gz: f9e9ca8f5a32e2231bb159edb36054ac0f2d32b6
5
+ SHA512:
6
+ metadata.gz: 6b5f37631aa89ad9a7bdec443adc34a3f2f8355247482eaa450cb7e3da159f0742b321060f78032510ef49dba44d3be5a71ca527640ac3952604168e993b6f9e
7
+ data.tar.gz: dd4b9c7b4a605103a184f0178218782c88dd040c05372b17a965fa21365aab34c859b4002aac818600487c39528cde2d769dc68fc23256751741f445b3f2be00
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Robert Dober
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # lab42\_streams
2
+
3
+ Bringing Streams to the much lazier Ruby 2.0
@@ -0,0 +1,3 @@
1
+ class Array
2
+ def to_stream; finite_stream self end
3
+ end
@@ -0,0 +1,8 @@
1
+ module Lab42
2
+ class Stream
3
+ class Delayed < Stream
4
+ def head; tail.head end
5
+ def tail; super.tail end
6
+ end # class Delayed
7
+ end # class Stream
8
+ end # module Lab42
@@ -0,0 +1,42 @@
1
+ module Lab42
2
+ class Stream
3
+ class Empty < Stream
4
+ # TODO: Implement all self returning methods with Forwarder
5
+ def append other
6
+ raise ArgumentError, "not a stream #{other}" unless self.class.superclass === other
7
+ # ??? Is the to_stream message a good idea
8
+ other.to_stream
9
+ end
10
+ alias_method :+, :append
11
+
12
+ def empty?; true end
13
+
14
+ def filter *args, &blk; self end
15
+
16
+ def head; raise StopIteration, "head called on empty stream" end
17
+
18
+ def make_cyclic; self end
19
+ def map *args, &blk; self end
20
+ # I believe that this definition is sound, although it is an obvious pitfall
21
+ # But falling into it once, means understanding streams better, well that is
22
+ # my opinion now, we will see what promises the future will bring...
23
+ def tail; self end
24
+
25
+
26
+ def flatmap *args, &blk; self end
27
+ def __flatmap__ a_proc; self end
28
+
29
+ private
30
+ def initialize; end
31
+
32
+ def self.new
33
+ @__instance__ ||= allocate
34
+ end
35
+
36
+ end # class Empty
37
+
38
+ module ::Kernel
39
+ def empty_stream; Empty.new end
40
+ end # module ::Kernel
41
+ end # class Stream
42
+ end # module Lab42
@@ -0,0 +1,3 @@
1
+ module Enumerable
2
+ def to_stream; finite_stream self end
3
+ end
@@ -0,0 +1,10 @@
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
@@ -0,0 +1,65 @@
1
+ require 'lab42/core/kernel'
2
+ module Kernel
3
+ def binop_streams op, stream1, stream2
4
+ combine_streams stream1, stream2 do |e1, e2|
5
+ e1.send op, e2
6
+ end
7
+ end
8
+
9
+ def combine_streams s1, s2, op=nil, &operation
10
+ return empty_stream if s1.empty? || s2.empty?
11
+ op ||= operation
12
+ cons_stream op.(s1.head, s2.head) do
13
+ combine_streams( s1.tail, s2.tail, op)
14
+ end
15
+ end
16
+
17
+ def cons_stream head, &tail
18
+ Lab42::Stream.new head, tail
19
+ end
20
+
21
+ def const_stream const
22
+ c = cons_stream( const ){ c }
23
+ end
24
+
25
+ def cyclic_stream *args
26
+ args = args.first if
27
+ args.size == 1 && Enumerable === args.first
28
+
29
+ finite_stream( args ).make_cyclic
30
+ end
31
+
32
+ def finite_stream enum
33
+ e = enum.lazy
34
+ cons_stream( e.peek ){ finite_stream e.drop( 1 ) }
35
+ rescue StopIteration
36
+ empty_stream
37
+ end
38
+
39
+ def flatmap stream, *args, &blk
40
+ stream.flatmap( *args, &blk )
41
+ end
42
+
43
+ # TODO: Reimplement with a cursor into streams to avoid
44
+ # the (potentially) costly array arithm in the tail def
45
+ def merge_streams *streams
46
+ s = streams.reject( &:empty? )
47
+ return empty_stream if s.empty?
48
+ cons_stream s.first.head do
49
+ merge_streams(*(s.drop(1) + [s.first.tail]))
50
+ end
51
+ end
52
+
53
+ def stream_by *args, &blk
54
+ if blk
55
+ cons_stream(*args){ stream_by( blk.(*args), &blk ) }
56
+ else
57
+ rest = args.drop 1
58
+ if Method === rest.first
59
+ cons_stream( args.first ){ stream_by( rest.first.(*([args.first] + rest.drop(1))), *rest ) }
60
+ else
61
+ cons_stream( args.first ){ stream_by( sendmsg(*rest).(args.first), *rest ) }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ class Proc
2
+ def memoized
3
+ already_run = false
4
+ result = nil
5
+ ->{
6
+ if already_run
7
+ result
8
+ else
9
+ already_run = true
10
+ result = call()
11
+ end
12
+ }
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Lab42
2
+ class Stream
3
+ Version = "0.1.0"
4
+ end # class Stream
5
+ end # module Lab42
@@ -0,0 +1,130 @@
1
+ require 'lab42/stream/empty'
2
+ require 'lab42/stream/delayed'
3
+ require 'lab42/stream/kernel'
4
+ require 'lab42/stream/array'
5
+ require 'lab42/stream/enumerable'
6
+ require 'lab42/stream/hash'
7
+ require 'lab42/stream/proc'
8
+
9
+ module Lab42
10
+ class Stream
11
+ ConstraintError = Class.new RuntimeError
12
+ include Enumerable
13
+ attr_reader :head, :promise
14
+
15
+ def append other
16
+ raise ArgumentError, "not a stream #{other}" unless self.class === other
17
+ cons_stream( head ){ tail.append other }
18
+ end
19
+ alias_method :+, :append
20
+
21
+ def drop n = 1
22
+ raise ArgumentError, "not a non negative number" if n < 0
23
+ t = self
24
+ loop do
25
+ return t if n.zero?
26
+ n -=1
27
+ t = t.tail
28
+ end
29
+ end
30
+
31
+ def each
32
+ t = self
33
+ loop do
34
+ yield t.head
35
+ t = t.tail
36
+ end
37
+ end
38
+
39
+ def empty?; false end
40
+
41
+ def filter *args, &blk
42
+ # TODO: Get this check and a factory to create a proc for this into core
43
+ raise ArgumentError, "use either a block or arguments" if args.empty? && !blk || !args.empty? && blk
44
+ filter_by_proc mk_proc( blk || args )
45
+ end
46
+
47
+ def filter_by_proc prc
48
+ if prc.(head)
49
+ cons_stream(head){ tail.filter_by_proc prc }
50
+ else
51
+ # TODO: Replace this with Delayed Stream (1 off)
52
+ tail.filter_by_proc prc
53
+ end
54
+ end
55
+
56
+ def flatmap *args, &blk
57
+ if args.empty?
58
+ __flatmap__ blk
59
+ elsif args.size == 1 && args.first.respond_to?( :call )
60
+ __flatmap__ args.first
61
+ else
62
+ __flatmap__ sendmsg(*args)
63
+ end
64
+ end
65
+
66
+ def __inject_while__ ival, cond, red
67
+ raise ConstraintError unless cond.(ival)
68
+ s = self
69
+ loop do
70
+ new_val = red.(ival, s.head)
71
+ return ival unless cond.(new_val)
72
+ ival = new_val
73
+ s = s.tail
74
+ return ival if s.empty?
75
+ end
76
+ end
77
+
78
+ def make_cyclic
79
+ cons_stream( head ){
80
+ tail.append( make_cyclic )
81
+ }
82
+ end
83
+
84
+ def map *args, &blk
85
+ # TODO: Get this check and a factory to create a proc for this into core/fn
86
+ raise ArgumentError, "use either a block or arguments" if args.empty? && !blk || !args.empty? && blk
87
+ transform_by_proc mk_proc( blk || args )
88
+ end
89
+
90
+ def reduce_while cond, red=nil, &reducer
91
+ red ||= reducer
92
+ tail.__inject_while__ head, cond, red
93
+ end
94
+
95
+ def tail
96
+ promise.()
97
+ end
98
+
99
+ def to_stream; self end
100
+
101
+ def transform_by_proc prc
102
+ cons_stream( prc.(head) ){ tail.transform_by_proc prc }
103
+ end
104
+
105
+ def __flatmap__ a_proc
106
+ hh = a_proc.( head )
107
+ if hh.empty?
108
+ tail.__flatmap__ a_proc
109
+ else
110
+ cons_stream( hh.head ){ hh.tail + tail.__flatmap__( a_proc ) }
111
+ end
112
+ end
113
+
114
+ private
115
+ def initialize h, t=nil, &tail
116
+ @head = h
117
+ @promise = ( t || tail ).memoized
118
+ end
119
+
120
+ # TODO: Use this from core/fn as soon as available
121
+ def mk_proc args
122
+ return args if Proc === args
123
+ raise ArgumentError, "neither a Proc nor an array of args for Kernel#sendmsg" unless Array === args
124
+ return args.first if Proc === args.first || Method === args.first
125
+ sendmsg(*args)
126
+ end
127
+
128
+ end # class Stream
129
+
130
+ end # module Lab42
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lab42_streams
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Dober
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: forwarder2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: lab42_core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.5
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.12
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.12
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ description: Lazy Evaluation, Streams, Enumerator#Lazy
70
+ email: robert.dober@gmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/lab42/stream.rb
76
+ - lib/lab42/stream/array.rb
77
+ - lib/lab42/stream/version.rb
78
+ - lib/lab42/stream/delayed.rb
79
+ - lib/lab42/stream/hash.rb
80
+ - lib/lab42/stream/empty.rb
81
+ - lib/lab42/stream/proc.rb
82
+ - lib/lab42/stream/kernel.rb
83
+ - lib/lab42/stream/enumerable.rb
84
+ - LICENSE
85
+ - README.md
86
+ homepage: https://github.com/RobertDober/lab42_streams
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 2.0.0
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.1.5
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Streams for Ruby 2.0
110
+ test_files: []