class Object
Public Instance Methods
Public: Compose a series of functions.
f - Any number of Procs, Methods, or Symbols which should be composed. If
functions f, g, and h are composed in that order, the result will be f . g . h -> f(g(h(...)))
Returns a composed Proc
.
# File lib/funtools/composition.rb, line 33 def compose(*f) f.map do |g| g.is_a?(Array) ? ->(n) { g.first.to_proc[n,*g.drop(1)] } : g.to_proc end.reduce(&:*) end
Public: Define a method in the current scope which will execute a given block recursively until a fixpoint is reached.
sym - Symbol defining the name of the method to be created. block - Block containing the logic for the function to be created.
Returns nothing.
# File lib/funtools/recursion.rb, line 13 def defix(sym, &block) message = :define_method if respond_to?(:define_method, true) message ||= :define_singleton_method self.send(message, sym) do |*n| last = nil [1].cycle.reduce(n) do |c, e| break c if last == c last = c instance_exec(c, &block) end end end
Public: Define a method in the current scope which allow pattern matching function declaration to be used.
sym - Symbol defining the name of the method to be created. block - Block containing the logic for the function to be created.
Returns nothing.
# File lib/funtools/pattern-matching.rb, line 9 def defpattern(sym) match = ->(a, b) do if([a,b].map { |o| o.is_a?(Enumerable) && a.class == b.class }.all?) raise ArgumentError unless a.length == b.length zipped = a.is_a?(Hash) ? a.sort.zip(b.sort) : a.zip(b) zipped.reduce(true) do |c, e| c && match.(*e) end else a.nil? || a == b end end old_method = self.class.method(sym) if self.class.method_defined?(:sym) patterns = [] self.class.send(:define_method, sym) do |*l, &b| patterns << ->(*n) do ->(*m) do if m.length == n.length e = m.zip(n) raise NoMatch if e.reject { |e| match.(*e) }.any? instance_exec(*n, &b) end end.(*l) end end yield if old_method self.class.send(:define_method, sym, &old_method) else self.class.send(:remove_method, sym) end message = :define_method if respond_to?(:define_method, true) message ||= :define_singleton_method self.send(message, sym) do |*args| patterns.each do |pattern| begin return instance_exec(*args, &pattern) rescue NoMatch end end instance_exec { raise NoMatch } end end
Public: Define a method in the current scope which will execute a given block in such a manner that recursive calls are handled properly so long as the call constitutes tail recursion.
sym - Symbol defining the name of the method to be created. block - Block containing the logic for the function to be created.
Returns nothing.
# File lib/funtools/recursion.rb, line 34 def deftail(sym, &block) message = :define_method if respond_to?(:define_method, true) message ||= :define_singleton_method self.send(message, sym) do |*n| instance_exec { self.dup }.instance_exec do class << self self end.class_eval do define_method(sym) { |*a| Funtools::RecurseArgs.new(a) } end loop do n = instance_exec(*n, &block) return n unless n.is_a?(Funtools::RecurseArgs) end end end end
Public: Construct a list of nested Cons
cells.
first - Any Object
to be the leftmost element of the list. second - Any Object
to be the second element of the list (default: nil). rest - Any number of Objects to serve as elements in the list.
Returns a list (nested Cons
cells).
# File lib/funtools/cons.rb, line 19 def list(first, second = nil, *rest) set = (rest.empty? && second.nil?) ? [] : rest + [nil] ([first, second] + set).reverse.reduce { |c, e| Cons.new(e, c) } end
Public: Mimic the -> macro in Clojure.
data - Any data to be passed to the composed function. f - Any number of Procs, Methods, or Symbols which should be composed.
If functions f, g, and h are composed in that order, the result will be f . g . h -> f(g(h(...)))
Returns the result of the composed functions, called with data as the argument.
# File lib/funtools/composition.rb, line 48 def pl(data, *f) compose(*f.reverse)[*[data].flatten] end