From: horndude77@... Date: 2006-02-13T14:53:25+09:00 Subject: [QUIZ][SOLUTION] FasterGenerator (#66) I'd been wanting to mess with writing ruby extensions so I wrote the whole thing in c. I do realize one thing already: I'm using the 'entries' method to pull out what to iterate over instead of each. This comes from something that I'm totally clear on: The contract for the method is that we receive in an Enumrable object so can we assume that this method will be implemented since it's part of the Enumerable mixin? Anyways here's the code: #include "ruby.h" static ID id_entries; typedef struct _gen { int curr; VALUE values; } Generator; //Is there a built in way to do this? #define TEST(t) t?Qtrue:Qfalse static VALUE t_init(int argc, VALUE *argv, VALUE self) { Generator* gen; Data_Get_Struct(self, Generator, gen); if(argc > 0) { VALUE arr = rb_funcall(argv[0], id_entries, 0); gen->values = arr; } else { VALUE arr = rb_ary_new(); gen->values = arr; rb_yield(self); } gen->curr = 0; return self; } static VALUE t_end_q(VALUE self) { Generator* gen; Data_Get_Struct(self, Generator, gen); int size = RARRAY(gen->values)->len; int curr = gen->curr; return TEST(curr >= size); } static VALUE t_next_q(VALUE self) { return TEST(!t_end_q(self)); } static VALUE t_pos(VALUE self) { Generator* gen; Data_Get_Struct(self, Generator, gen); int curr = gen->curr; return INT2NUM(curr); } static VALUE t_rewind(VALUE self) { Generator* gen; Data_Get_Struct(self, Generator, gen); gen->curr = 0; return self; } static VALUE t_yield(VALUE self, VALUE element) { Generator* gen; Data_Get_Struct(self, Generator, gen); rb_ary_push(gen->values, element); return gen->values; } static VALUE t_current(VALUE self) { if(t_end_q(self)) { rb_raise(rb_eEOFError, "no more elements available"); return Qnil; } Generator* gen; Data_Get_Struct(self, Generator, gen); int curr = gen->curr; return rb_ary_entry(gen->values, curr); } static VALUE t_next(VALUE self) { if(t_end_q(self)) { rb_raise(rb_eEOFError, "no more elements available"); return Qnil; } Generator* gen; Data_Get_Struct(self, Generator, gen); int curr = gen->curr++; VALUE temp = rb_ary_entry(gen->values, curr); return temp; } static VALUE t_each(VALUE self) { Generator* gen; Data_Get_Struct(self, Generator, gen); gen->curr = 0; rb_iterate(rb_each, gen->values, rb_yield, 0); return self; } static void gen_free(void* p) { free(p); } static void gen_mark(void* p) { Generator* g = p; rb_gc_mark(g->values); } static VALUE gen_alloc(VALUE klass) { Generator* gen = malloc(sizeof(Generator)); VALUE obj; obj = Data_Wrap_Struct(klass, gen_mark, gen_free, gen); return obj; } VALUE cGen; void Init_generator_j() { id_entries = rb_intern("entries"); cGen = rb_define_class("GeneratorJ", rb_cObject); rb_define_method(cGen, "initialize", t_init, -1); rb_define_method(cGen, "next?", t_next_q, 0); rb_define_method(cGen, "next", t_next, 0); rb_define_method(cGen, "end?", t_end_q, 0); rb_define_method(cGen, "pos", t_pos, 0); rb_define_method(cGen, "index", t_pos, 0); rb_define_method(cGen, "rewind", t_rewind, 0); rb_define_method(cGen, "yield", t_yield, 1); rb_define_method(cGen, "current", t_current, 0); rb_define_method(cGen, "each", t_each, 0); rb_define_alloc_func(cGen, gen_alloc); } I haven't written much c in a long while (let alone ruby in c) so be kind. It doesn't seem to be much of a speed improvement at all over the same code written in straight ruby, but it was educational for me. I did mess with trying to get infinite generators to work, but I cheated and looked at the code after a bit so I was tainted after that. Here is a test case I made however if it's of any use. (require 'matrix' of course): def fib(n) (Matrix[[1,1],[1,0]]**(n-1))[0,0] end def test_fib g = Generator.new do |g| a, b = 0, 1 while true g.yield a a, b = b, a+b end end 100.times do |i| assert_equal(i, g.pos) assert_equal(fib(i), g.next) end end Thanks! -----Horndude77