|
|
 |
 |
 |
 |
Ruby Programming Language
|
 |
 |
 |
 |
 |
 |
 |
 |
What about a 'series' type?
I'm sure everyone is fimilar with ranges: (1..10).to_a = [1,2,3,4,5,6,7,8,9,10] (from manual) But this isn't any good if you want a arithmetic [3,5,7,9] or geometric [2,6,18,54] series. So I suggest this: arithmetic_series = Arith.new(First_Term,Common_Difference) aritmetic_series[9] will then generate the 10th term of the sequence (0 being the first to be consistent) and arithmetic_series[0..9] will return an array with the first to tenth terms. The same goes for a geometric series. gemometric_series = Geometric.new(First_Term,Common_Ratio) Now, there may be other ways to achieve this, but I think this way is nicer. -- Posted via http://www.ruby-forum.com/.
On 6/7/07, Peter Marsh <evil_grun@hotmail.com> wrote: Honestly I do not believe that the core is the place to put such things, furthermore I believe it is too specific a feature, a more general approach might have better chances to be fit for the core; Yet I do not think what follows is fit for the core either, but maybe you find it interesting or helpful: class Lazy def initialize init, op, *args @init = init @op = op @args = args.dup end def upto value return [] if value < 1 (2..value).inject([@init]){ |acc,| acc << acc.last.send( @op, *@args ) } end end # class Lazy 505/6 > irb -r lazy.rb irb(main):001:0> l = Lazy.new 1, :+, 2 => #<Lazy:0xb7ddfa60 @args=[2], @init=1, @op=:+> irb(main):002:0> l.upto 5 => [1, 3, 5, 7, 9] irb(main):003:0> m = Lazy.new 2, :*, 3 => #<Lazy:0xb7dd4908 @args=[3], @init=2, @op=:*> irb(main):004:0> m.upto 4 => [2, 6, 18, 54] irb(main):005:0> Cheers Robert P.S. Implementations without #inject are theoretically possible ;) R. -- You see things; and you say Why? But I dream things that never were; and I say Why not? -- George Bernard Shaw
In message <cd08f63ca2ad51e567d4288410f59@ruby-forum.com>, Peter Marsh writes: >But this isn't any good if you want a arithmetic [3,5,7,9] or geometric >[2,6,18,54] series. So I suggest this: >arithmetic_series = Arith.new(First_Term,Common_Difference) >aritmetic_series[9] will then generate the 10th term of the sequence (0 >being the first to be consistent) and arithmetic_series[0..9] will >return an array with the first to tenth terms. The same goes for a >geometric series. >gemometric_series = Geometric.new(First_Term,Common_Ratio) >Now, there may be other ways to achieve this, but I think this way is >nicer.
These seem like special cases of Series.new([Start Terms], Proc). (Consider Fibbonacci sequences, for instance, or Pascal's Triangle.) -s
Actually, the syntax: a = Series.new(8) {|i| 5*i**3} a.collect{|i| i + 1}.select{|i| i % 2 == 0} # i.e. a is an enumerable object is quite cool and might be useful in some cases. The only problem I'm trying to solve is that of providing syntax for persistence (e.g. try to implement a series of Fibonacci numbers with the iterative algorithm using this Series syntax). Perhaps an optional hash argument that is used to give initial values to instance variables? With persistence, this could be a very useful idiom in many cases, and maybe even deserves to be stdlib. Aur On 6/7/07, Peter Seebach <s@seebs.net> wrote:
> In message <cd08f63ca2ad51e567d4288410f59 @ruby-forum.com>, Peter Marsh writes: > >But this isn't any good if you want a arithmetic [3,5,7,9] or geometric > >[2,6,18,54] series. So I suggest this: > >arithmetic_series = Arith.new(First_Term,Common_Difference) > >aritmetic_series[9] will then generate the 10th term of the sequence (0 > >being the first to be consistent) and arithmetic_series[0..9] will > >return an array with the first to tenth terms. The same goes for a > >geometric series. > >gemometric_series = Geometric.new(First_Term,Common_Ratio) > >Now, there may be other ways to achieve this, but I think this way is > >nicer. > These seem like special cases of Series.new([Start Terms], Proc). > (Consider Fibbonacci sequences, for instance, or Pascal's Triangle.) > -s
On Jun 7, 2007, at 5:00 AM, Peter Marsh wrote:
> I'm sure everyone is fimilar with ranges: > (1..10).to_a = [1,2,3,4,5,6,7,8,9,10] (from manual) > But this isn't any good if you want a arithmetic [3,5,7,9] or > geometric > [2,6,18,54] series. So I suggest this: > arithmetic_series = Arith.new(First_Term,Common_Difference) > aritmetic_series[9] will then generate the 10th term of the > sequence (0 > being the first to be consistent) and arithmetic_series[0..9] will > return an array with the first to tenth terms. The same goes for a > geometric series. > gemometric_series = Geometric.new(First_Term,Common_Ratio) > Now, there may be other ways to achieve this, but I think this way is > nicer. > -- > Posted via http://www.ruby-forum.com/.
it can be done with lambdas in ruby on a case by case basis: cfp:~ > cat a.rb arithmetic = lambda do |i| if i.respond_to? :map i.map{|j| arithmetic[j]} else i >= 1 ? (2 + arithmetic[i-1]) : 1 end end p arithmetic[0] p arithmetic[1] p arithmetic[4] p arithmetic[0..9] cfp:~ > ruby a.rb 1 3 9 [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] kind regards. -a -- we can deny everything, except that we have the possibility of being better. simply reflect on that. h.h. the 14th dalai lama
SonOfLilit wrote: > Actually, the syntax: > a = Series.new(8) {|i| 5*i**3} > a.collect{|i| i + 1}.select{|i| i % 2 == 0} # i.e. a is an enumerable > object > is quite cool and might be useful in some cases. The only problem I'm > trying to solve is that of providing syntax for persistence (e.g. try > to implement a series of Fibonacci numbers with the iterative > algorithm using this Series syntax). Perhaps an optional hash argument > that is used to give initial values to instance variables? > With persistence, this could be a very useful idiom in many cases, and > maybe even deserves to be stdlib. > Aur
The two examples I gave are fairly specific, I didn't really think about that. However, I still think a 'series' type would be useful, even if the implimentation is different to my example. Taking prime numbers as an example using the current 'prime' class you have to use: require 'mathn' primes = Prime.new etc With a seires type you could do something like this: primes = Series.new(Starting_value,Some_block_to_define_serires) primes[0] = 2 primes[0..2] >> [2,3,5]
Giving definate advantages, I feel. -- Posted via http://www.ruby-forum.com/.
> primes = Series.new(Starting_value,Some_block_to_define_serires)
Just wanted to be a bit clearer about this, the second argument is a block which can generate nth term in a series. This would probally take a while for primes, but if it were recursive then it would be easier... -- Posted via http://www.ruby-forum.com/.
On 07.06.2007 16:52, Peter Marsh wrote: >> primes = Series.new(Starting_value,Some_block_to_define_serires) > Just wanted to be a bit clearer about this, the second argument is a > block which can generate nth term in a series. This would probally take > a while for primes, but if it were recursive then it would be easier...
IMHO for a series it would be more natural to let the block calculate a[n+1] from a[n] wouldn't it? Of course, for Fibonacci this would only work if you allow for multiple arguments. Something like #!ruby class Serial include Enumerable def initialize(*init, &f) @init = init @f = f end def each(&b) a = b.arity current = @init loop do b[*current[0 ... a]] current = Array(@f[*current]) end self end end s1 = Serial.new 0 do |x| x+1 end s1.each {|x| p x; break if x > 10} puts s2 = Serial.new 0,1 do |a,b| [b,a+b] end s2.each {|x| p x; break if x > 40} Kind regards robert
On 6/7/07, Robert Klemme <shortcut@googlemail.com> wrote:
> On 07.06.2007 16:52, Peter Marsh wrote: > >> primes = Series.new(Starting_value,Some_block_to_define_serires) > > Just wanted to be a bit clearer about this, the second argument is a > > block which can generate nth term in a series. This would probally take > > a while for primes, but if it were recursive then it would be easier... > IMHO for a series it would be more natural to let the block calculate > a[n+1] from a[n] wouldn't it? Of course, for Fibonacci this would only > work if you allow for multiple arguments. > Something like > #!ruby > class Serial > include Enumerable > def initialize(*init, &f) > @init = init > @f = f > end > def each(&b) > a = b.arity > current = @init > loop do > b[*current[0 ... a]] > current = Array(@f[*current]) > end > self > end > end > s1 = Serial.new 0 do |x| x+1 end > s1.each {|x| p x; break if x > 10} > puts > s2 = Serial.new 0,1 do |a,b| [b,a+b] end > s2.each {|x| p x; break if x > 40}
Robert I hope you do not mind my fantasy about/over/at??? your theme. class Serial include Enumerable def initialize(*init, &f) @init = init @f = f @arity = f.arity end def get_some( some = nil, &b) @current = @init.dup if some && b.nil? then (0...some).inject([]) do |acc,| compute_next acc << @current.first end else a = b.arity loop do break if some && ( some -= 1 ) < 0 b[*@current.first( a )] compute_next end self end end private def compute_next @current += Array( @f[*@current] ) @current = @current.last @arity end end s1 = Serial.new 0 do |x| x+1 end s1.get_some {|x| p x; break if x > 10} puts "-"*42 puts s1.get_some(6) puts "-"*42 f = Serial.new 1, 1 do |x, y| x + y end f.get_some(6){ |x,| puts x } puts "-"*42 a = Serial.new 1, 1, 1 do |x, y, z| x + y + z end puts a.get_some(6) Cheers Robert -- You see things; and you say Why? But I dream things that never were; and I say Why not? -- George Bernard Shaw
On 6/7/07, Robert Dober <robert.do@gmail.com> wrote:
> On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: > > On 07.06.2007 16:52, Peter Marsh wrote: > > >> primes = Series.new(Starting_value,Some_block_to_define_serires) > > > Just wanted to be a bit clearer about this, the second argument is a > > > block which can generate nth term in a series. This would probally take > > > a while for primes, but if it were recursive then it would be easier... > > IMHO for a series it would be more natural to let the block calculate > > a[n+1] from a[n] wouldn't it? Of course, for Fibonacci this would only > > work if you allow for multiple arguments. > > Something like > > #!ruby > > class Serial > > include Enumerable > > def initialize(*init, &f) > > @init = init > > @f = f > > end > > def each(&b) > > a = b.arity > > current = @init > > loop do > > b[*current[0 ... a]] > > current = Array(@f[*current]) > > end > > self > > end > > end > > s1 = Serial.new 0 do |x| x+1 end > > s1.each {|x| p x; break if x > 10} > > puts > > s2 = Serial.new 0,1 do |a,b| [b,a+b] end > > s2.each {|x| p x; break if x > 40} > Robert I hope you do not mind my fantasy about/over/at??? your theme. > class Serial > include Enumerable > def initialize(*init, &f) > @init = init > @f = f > @arity = f.arity > end > def get_some( some = nil, &b) > @current = @init.dup > if some && b.nil? then > (0...some).inject([]) do |acc,| > compute_next > acc << @current.first > end > else > a = b.arity > loop do > break if some && ( some -= 1 ) < 0 > b[*@current.first( a )] > compute_next > end > self > end > end > private > def compute_next > @current += Array( @f[*@current] ) > @current = @current.last @arity > end > end > s1 = Serial.new 0 do |x| x+1 end > s1.get_some {|x| p x; break if x > 10} > puts "-"*42 > puts s1.get_some(6) > puts "-"*42 > f = Serial.new 1, 1 do |x, y| x + y end > f.get_some(6){ |x,| puts x } > puts "-"*42 > a = Serial.new 1, 1, 1 do |x, y, z| x + y + z end > puts a.get_some(6) > Cheers > Robert > -- > You see things; and you say Why? > But I dream things that never were; and I say Why not? > -- George Bernard Shaw
Isn't ruby cool? -- -fREW
On 07.06.2007 21:41, Robert Dober wrote: > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: > Robert I hope you do not mind my fantasy about/over/at??? your theme.
Not at all. I had thought about the limit myself but did not want to bother implementing it. Two remarks about your code: You include Enumerable but do not provide #each which is required. You use instance variables for the iteration which is a bad thing because this needless restricts usability (namely in the light of multiple threads). Array and other's also do not store the iteration state in instance variables but in local variables in method #each. You can try it with something like this which would not work if instance variables would be used for storing iteration state a=(1..10).to_a 2.times {|i| Thread.new(i) {|j| a.each {|x| puts "[#{j}-#{x}]"; sleep 0.5}}} Kind regards robert
On 6/7/07, Robert Klemme <shortcut@googlemail.com> wrote: > On 07.06.2007 21:41, Robert Dober wrote: > > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: > > Robert I hope you do not mind my fantasy about/over/at??? your theme. > Not at all. I had thought about the limit myself but did not want to > bother implementing it. Two remarks about your code: > You include Enumerable but do not provide #each which is required.
Sure, bad error; that was a leftover of your code, I do not really want to include it. > You use instance variables for the iteration which is a bad thing > because this needless restricts usability (namely in the light of > multiple threads).
I am not sure I understand this point, but we can get rid of that by expanding compute_next, but this will make the code much less readable :( The whole beast is not thread safe at all I guess, even without the instance var. It would be a nice challenge to make it thread safe, I guess we would need to cache values and synchronize the computation part, sounds *very* expansive, time and memory wise; probably not worth it . >Array and other's also do not store the iteration > state in instance variables but in local variables in method #each.
Would that not be for performance reasons? > You > can try it with something like this which would not work if instance > variables would be used for storing iteration state > a=(1..10).to_a > 2.times {|i| Thread.new(i) {|j| a.each {|x| puts "[#{j}-#{x}]"; sleep 0.5}}}
I guess I do not know enough to understand this :( How is concurrent access to the method not a problem with local variables? However it would be sufficient to synchronize the access to the method only and not the access to the ivar. Is that what you are worried about? Cheers Robert -- You see things; and you say Why? But I dream things that never were; and I say Why not? -- George Bernard Shaw
On Behalf Of Peter Marsh:
# [2,6,18,54] series. So I suggest this:
# arithmetic_series = Arith.new(First_Term,Common_Difference)
nice, but i am poor in english, and there are too many words in ruby already.
i'd propose extending Range a bit (i hope), like
Range.new(start, end, exclusive=false, diff=&:succ)
where diff could be
&:succ where next term is succ
this is the current implem
&:pred where next term is pred (succ==pred)
this has the effect of defining a reverse range
("z".."x")==("x".."z").reverse == Range.new("z".."x",,&:pred)
"z".succ => "y"
"y".succ => "x"
eg, r=Range.new("z","x",,&:pred)
r.each{|x| p x} => z y x
&:+n where next term is term+n
&:-n where next term is term-n
eg, r=Range.new(100,96,,&:-2)
r.each{|x| p x} => 100 98 96
&:*n where next term is term*n
&:/n where next term is term/n
{blck} where {blck} is a code block
where {block} defines next/succ
eg, r=Range.new("0a","zz",) do |x| case
when x=="0z"
"aa"
else
x.succ
end
end
r.each{|x| p x} => 0a 0b.. 0z aa ab ..yz zz
or something like that to that effect..
On 07.06.2007 23:12, Robert Dober wrote:
> On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: >> On 07.06.2007 21:41, Robert Dober wrote: >> > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: >> > Robert I hope you do not mind my fantasy about/over/at??? your theme. >> Not at all. I had thought about the limit myself but did not want to >> bother implementing it. Two remarks about your code: >> You include Enumerable but do not provide #each which is required. > Sure, bad error; that was a leftover of your code, I do not really > want to include it. >> You use instance variables for the iteration which is a bad thing >> because this needless restricts usability (namely in the light of >> multiple threads). > I am not sure I understand this point, but we can get rid of that by > expanding compute_next, but this will make the code much less readable > :(
You can as well add parameters and return values to compute_next. > The whole beast is not thread safe at all I guess, even without the > instance var.
Oh, it's perfectly thread safe if you change the use of instance variables. > It would be a nice challenge to make it thread safe, I guess we would > need to cache values and synchronize the computation part, sounds > *very* expansive, time and memory wise; probably not worth it .
Caching is only needed if you want to make it faster or more efficient. But for a general implementation I would not do it as it can have all sorts of unwanted side effects. >> Array and other's also do not store the iteration >> state in instance variables but in local variables in method #each. > Would that not be for performance reasons?
That's another advantage. >> You >> can try it with something like this which would not work if instance >> variables would be used for storing iteration state >> a=(1..10).to_a >> 2.times {|i| Thread.new(i) {|j| a.each {|x| puts "[#{j}-#{x}]"; sleep >> 0.5}}} > I guess I do not know enough to understand this :( > How is concurrent access to the method not a problem with local variables?
Because then there is iteration state per method invocation. Compare these two variants of #each: class Foo # a must be an Array def initialize(a) @a=a.dup end # thread safe def each_1 for i in 0@a.size yield @a[i] end self end # not thread safe def each_2 for @i in 0@a.size yield @a[@i] end self end end Now, think about what happens if two threads invoke #each_1 and #each_2. > However it would be sufficient to synchronize the access to the method > only and not the access to the ivar.
That would work but it would limit usability - and there is no need to do that. Kind regards robert
On 6/8/07, Robert Klemme <shortcut@googlemail.com> wrote: > On 07.06.2007 23:12, Robert Dober wrote: > > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: > >> On 07.06.2007 21:41, Robert Dober wrote: > >> > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: First of all thx for your time but I am not a quick learner :( Obviously I am (or rather was )missing something very basic and if I understand correctly that very basic is that method_invocations are on copies of the local data, in each thread, is this *really* true? Well you already showed with an example. Hopefully I can put this into work tonight, gotta go back to work here, hopefully this weeks Ruby Quiz will be boooring, please James ;) Robert -- You see things; and you say Why? But I dream things that never were; and I say Why not? -- George Bernard Shaw
On 08.06.2007 14:27, Robert Dober wrote: > On 6/8/07, Robert Klemme <shortcut @googlemail.com> wrote: >> On 07.06.2007 23:12, Robert Dober wrote: >> > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: >> >> On 07.06.2007 21:41, Robert Dober wrote: >> >> > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: > First of all thx for your time but I am not a quick learner :( > Obviously I am (or rather was )missing something very basic and if I > understand correctly that very basic is that method_invocations are on > copies of the local data, in each thread, is this *really* true? Well > you already showed with an example.
Even more fine granular: there is one copy of local variables /per invocation/ of a method. You can play a bit with this to see the effect: def recurse(i = 5) s = "#{Thread.current.inspect} #{i} " print s, "enter\n" recurse(i - 1) if i > 0 print s, "leave\n" end If you then do something like 2.times { Thread.new { recurse } } You'll notice how values for "enter" decrease nicely and increase for "leave" per thread. Internally Ruby has a stack that stores all local variables (including method arguments) and every time a method is invoked a new stack frame is put on the stack which stores all those values. This is basically how most popular programming languages work. > Hopefully I can put this into work tonight, gotta go back to work > here, hopefully this weeks Ruby Quiz will be boooring, please James ;)
:-) Kind regards robert
On 6/8/07, Robert Klemme <shortcut@googlemail.com> wrote: > On 08.06.2007 14:27, Robert Dober wrote: > > On 6/8/07, Robert Klemme <shortcut @googlemail.com> wrote: > >> On 07.06.2007 23:12, Robert Dober wrote: > >> > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: > >> >> On 07.06.2007 21:41, Robert Dober wrote: > >> >> > On 6/7/07, Robert Klemme <shortcut @googlemail.com> wrote: That's a dialog, right, I wonder if OP lost interest or did I just hitchhike another thread? let us see what I came up with under your tutorship. require 'test/unit' class Serial def initialize(*init, &f) @init = init @f = f @arity = f.arity end def get_some( some = nil, &b) current = @init.dup if some && b.nil? then r = @init.dup (some - r.size).times do current = compute_next( current ) r << current.last end r else a = b.arity loop do break if some && ( some -= 1 ) < 0 b[ *current.first( a ) ] current = compute_next( current ) end self end end private def compute_next current (current + Array( @f[*current] ) ).last @arity end end if __FILE__ == $0 then class Testee < Test::Unit::TestCase def test_1 s1 = Serial.new 0 do |x| x+1 end assert_equal [*0..3], s1.get_some( 4 ) a = [] s1.get_some{ |x| a << x; break if x > 10 } assert_equal [*0..11], a end # def test_1 def test_2 f = Serial.new 1, 1 do |x, y| x + y end assert_equal [1,1,2,3,5,8], f.get_some(6) a = [] f.get_some{ |x| a << x; break if x > 10 } assert_equal [1,1,2,3,5,8,13], a a = [] f.get_some(4){ |x| a.unshift x } assert_equal [3,2,1,1], a end # def test_2 def test_3 f = Serial.new 0, 0, 1 do |a, b, c| 3*a + 2*b + c end assert_equal [0,0,1,1,3,8,17,42,100], f.get_some(9) end # def test_3 end # class Testee < Test::Unit::TestCase end Thanks again Robert -- You see things; and you say Why? But I dream things that never were; and I say Why not? -- George Bernard Shaw
|
 |
 |
 |
 |
|