diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8146014 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +functional.gemspec +pkg diff --git a/README.md b/README.md index d625a86..1478272 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +Obsolete +======== + +Use `#lazy`. + Install ======= @@ -8,8 +13,88 @@ Usage require 'functional' - obj = 0 .. 10**12 - Functional.new( obj).select {|i| i.even? }.collect {|i| i/3 }.select {|i| i.even? }.each &method( :puts) + # To demonstrate Functional, we create a Class with a infinite loop: + class Sequence + include Enumerable + def initialize first = 0, step = 1 + @i, @step = first, step + end + def each + # Our infinite loop: + loop do + yield @i + @i += @step + end + end + end + + Functional.new( Sequence.new). + select {|i| i.even? }. + collect {|i| i/3 }. + select {|i| i.even?}. + collect {|i| [[[[[[i.even?, i.odd?]]], i, [[[[[[i.class]]]]]]]]] }. + flatten. # It flattens everything! Not like: collect {|i| i.flatten }. + p + + # Without Functional... Bye bye. + Sequence.new. + select {|i| i.even? }. + collect {|i| i/3 }. + select {|i| i.even?}. + collect {|i| [[[[[[i.even?, i.odd?]]], i, [[[[[[i.class]]]]]]]]] }. + flatten. # It flattens everything! Not like: collect {|i| i.flatten }. + p + +It will never realize, that #p doesn't exists, because the first select runs endless. +Functional#p prints everything to stdout. + + (0..100000).to_fun. + collect {|i| i*3 }. + select {|i| i%5 == 2 }. + to_a + +Thanks to `Symbol#to_proc`: + + Sequence.new.to_fun. + select( &:even?). + collect {|i| i/3 }. + select( &:even?). + collect {|i| [[[[[[i.even?, i.odd?]]], i, [[[[[[i.class]]]]]]]]] }. + flatten. # It flattens everything! Not like: collect {|i| i.flatten }. + p + +If you know methodphitamine, combine it: + + require 'methodphitamine' + + Sequence.new.to_fun. + select( &it.even?). + collect( &it/3). + select( &it.even?). + collect {|i| [[[[[[i.even?, i.odd?]]], i, [[[[[[i.class]]]]]]]]] }. + flatten. + p + + (0..100000).to_fun. + collect( &it*3). + select( &it%5 == 2). + to_a + +Makros +====== + + seq = Sequence.new.to_fun + seq = seq.select &it.even? if must_be_even + seq = seq. + collect( &it/3). + select( &it.even?). + collect {|i| [[[[[[i.even?, i.odd?]]], i, [[[[[[i.class]]]]]]]]] } + seq = seq.flatten if please_flatten + if print_it + seq.p + else + seq_to_a + end What's with _#map_? ================= diff --git a/VERSION b/VERSION index 8acdd82..9faa1b7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1 +0.1.5 diff --git a/lib/functional.rb b/lib/functional.rb index dce2126..f129e3d 100644 --- a/lib/functional.rb +++ b/lib/functional.rb @@ -1,46 +1,465 @@ - -class Functional - include Enumerable - - def self.method_missing meth, *args, &exe - self.new.send meth, *args, &exe +class ::Regexp + class NegRegexp + def initialize r + @rx = r + end + def match l + ! @rx.match( l) + end + def =~ l + ! @rx =~ l + end + def -@ + @rx + end end - def push_method code, *args, &exe - name = "__meth_#{exe.object_id}" - define_singleton_method name, &exe - @stack.push [code % name]+args + def -@ + NegRegexp.new self + end +end + +class ::Object + def to_fun meth = nil + Functional.new self, meth + end +end + +class Counter + include Enumerable + attr_reader :c + + def initialize first = nil, step = nil + @c, @step = first || 0, step || 1 + end + + def next; @c += @step end + def to_i; c.to_i end + def to_f; c.to_f end + + def + i + @c += @step*i + end + + def each &e + loop { e.call self; self.next } + end +end + +class Functional + def self.__version__() '0.1.5' end + include Enumerable + + class DEFAULT + end + + class Base + attr_reader :exe + attr_accessor :next + attr_reader :caller + + def initialize &e + @caller = Kernel.caller.first + @exe = e + end + + def base_fun *a + @next.call *a + end + alias call base_fun + + def end + @next.end + end + + def clean + @next.clean + end + + def to_proc + method( :call).to_proc + end + end + + class Collect @buf.size + @next.call @buf + @buf.clear + end + end + alias call slice_fun + + def end + @next.call @buf + @next.end + end + end + + class Cons @buf.size + class < @buf.size + @next.end + end + end + + class Pager [ [0, _A], [1, _B], ..., [_I, _C], ..., [_N, _D]] + # [ [_A|_As], [_B|_Bs], ..., [_C|_Cs], ..., [_D|_Ds] ] ==> [ [0,_A|_As], [1,_B|_Bs], ..., [_I,_C|_Cs], ..., [_N,_D|_Ds] ] + def with_index &exe + i = 0 + exe ||= Array.method :[] + push Collect.new {|*a| exe.call i, *a; i += 1 } + end + + def slice n, &exe + push Slice.new( n) + block_given? ? self.collect( &exe) : self + end + + def cons n, &exe + push Cons.new( n) + block_given? ? self.collect( &exe) : self + end + + def tap &exe + push Tap.new( &exe) + end + + class Save < Base + attr_reader :db + + def initialize db + @db = db + end + + def call k, *v + @db[ k] = v.length == 1 ? v.first : v + end + end + + def save db + push Save.new( db) end end diff --git a/test/functional_test.rb b/test/functional_test.rb new file mode 100644 index 0000000..9497c66 --- /dev/null +++ b/test/functional_test.rb @@ -0,0 +1,29 @@ +require 'test/unit' +require 'functional' + +class FunTest < Test::Unit::TestCase + M = 0..100 + + def doit_fun m, &exe + f = m.to_fun + yield f + f.to_a + end + + def test_to_fun_exists + assert_respond_to Object, :to_fun + end + + def test_to_a + assert_equal M.to_a, doit_fun( M) {|x| x } + end + + def test_collect + l = lambda {|x| x*2} + assert_equal M.collect( &l), doit_fun( M) {|x| x.collect( &l) } + end + + def test_inject + assert_equal M.inject( 0) {|i,j| i+j }, M.to_fun.inject( 0) {|i,j| i+j } + end +end