sequence 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,152 @@
1
+ # Copyright (C) 2006 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+
4
+ class Sequence
5
+ #i thought ruby had this already, but i can't find it...
6
+ class WeakHash
7
+ #dunno if this is thread-safe
8
+ def initialize hash={},default=nil,&block
9
+ @hash=block ? Hash.new(&block) : Hash.new(default)
10
+ hash.each{|(k,v)|
11
+ self[k]=v
12
+ }
13
+ end
14
+
15
+ def delete_when_dies key
16
+ ObjectSpace.define_finalizer(key){|id| @hash.include? id and @hash.delete id}
17
+ return key
18
+ end
19
+
20
+ def [] key
21
+ @hash[key.__id__]
22
+ end
23
+
24
+ def []= key, val
25
+ delete_when_dies key
26
+ @hash[key.__id__]=val
27
+ end
28
+
29
+ def delete key
30
+ @hash.delete key.__id__
31
+ end
32
+
33
+ def values
34
+ @hash.values
35
+ end
36
+
37
+ def keys
38
+ @hash.keys.map!{|id| ObjectSpace._id2ref(id)}
39
+ end
40
+ end
41
+
42
+ #methods (and constants) related to functional programming
43
+ module Functional; end
44
+ class<<Functional
45
+ HAS_SIDE_EFFECT=WeakHash.new
46
+ NO_SIDE_EFFECT=WeakHash.new
47
+ #hashes of Module (or Class, including meta-Class) to a list of method names
48
+ #which do or don't have side effects
49
+ def functions_of(obj)
50
+ published=Set[public_methods_of(obj).delete_if{|name| /[!=]$/===name}]
51
+ result=[]
52
+ list=class<<obj; ancestors.unshift self end
53
+ list.each{|mod|
54
+ result.push( *published&NO_SIDE_EFFECT[mod] )
55
+ published&=Set[*NO_SIDE_EFFECT[mod]+HAS_SIDE_EFFECT[mod]]
56
+ }
57
+ return result
58
+ end
59
+
60
+ def maybe_functions_of(obj)
61
+ published=public_methods_of(obj).delete_if{|name| /[!=]$/===name}
62
+ result=[]
63
+ list=class<<obj; ancestors.unshift self end
64
+ list.each{|mod|
65
+ published.delete_if{|name|
66
+ NO_SIDE_EFFECT[mod].include? name ||
67
+ HAS_SIDE_EFFECT[mod].include? name
68
+ }
69
+ }
70
+ return result
71
+ end
72
+
73
+ def nonfunctions_of(obj)
74
+ public_methods_of(obj)-functions_of(obj)-maybe_functions_of(obj)
75
+ end
76
+
77
+ def is_function?(obj,name)
78
+ list=class<<obj; ancestors.unshift self end
79
+ list.each{|mod|
80
+ return true if NO_SIDE_EFFECT[mod].include? name
81
+ return false if HAS_SIDE_EFFECT[mod].include? name
82
+ }
83
+ return false
84
+ end
85
+
86
+ def is_maybe_not_function?(obj,name)
87
+ !is_function(obj,name)
88
+ end
89
+
90
+ def is_maybe_function?(obj,name)
91
+ list=class<<obj; ancestors.unshift self end
92
+ list.each{|mod|
93
+ return true if NO_SIDE_EFFECT[mod].include? name
94
+ return false if HAS_SIDE_EFFECT[mod].include? name
95
+ }
96
+ return true
97
+ end
98
+
99
+ def is_not_function?(obj,name)
100
+ !is_maybe_function(obj,name)
101
+ end
102
+
103
+ PMETHS_REF= ::Object.instance_method("public_methods")
104
+ def public_methods_of(obj)
105
+ PMETHS_REF.bind(obj).call
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ class Module
112
+ def has_side_effect(*names)
113
+ names.map!{|name| name.to_s}
114
+ Functional::HAS_SIDE_EFFECT[self]|= names
115
+ Functional::NO_SIDE_EFFECT[self] -= names
116
+ end
117
+
118
+ def no_side_effect(*names)
119
+ names.map!{|name| name.to_s}
120
+ Functional::NO_SIDE_EFFECT[self] |= names
121
+ Functional::HAS_SIDE_EFFECT[self]-=names
122
+ end
123
+ end
124
+
125
+ class Object
126
+ has_side_effect *l=%w(__send__ display extend freeze instance_eval instance_exec instance_variable_set method_missing send taint untaint)
127
+ no_side_effect *instance_methods(false)-l
128
+ end
129
+ class String
130
+ has_side_effect *l=%w(<< []= capitalize! chomp! chop! concat delete! downcase! gsub! lstrip! next! replace reverse! rstrip! slice! squeeze! strip! sub! succ! swapcase! tr! tr_s! upcase!)
131
+ no_side_effect *instance_methods(false)-l
132
+ end
133
+ class Array
134
+ has_side_effect *l=%w(<< []= clear collect! compact! concat delete delete_at delete_if fill flatten! insert map! pop push reject! replace reverse! shift slice! sort! uniq! unshift)
135
+ no_side_effect *instance_methods(false)-l
136
+ end
137
+ class Hash
138
+ has_side_effect *l=%w([]= clear default= delete delete_if merge! rehash reject! replace shift store update)
139
+ no_side_effect *instance_methods(false)-l
140
+ end
141
+ class Class
142
+ has_side_effect *%w(inheirited new)
143
+ no_side_effect *%w(allocate superclass)
144
+ end
145
+ class Module
146
+ has_side_effect *l=%w(class_eval const_set module_eval private_class_method public_class_method)+private_instance_methods(false)-["class_variable_get"]
147
+ no_side_effect *["class_variable_get"]+instance_methods(false)-l
148
+ end
149
+ class Proc
150
+ has_side_effect *%w[[] call]
151
+ no_side_effect "arity"
152
+ end
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # $Idaemons: /home/cvs/rb/generator.rb,v 1.8 2001/10/03 08:54:32 knu Exp $
4
+ # $RoughId: generator.rb,v 1.10 2003/10/14 19:36:58 knu Exp $
5
+ # $Id: generator.rb,v 1.12 2005/12/31 02:56:46 ocean Exp $
6
+ #++
7
+
8
+ #this is a copy of (the newest, fastest) generator.rb from the standard lib,
9
+ #with SyncEnumerator removed. -cc
10
+
11
+ #
12
+ # = generator.rb: convert an internal iterator to an external one
13
+ #
14
+ # Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
15
+ #
16
+ # All rights reserved. You can redistribute and/or modify it under
17
+ # the same terms as Ruby.
18
+ #
19
+ # == Overview
20
+ #
21
+ # This library provides the Generator class, which converts an
22
+ # internal iterator (i.e. an Enumerable object) to an external
23
+ # iterator. In that form, you can roll many iterators independently.
24
+ #
25
+ # The SyncEnumerator class, which is implemented using Generator,
26
+ # makes it easy to roll many Enumerable objects synchronously.
27
+ #
28
+ # See the respective classes for examples of usage.
29
+
30
+
31
+ #
32
+ # Generator converts an internal iterator (i.e. an Enumerable object)
33
+ # to an external iterator.
34
+ #
35
+ # == Example
36
+ #
37
+ # require 'generator'
38
+ #
39
+ # # Generator from an Enumerable object
40
+ # g = Generator.new(['A', 'B', 'C', 'Z'])
41
+ #
42
+ # while g.next?
43
+ # puts g.next
44
+ # end
45
+ #
46
+ # # Generator from a block
47
+ # g = Generator.new { |g|
48
+ # for i in 'A'..'C'
49
+ # g.yield i
50
+ # end
51
+ #
52
+ # g.yield 'Z'
53
+ # }
54
+ #
55
+ # # The same result as above
56
+ # while g.next?
57
+ # puts g.next
58
+ # end
59
+ #
60
+
61
+ #if existing Generator library is the slow one, replace with the faster version (above)
62
+ require 'generator.rb'
63
+ unless Generator.new.instance_variables.include("@loop_thread")
64
+ remove_const :Generator
65
+ class Generator
66
+ include Enumerable
67
+
68
+ # Creates a new generator either from an Enumerable object or from a
69
+ # block.
70
+ #
71
+ # In the former, block is ignored even if given.
72
+ #
73
+ # In the latter, the given block is called with the generator
74
+ # itself, and expected to call the +yield+ method for each element.
75
+ def initialize(enum = nil, &block)
76
+ if enum
77
+ @block = proc{|g| enum.each{|value| g.yield value}}
78
+ else
79
+ @block = block
80
+ end
81
+ @index = 0
82
+ @queue = []
83
+ @main_thread = nil
84
+ @loop_thread.kill if defined?(@loop_thread)
85
+ @loop_thread = Thread.new do
86
+ Thread.stop
87
+ begin
88
+ @block.call(self)
89
+ rescue
90
+ @main_thread.raise $!
91
+ ensure
92
+ @main_thread.wakeup
93
+ end
94
+ end
95
+ Thread.pass until @loop_thread.stop?
96
+ self
97
+ end
98
+
99
+ # Yields an element to the generator.
100
+ def yield(value)
101
+ if Thread.current != @loop_thread
102
+ raise "should be called in Generator.new{|g| ... }"
103
+ end
104
+ Thread.critical = true
105
+ begin
106
+ @queue << value
107
+ @main_thread.wakeup
108
+ Thread.stop
109
+ ensure
110
+ Thread.critical = false
111
+ end
112
+ self
113
+ end
114
+
115
+ # Returns true if the generator has reached the end.
116
+ def end?
117
+ if @queue.empty?
118
+ if @main_thread
119
+ raise "should not be called in Generator.new{|g| ... }"
120
+ end
121
+ Thread.critical = true
122
+ begin
123
+ @main_thread = Thread.current
124
+ @loop_thread.wakeup
125
+ Thread.stop
126
+ rescue ThreadError
127
+ # ignore
128
+ ensure
129
+ @main_thread = nil
130
+ Thread.critical = false
131
+ end
132
+ end
133
+ @queue.empty?
134
+ end
135
+
136
+ # Returns true if the generator has not reached the end yet.
137
+ def next?
138
+ !end?
139
+ end
140
+
141
+ # Returns the current index (position) counting from zero.
142
+ def index
143
+ @index
144
+ end
145
+
146
+ # Returns the current index (position) counting from zero.
147
+ def pos
148
+ @index
149
+ end
150
+
151
+ # Returns the element at the current position and moves forward.
152
+ def next
153
+ raise EOFError.new("no more elements available") if end?
154
+ @index += 1
155
+ @queue.shift
156
+ end
157
+
158
+ # Returns the element at the current position.
159
+ def current
160
+ raise EOFError.new("no more elements available") if end?
161
+ @queue.first
162
+ end
163
+
164
+ # Rewinds the generator.
165
+ def rewind
166
+ initialize(nil, &@block) if @index.nonzero?
167
+ self
168
+ end
169
+
170
+ # Rewinds the generator and enumerates the elements.
171
+ def each
172
+ rewind
173
+ until end?
174
+ yield self.next
175
+ end
176
+ self
177
+ end
178
+ end
179
+ end
180
+
181
+
182
+
183
+ if $0 == __FILE__
184
+ eval DATA.read, nil, $0, __LINE__+4
185
+ end
186
+
187
+ __END__
188
+
189
+ require 'test/unit'
190
+
191
+ class TC_Generator < Test::Unit::TestCase
192
+ def test_block1
193
+ g = Generator.new { |g|
194
+ # no yield's
195
+ }
196
+
197
+ assert_equal(0, g.pos)
198
+ assert_raises(EOFError) { g.current }
199
+ end
200
+
201
+ def test_block2
202
+ g = Generator.new { |g|
203
+ for i in 'A'..'C'
204
+ g.yield i
205
+ end
206
+
207
+ g.yield 'Z'
208
+ }
209
+
210
+ assert_equal(0, g.pos)
211
+ assert_equal('A', g.current)
212
+
213
+ assert_equal(true, g.next?)
214
+ assert_equal(0, g.pos)
215
+ assert_equal('A', g.current)
216
+ assert_equal(0, g.pos)
217
+ assert_equal('A', g.next)
218
+
219
+ assert_equal(1, g.pos)
220
+ assert_equal(true, g.next?)
221
+ assert_equal(1, g.pos)
222
+ assert_equal('B', g.current)
223
+ assert_equal(1, g.pos)
224
+ assert_equal('B', g.next)
225
+
226
+ assert_equal(g, g.rewind)
227
+
228
+ assert_equal(0, g.pos)
229
+ assert_equal('A', g.current)
230
+
231
+ assert_equal(true, g.next?)
232
+ assert_equal(0, g.pos)
233
+ assert_equal('A', g.current)
234
+ assert_equal(0, g.pos)
235
+ assert_equal('A', g.next)
236
+
237
+ assert_equal(1, g.pos)
238
+ assert_equal(true, g.next?)
239
+ assert_equal(1, g.pos)
240
+ assert_equal('B', g.current)
241
+ assert_equal(1, g.pos)
242
+ assert_equal('B', g.next)
243
+
244
+ assert_equal(2, g.pos)
245
+ assert_equal(true, g.next?)
246
+ assert_equal(2, g.pos)
247
+ assert_equal('C', g.current)
248
+ assert_equal(2, g.pos)
249
+ assert_equal('C', g.next)
250
+
251
+ assert_equal(3, g.pos)
252
+ assert_equal(true, g.next?)
253
+ assert_equal(3, g.pos)
254
+ assert_equal('Z', g.current)
255
+ assert_equal(3, g.pos)
256
+ assert_equal('Z', g.next)
257
+
258
+ assert_equal(4, g.pos)
259
+ assert_equal(false, g.next?)
260
+ assert_raises(EOFError) { g.next }
261
+ end
262
+
263
+ def test_each
264
+ a = [5, 6, 7, 8, 9]
265
+
266
+ g = Generator.new(a)
267
+
268
+ i = 0
269
+
270
+ g.each { |x|
271
+ assert_equal(a[i], x)
272
+
273
+ i += 1
274
+
275
+ break if i == 3
276
+ }
277
+
278
+ assert_equal(3, i)
279
+
280
+ i = 0
281
+
282
+ g.each { |x|
283
+ assert_equal(a[i], x)
284
+
285
+ i += 1
286
+ }
287
+
288
+ assert_equal(5, i)
289
+ end
290
+ end
@@ -0,0 +1,234 @@
1
+ # $Id$
2
+ # Copyright (C) 2006 Caleb Clausen
3
+ # Distributed under the terms of Ruby's license.
4
+
5
+ require 'sequence'
6
+ require 'sequence/arraylike'
7
+ require 'sequence/stringlike'
8
+ require 'sequence/usedata'
9
+ require 'sequence/subseq'
10
+
11
+ class Sequence
12
+ # This class makes a seq over an Array or String.
13
+ class Indexed < UseData
14
+
15
+ class <<self
16
+ alias _orig_new new
17
+ def new(data,p=0)
18
+ if data.respond_to? :to_sequence
19
+ data.to_sequence
20
+ else _orig_new(data,p)
21
+ end
22
+ end
23
+ end
24
+
25
+ def initialize(data,pos=0)
26
+ @data = data
27
+ @pos = pos
28
+ end
29
+ # :stopdoc:
30
+ def new_data
31
+ @data.class.new
32
+ end
33
+
34
+ =begin
35
+ protected
36
+ def _delete1after?
37
+ @data.slice!(@pos)
38
+ end
39
+ def _delete1before?
40
+ @pos==0 ? nil : @data.slice!(@pos-=1)
41
+ end
42
+ def _insert1before(v)
43
+ @data[@pos,0] = (new_data << v)
44
+ @pos += 1
45
+ true
46
+ end
47
+ def _insert1after(v)
48
+ @data[@pos,0] = (new_data << v)
49
+ true
50
+ end
51
+ public
52
+ =end
53
+
54
+ attr_reader :pos,:data
55
+ def _pos= x; @pos=x end
56
+
57
+ def eof?; @pos >= size end
58
+
59
+ def_delegators :@data, :size, :slice, :[]
60
+
61
+ def maxmatchlen(backwards) size end
62
+
63
+ #can't change maxmatchlen (maybe someday?)
64
+ def maxmatchlen= x; end
65
+
66
+ def modify(*args)
67
+ newsize=args.last.size
68
+ first,len,only1=_parse_slice_args( *args[0...-1])
69
+ @data.[]=(*args)
70
+ @pos=_adjust_pos_on_change(@pos, first,len,newsize)
71
+ notify_change(self,first,len,newsize)
72
+ return args.last
73
+ end
74
+ alias []= modify
75
+
76
+ end
77
+
78
+ class OfArray < Indexed
79
+ class<<self
80
+ alias new _orig_new
81
+ end
82
+ include ArrayLike
83
+ #scan,scan_until provided by ArrayLike
84
+
85
+ #a better, string-like #index... with an offset parameter
86
+ #scalar matchers only
87
+ def index(pat,offset=0)
88
+ pat=pat.dup
89
+ class<<pat; alias == ===; end
90
+ offset.zero? and return( @data.index pat)
91
+ @data[offset..-1].index(pat)+offset
92
+ end
93
+
94
+ #a better, string-like #rindex... with an offset parameter
95
+ #scalar matchers only
96
+ def rindex(pat,offset=size)
97
+ pat=pat.dup
98
+ class<<pat; alias == ===; end
99
+ (offset==size ? @data : @data[0...offset]).rindex pat
100
+ end
101
+
102
+ def append(arr)
103
+ sz=size
104
+ @data.push(*arr)
105
+ notify_change(self,sz,0,arr.size)
106
+ self
107
+ end
108
+
109
+ def new_data
110
+ []
111
+ end
112
+
113
+ end
114
+
115
+ class OfString < Indexed
116
+ class<<self
117
+ alias new _orig_new
118
+ end
119
+
120
+ include StringLike
121
+ def scan(pat)
122
+ holding?{case pat
123
+ when Regexp:
124
+ if (m=match pat,true)
125
+ @pos= m.end(0)
126
+ m.to_s
127
+ end
128
+ when Integer: (res=@data[@pos])==pat and @pos+=1 and res
129
+ when String: @data[@pos...@pos+=pat.size]==pat and pat
130
+ end}
131
+ end
132
+
133
+ def scanback(pat)
134
+ holding?{case pat
135
+ when Regexp:
136
+ if m=matchback(pat,true)
137
+ @pos= m.begin(0)
138
+ m.to_s
139
+ end
140
+ when Integer: @data[@pos-=1]==pat and pat.chr
141
+ when String: @data[@pos-=pat.size,pat.size]==pat and pat
142
+ end}
143
+ end
144
+
145
+ def scan_until(pat)
146
+ if Regexp===pat
147
+ return(if (m=match pat,false)
148
+ @pos= m.end(0)
149
+ m.pre_match+m.to_s
150
+ end)
151
+ end
152
+ i=@data.index(pat,pos) or return
153
+ @data[@pos...@pos=i]
154
+ end
155
+
156
+ def scanback_until(pat)
157
+ if Regexp===pat
158
+ return(if (m=matchback pat,true)
159
+ @pos= m.begin(0)
160
+ m.to_s+m.post_match
161
+ end)
162
+ end
163
+ i=@data.rindex(pat,pos) or return
164
+ oldpos=@pos
165
+ @data[@pos=i...oldpos]
166
+ end
167
+
168
+ def match pat,anchored=true,len=size
169
+ len=size
170
+ anchored and pat=_anchor(pat)
171
+ #pat.last_match=
172
+ self.last_match=Thread.current[:last_match]=
173
+ #can't use String#index here... doesn't do anchors right
174
+ if pat.match @data[pos..-1]
175
+ newpos=@pos+$~.end(0)
176
+ fixup_match_result( $~,[],@pos,:post){
177
+ SubSeq.new(self,newpos,size-newpos)
178
+ }
179
+ end
180
+ end
181
+
182
+ def matchback pat,anchored=true
183
+ anchored and pat=_anchor(pat,:back)
184
+ #pat.last_match=
185
+ self.last_match=Thread.current[:last_match]=
186
+ if pat.match @data[0...pos]
187
+ fixup_match_result($~,[],0,:pre){
188
+ cu=SubSeq.new(self,0,pos=$~.pre_match.size)
189
+ cu.pos=pos
190
+ cu
191
+ }
192
+ end
193
+ end
194
+
195
+ def index(pat,offset=0)
196
+ @data.index(pat,offset)
197
+ end
198
+
199
+ def rindex(pat,offset=0)
200
+ @data.rindex(pat,offset)
201
+ end
202
+
203
+
204
+ def append(str)
205
+ sz=size
206
+ @data << str
207
+ notify_change(self,sz,0,str.size)
208
+ self
209
+ end
210
+
211
+ def new_data
212
+ ""
213
+ end
214
+
215
+
216
+ end
217
+
218
+ end
219
+
220
+ class Array
221
+ # convert an array to a seq starting at +pos+
222
+ def to_sequence(pos=0)
223
+ Sequence::OfArray.new(self,pos)
224
+ end
225
+ end
226
+
227
+ class String
228
+ # convert a string to a seq starting at +pos+
229
+ def to_sequence (pos=0)
230
+ Sequence::OfString.new(self,pos)
231
+ end
232
+ end
233
+
234
+