Compare commits

...

27 Commits

Author SHA1 Message Date
Denis Knauf ede467e934 „README.md“ ändern 2021-12-03 18:40:08 +01:00
Denis Knauf 145129641d v0.1.5 2011-12-16 12:45:52 +01:00
Denis Knauf 99a268a89d more tests 2011-12-16 12:45:22 +01:00
Denis Knauf c8edcddcf6 tests added 2011-12-16 11:42:45 +01:00
Denis Knauf 53033b8cdd bugfixes; tap. v0.1.4 2011-12-16 11:22:48 +01:00
Denis Knauf 29e7a0e4e2 ignore pkg-dir 2011-11-24 11:50:08 +01:00
Denis Knauf 77d2cfb488 .gitignore created 2011-11-24 11:48:24 +01:00
Denis Knauf 11128ea131 README.md: methodphitamine & makros. VERSION 0.1.3 2011-11-24 11:47:03 +01:00
Denis Knauf ed649d3801 renamed call-functions. every call is an alias for a meaningful function-name. useful for debugging. 2011-11-24 11:35:08 +01:00
Denis Knauf 70e8c7ea29 [].to_fun.reduce( []) # => Ups. [] will not be duped, so every key will have the same value.
Argument for #reduce removed and everytime it will be an Array.
2011-11-21 11:38:06 +01:00
Denis Knauf a97c4ed1f9 oops. #reduce is better than #inject 2011-02-13 17:38:13 +01:00
Denis Knauf 68ab7003d5 VERSION=0.1.2 2011-02-13 16:53:58 +01:00
Denis Knauf abb9954a33 added: tap. delete_if -> filter (delete_if manipulates self, but Functional never, so filter is better). reduce |-> inject (it is like Array#inject) 2011-02-13 16:53:14 +01:00
Denis Knauf 2b7dd9fb58 reduce fix 2010-11-30 14:45:06 +01:00
Denis Knauf 362a987544 save, reduce, cons, slice, with_index added 2010-11-30 14:36:38 +01:00
Denis Knauf 275183d346 more example in README.md... 2 spaces replaced 2010-06-23 17:01:43 +02:00
Denis Knauf a5b273033b more example in README.md 2010-06-23 17:01:01 +02:00
Denis Knauf 95a3027926 flatten added 2010-06-23 16:40:01 +02:00
Denis Knauf 28dee69c07 Version 0.1.1 2010-05-25 19:04:03 +02:00
Denis Knauf 67a2d116fe MapReduce implementiert 2010-05-25 19:02:49 +02:00
Denis Knauf 76d141c101 NotRegexp -> NegRegexp. #map implemented. very simple. #reduce: not yet. 2010-05-23 22:20:53 +02:00
Denis Knauf aaa7f106c8 MapReduce: first code - needs tokyocabinet 2010-05-20 21:28:39 +02:00
Denis Knauf 63b6ac53b1 Version 0.1.0: Vollstaendig neugeschrieben. Klassen statt Lambdas. Erweiterbar 2010-05-20 17:07:23 +02:00
Denis Knauf 8e883065ca Version 0.0.2 2010-05-20 14:48:42 +02:00
Denis Knauf bf67c1752f map&reduce: first step 2010-04-20 18:54:35 +02:00
Denis Knauf 162dbb020c eval -> many lambdas. #map added 2010-04-20 16:50:59 +02:00
Denis Knauf ad6f52e7f2 no args on stack 2010-04-19 23:01:42 +02:00
5 changed files with 562 additions and 27 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
functional.gemspec
pkg

View File

@ -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_?
=================

View File

@ -1 +1 @@
0.0.1
0.1.5

View File

@ -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 <Base
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 select_fun *a
@next.call *a if @exe.call *a
end
alias call select_fun
end
class Filter <Base
def filter_fun *a
@next.call *a unless @exe.call *a
end
alias call filter_fun
end
class Compact <Base
def compact_fun *a
@next.call *a unless a.empty? || [nil] == a
end
alias call compact_fun
end
class BottomUp <Base
def initialize start, *a, &e
@next.call *a, &e
@buffer, @start = nil, start
end
def bottom_up_fun a
if @exe.call a
@next.call @buffer+a
@buffer = @start.dup
else
@buffer += a
end
end
alias call bottom_up_fun
def end
@next.call @buffer
@next.end
end
end
class TopDown <Base
def initialize start, *a, &e
@next.call *a, &e
@buffer, @start = nil, start
end
def top_down_fun a
if @exe.call a
@next.call @buffer
@buffer = @start.dup+a
else
@buffer += a
end
end
alias call top_down_fun
def end
@next.call @buffer
@next.end
end
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
def initialize *a
super *a, &Kernel.method( :p)
end
end
class Inject <Base
attr_reader :it
alias :end :it
def initialize start, *a, &e
super *a, &e
@it = start
end
def inject_fun *a
@it = @exe.call @it, *a
end
alias call inject_fun
end
class To_a <Inject
def initialize *a, &e
super [], *a, &e
end
end
class Map <Collect
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 = ::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 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
clean
nil
end
end
attr_accessor :next, :stack, :obj, :func, :args
def initialize obj = nil, func = nil, *args
@next, @stack, @obj, @func, @args = self, self, obj, func, args
end
def push a
@stack = @stack.next = a
self
end
def initialize obj = nil, func = nil, *args
@stack, @obj, @func, @args = [], obj, func, args
end
def collect &exe
push_method "value=%s(value)", &exe
push Collect.new( &exe)
end
# map/reduce?
def map &exe
raise "Reserved for MapReduce."
push Map.new( &exe)
end
# map/reduce?
def reduce &exe
raise "Reserved for MapReduce."
def reduce iv = ::Functional::DEFAULT, &exe
push Reduce.new( iv, &exe)
end
def select &exe
push_method "%s(value)||next", &exe
push Select.new( &exe)
end
def delete_if &exe
push_method "%s(value)&&next", &exe
def grep re
push Select.new( &re.method( :match))
end
def filter &exe
push Filter.new( &exe)
end
alias reject filter
def compact
push Compact.new
end
def updown init, &exe
push UpDown.new( init, &exe)
end
def topdown init, &exe
push TopDown.new( init, &exe)
end
def flatten &exe
push Flatten.new
end
def each &exe
return self unless exe
@obj.send @func || :each, *@args, &eval( "lambda{|value|#{@stack.join( ";")};exe.call(value)}")
push Each.new( &exe)
run
end
def to_hash
push ToHash.new
run
end
def join deli
push Inject.new('') {|i,j|i+deli+j}
run
end
def run
@obj.send @func||:each, *@args, &@next #.method(:call)
@next.end
rescue Object
@next.clean
raise $!
end
def p
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

29
test/functional_test.rb Normal file
View File

@ -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