lab42_streams 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []