Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
Denis Knauf | ede467e934 | |
Denis Knauf | 145129641d | |
Denis Knauf | 99a268a89d | |
Denis Knauf | c8edcddcf6 | |
Denis Knauf | 53033b8cdd | |
Denis Knauf | 29e7a0e4e2 | |
Denis Knauf | 77d2cfb488 | |
Denis Knauf | 11128ea131 | |
Denis Knauf | ed649d3801 | |
Denis Knauf | 70e8c7ea29 | |
Denis Knauf | a97c4ed1f9 | |
Denis Knauf | 68ab7003d5 | |
Denis Knauf | abb9954a33 | |
Denis Knauf | 2b7dd9fb58 | |
Denis Knauf | 362a987544 | |
Denis Knauf | 275183d346 | |
Denis Knauf | a5b273033b | |
Denis Knauf | 95a3027926 |
|
@ -0,0 +1,2 @@
|
|||
functional.gemspec
|
||||
pkg
|
89
README.md
89
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_?
|
||||
=================
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
class ::Regexp
|
||||
class NegRegexp
|
||||
def initialize r
|
||||
|
@ -21,57 +20,101 @@ class ::Regexp
|
|||
end
|
||||
|
||||
class ::Object
|
||||
def functional meth = nil
|
||||
def to_fun meth = nil
|
||||
Functional.new self, meth
|
||||
end
|
||||
alias to_fun functional
|
||||
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 call *a
|
||||
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 <Base
|
||||
def call *a
|
||||
def collect_fun *a
|
||||
@next.call *@exe.call( *a)
|
||||
end
|
||||
alias call collect_fun
|
||||
end
|
||||
|
||||
class Tap <Base
|
||||
def tap_fun *a
|
||||
@exe.call *a
|
||||
@next.call *a
|
||||
end
|
||||
alias call tap_fun
|
||||
end
|
||||
|
||||
class Select <Base
|
||||
def call *a
|
||||
def select_fun *a
|
||||
@next.call *a if @exe.call *a
|
||||
end
|
||||
alias call select_fun
|
||||
end
|
||||
|
||||
class DeleteIf <Base
|
||||
def call *a
|
||||
class Filter <Base
|
||||
def filter_fun *a
|
||||
@next.call *a unless @exe.call *a
|
||||
end
|
||||
alias call filter_fun
|
||||
end
|
||||
|
||||
class Compact <Base
|
||||
def call *a
|
||||
def compact_fun *a
|
||||
@next.call *a unless a.empty? || [nil] == a
|
||||
end
|
||||
alias call compact_fun
|
||||
end
|
||||
|
||||
class BottomUp <Base
|
||||
|
@ -80,7 +123,7 @@ class Functional
|
|||
@buffer, @start = nil, start
|
||||
end
|
||||
|
||||
def call a
|
||||
def bottom_up_fun a
|
||||
if @exe.call a
|
||||
@next.call @buffer+a
|
||||
@buffer = @start.dup
|
||||
|
@ -88,6 +131,7 @@ class Functional
|
|||
@buffer += a
|
||||
end
|
||||
end
|
||||
alias call bottom_up_fun
|
||||
|
||||
def end
|
||||
@next.call @buffer
|
||||
|
@ -101,7 +145,7 @@ class Functional
|
|||
@buffer, @start = nil, start
|
||||
end
|
||||
|
||||
def call a
|
||||
def top_down_fun a
|
||||
if @exe.call a
|
||||
@next.call @buffer
|
||||
@buffer = @start.dup+a
|
||||
|
@ -109,6 +153,7 @@ class Functional
|
|||
@buffer += a
|
||||
end
|
||||
end
|
||||
alias call top_down_fun
|
||||
|
||||
def end
|
||||
@next.call @buffer
|
||||
|
@ -117,9 +162,15 @@ class Functional
|
|||
end
|
||||
|
||||
class Each <Base
|
||||
def each_fun *a
|
||||
@exe.call *a
|
||||
end
|
||||
alias call each_fun
|
||||
|
||||
def end
|
||||
nil
|
||||
end
|
||||
alias :clean :end
|
||||
end
|
||||
|
||||
class P <Each
|
||||
|
@ -130,16 +181,17 @@ class Functional
|
|||
|
||||
class Inject <Base
|
||||
attr_reader :it
|
||||
alias :end :it
|
||||
|
||||
def initialize start, *a, &e
|
||||
super *a, &e
|
||||
@it = start
|
||||
end
|
||||
def call *a
|
||||
|
||||
def inject_fun *a
|
||||
@it = @exe.call @it, *a
|
||||
end
|
||||
def end
|
||||
@it
|
||||
end
|
||||
alias call inject_fun
|
||||
end
|
||||
|
||||
class To_a <Inject
|
||||
|
@ -149,25 +201,134 @@ class Functional
|
|||
end
|
||||
|
||||
class Map <Collect
|
||||
def call *a
|
||||
def map_fun *a
|
||||
@exe.call *a, &@next
|
||||
end
|
||||
alias call map_fun
|
||||
end
|
||||
|
||||
class Flatten <Base
|
||||
def flatten_fun *a
|
||||
a.each &@next.method( :call)
|
||||
end
|
||||
alias call flatten_fun
|
||||
end
|
||||
|
||||
class Reduce <Base
|
||||
def initialize iv, *a, &e
|
||||
super *a, &e
|
||||
@buf = {}
|
||||
@buf.default = iv
|
||||
def initialize iv = ::Functional::DEFAULT, *a, &exe
|
||||
super *a, &exe
|
||||
iv = Array.method :new if ::Functional::DEFAULT == iv
|
||||
@buf = if iv.kind_of?( ::Proc) || iv.kind_of?( ::Method)
|
||||
Hash.new {|h,k| h[k] = iv.call }
|
||||
else
|
||||
{}.tap {|h| h.default = iv }
|
||||
end
|
||||
end
|
||||
|
||||
def call *a
|
||||
@buf[ a[0]] = @exe.call @buf[ a[0]], *a[1..-1]
|
||||
def reduce_fun k, *a
|
||||
@buf[ k] = @exe.call k, @buf[ k], *a
|
||||
end
|
||||
alias call reduce_fun
|
||||
|
||||
def end
|
||||
@buf.each &@next.method( :call)
|
||||
@next.end
|
||||
end
|
||||
end
|
||||
|
||||
class ToHash <Base
|
||||
def initialize
|
||||
super
|
||||
@buf = {}
|
||||
end
|
||||
|
||||
def to_hash_fun k, *a
|
||||
@buf[k] = a
|
||||
end
|
||||
alias call to_hash_fun
|
||||
|
||||
def end
|
||||
@buf
|
||||
end
|
||||
|
||||
def clean
|
||||
end
|
||||
end
|
||||
|
||||
class Slice <Base
|
||||
def initialize n
|
||||
@buf, @n = [], n
|
||||
end
|
||||
|
||||
def slice_fun *a
|
||||
@buf.push a
|
||||
unless @n > @buf.size
|
||||
@next.call @buf
|
||||
@buf.clear
|
||||
end
|
||||
end
|
||||
alias call slice_fun
|
||||
|
||||
def end
|
||||
@next.call @buf
|
||||
@next.end
|
||||
end
|
||||
end
|
||||
|
||||
class Cons <Base
|
||||
def initialize n
|
||||
@buf, @n = [], n
|
||||
end
|
||||
|
||||
def cons_fun *a
|
||||
@buf.push a
|
||||
unless @n > @buf.size
|
||||
class <<self
|
||||
def call *a
|
||||
@buf.push a
|
||||
@next.call @buf
|
||||
@buf.shift
|
||||
end
|
||||
end
|
||||
@next.call @buf
|
||||
@buf.shift
|
||||
end
|
||||
end
|
||||
alias call cons_fun
|
||||
|
||||
def end
|
||||
@next.call @buf unless @n > @buf.size
|
||||
@next.end
|
||||
end
|
||||
end
|
||||
|
||||
class Pager <Base
|
||||
def initialize *opts
|
||||
@pager = IO.popen ENV['PAGER'] || 'less', 'w'
|
||||
opts.each do |opt|
|
||||
case opt.to_s
|
||||
when *%w[inspect i] then alias call call_inspect
|
||||
else raise ArgumentError, "Unknown opt: #{opt}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def call_inspect *a
|
||||
@pager.puts a.inspect
|
||||
end
|
||||
|
||||
def pager_fun *a
|
||||
@pager.puts a
|
||||
end
|
||||
alias call pager_fun
|
||||
|
||||
def clean
|
||||
@pager.close
|
||||
end
|
||||
|
||||
def end
|
||||
@buf.each {|i| @next.call *i}
|
||||
@next.end
|
||||
clean
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -190,7 +351,7 @@ class Functional
|
|||
push Map.new( &exe)
|
||||
end
|
||||
|
||||
def reduce iv, &exe
|
||||
def reduce iv = ::Functional::DEFAULT, &exe
|
||||
push Reduce.new( iv, &exe)
|
||||
end
|
||||
|
||||
|
@ -202,9 +363,10 @@ class Functional
|
|||
push Select.new( &re.method( :match))
|
||||
end
|
||||
|
||||
def delete_if &exe
|
||||
push DeleteIf.new( &exe)
|
||||
def filter &exe
|
||||
push Filter.new( &exe)
|
||||
end
|
||||
alias reject filter
|
||||
|
||||
def compact
|
||||
push Compact.new
|
||||
|
@ -218,10 +380,18 @@ class Functional
|
|||
push TopDown.new( init, &exe)
|
||||
end
|
||||
|
||||
def flatten &exe
|
||||
push Flatten.new
|
||||
end
|
||||
|
||||
def each &exe
|
||||
return self unless exe
|
||||
push Each.new
|
||||
push exe
|
||||
push Each.new( &exe)
|
||||
run
|
||||
end
|
||||
|
||||
def to_hash
|
||||
push ToHash.new
|
||||
run
|
||||
end
|
||||
|
||||
|
@ -233,37 +403,63 @@ class Functional
|
|||
def run
|
||||
@obj.send @func||:each, *@args, &@next #.method(:call)
|
||||
@next.end
|
||||
rescue Object
|
||||
@next.clean
|
||||
raise $!
|
||||
end
|
||||
|
||||
def p
|
||||
each {|*a|Kernel.p a}
|
||||
each &Kernel.method( :p)
|
||||
end
|
||||
|
||||
def pager *opts
|
||||
push Pager.new( *opts)
|
||||
run
|
||||
end
|
||||
|
||||
def sort &exe
|
||||
to_a.sort &exe
|
||||
end
|
||||
|
||||
def sort_by &exe
|
||||
to_a.sort_by &exe
|
||||
end
|
||||
|
||||
# [ _A, _B, ..., _C, ..., _D ] ==> [ [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
|
||||
|
||||
begin
|
||||
require 'tokyocabinet'
|
||||
|
||||
class Functional
|
||||
class Map <Base
|
||||
class Emit < TokyoCabinet::BDB
|
||||
alias emit putdup
|
||||
alias call emit
|
||||
end
|
||||
|
||||
def call *a
|
||||
@exe.call( *a).each &@next
|
||||
end
|
||||
end
|
||||
|
||||
def map name, &e
|
||||
push Map.new( name, &e)
|
||||
end
|
||||
|
||||
def Reduce name, &e
|
||||
push Reduce.new( name, &e)
|
||||
end
|
||||
end
|
||||
|
||||
rescue MissingSourceFile
|
||||
# TokyoCabinet not installed?
|
||||
end if false
|
||||
|
|
|
@ -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
|
Reference in New Issue