Class: Alf::Aggregator

Inherits:
Object
  • Object
show all
Extended by:
Support::Registry
Defined in:
lib/alf/aggregator.rb,
lib/alf/aggregator/avg.rb,
lib/alf/aggregator/sum.rb,
lib/alf/aggregator/min.rb,
lib/alf/aggregator/max.rb,
lib/alf/aggregator/count.rb,
lib/alf/aggregator/concat.rb,
lib/alf/aggregator/stddev.rb,
lib/alf/aggregator/collect.rb,
lib/alf/aggregator/variance.rb

Overview

Aggregation operator.

This class provides a basis for implementing aggregation operators. It should always be used as a superclass for such implementations.

Aggregation operators are made available through factory methods on the Aggregator class itself:

Aggregator.count
Aggregator.sum{ qty }

The coercion method should always be used for building aggregators from lispy source code:

Aggregator.coerce("count")
Aggregator.coerce("sum{ qty }")

Once built, aggregators can be used either in black-box or white-box modes.

relation = ...
agg = Aggregator.sum{ qty }

# Black box mode:
result = agg.aggregate(relation)

# White box mode:
memo = agg.least
relation.each do |tuple|
  memo = agg.happens(memo, tuple)
end
result = agg.finalize(memo)

Direct Known Subclasses

Avg, Collect, Concat, Count, Max, Min, Sum, Variance

Defined Under Namespace

Classes: Avg, Collect, Concat, Count, Max, Min, Stddev, Sum, Variance

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Registry

each, listen, listeners, register, registered

Constructor Details

#initialize(*args, &block) ⇒ Aggregator

Creates an Aggregator instance.

Example:

Aggregator.new{ size * price }

101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/alf/aggregator.rb', line 101

def initialize(*args, &block)
  @options = default_options
  args.push(block) if block
  args.each do |arg|
    case arg
    when Symbol, Proc then @functor = Support.coerce(arg, TupleExpression)
    when Hash         then @options = @options.merge(arg)
    else
      raise ArgumentError, "Unexpected `#{arg}`"
    end
  end
end

Instance Attribute Details

#functorTupleExpression (readonly)

Returns the underlying functor

Returns:

  • (TupleExpression)

    the underlying functor


90
91
92
# File 'lib/alf/aggregator.rb', line 90

def functor
  @functor
end

#optionsHash (readonly)

Returns Aggregation options

Returns:

  • (Hash)

    Aggregation options


87
88
89
# File 'lib/alf/aggregator.rb', line 87

def options
  @options
end

#sourceString

Returns source code of the aggregator, if any

Returns:

  • (String)

    source code of the aggregator, if any


93
94
95
# File 'lib/alf/aggregator.rb', line 93

def source
  @source
end

Class Method Details

.coerce(arg) ⇒ Aggregator

Coerces `arg` to an Aggregator

Implemented coercions are:

  • Aggregator -> self

  • String -> through factory methods on self

Parameters:

  • arg (Object)

    a value to coerce to an aggregator

Returns:

Raises:

  • (ArgumentError)

    if the coercion fails


76
77
78
79
80
81
82
83
# File 'lib/alf/aggregator.rb', line 76

def coerce(arg)
  case arg
  when Aggregator
    arg
  else
    raise ArgumentError, "Invalid arg `arg` for Aggregator()"
  end
end

.inherited(clazz) ⇒ Object

Automatically installs factory methods for inherited classes.

Parameters:

  • clazz (Class)

    a class that extends Aggregator


63
64
65
# File 'lib/alf/aggregator.rb', line 63

def inherited(clazz)
  register(clazz, Aggregator)
end

Instance Method Details

#==(other) ⇒ Boolean

Checks equality with another aggregator

Parameters:

Returns:

  • (Boolean)

    true is self and other are equal, false otherwise


199
200
201
202
203
204
# File 'lib/alf/aggregator.rb', line 199

def ==(other)
  return false unless other.is_a?(Aggregator)
  has_source_code! == other.has_source_code!
rescue NotImplementedError
  super
end

#aggregate(enum) ⇒ Object

Aggregates over an enumeration of tuples.

Parameters:

  • an (Enumerable<Tuple>)

    enumerable of tuples

Returns:

  • (Object)

    the computed aggregation value


164
165
166
167
# File 'lib/alf/aggregator.rb', line 164

def aggregate(enum)
  scope = Support::TupleScope.new
  finalize(enum.inject(least){|m,t| happens(m, scope.__set_tuple(t))})
end

#default_optionsHash

Returns the default options to use

Returns:

  • (Hash)

    the default aggregation options


117
118
119
# File 'lib/alf/aggregator.rb', line 117

def default_options
  {}
end

#finalize(memo) ⇒ Object

This method finalizes an aggregation.

Argument memo is either least or the result of aggregating through happens. The default implementation simply returns memo. The method is intended to be overriden for complex aggregations that need statefull information such as `avg`.

Parameters:

  • memo (Object)

    the current aggregation value

Returns:

  • (Object)

    the aggregation value, as finalized


156
157
158
# File 'lib/alf/aggregator.rb', line 156

def finalize(memo)
  memo
end

#happens(memo, scope) ⇒ Object

This method is called on each aggregated tuple and must return an updated memo value. It can be seen as the block typically given to Enumerable.inject.

The default implementation collects the pre-value on the tuple and delegates to _happens.

Parameters:

  • memo (Object)

    the current aggregation value

  • a (Support::TupleScope)

    tuple scope bound to the current tuple

Returns:

  • (Object)

    updated memo value


142
143
144
145
# File 'lib/alf/aggregator.rb', line 142

def happens(memo, scope)
  raise unless Support::TupleScope===scope
  _happens(memo, @functor.evaluate(scope))
end

#has_source_code!String

Asserts that this aggregator knows its source code or raises a NotImplementedError.

Returns:

  • (String)

    the source code when known


178
179
180
181
182
183
184
# File 'lib/alf/aggregator.rb', line 178

def has_source_code!
  if source.nil?
    raise NotImplementedError, "No known source code for this aggregator"
  else
    source
  end
end

#infer_typeObject

Infers the resulting type from expression source code


170
171
172
# File 'lib/alf/aggregator.rb', line 170

def infer_type
  Object
end

#leastObject

Returns the least value, which is the one to use on an empty set.

This method is intended to be overriden by subclasses; default implementation returns nil.

Returns:

  • (Object)

    the least value for this aggregator


128
129
130
# File 'lib/alf/aggregator.rb', line 128

def least
  nil
end

#to_lispyString

Returns a lispy expression

Returns:

  • (String)

    a lispy expression for this aggregator


189
190
191
192
193
# File 'lib/alf/aggregator.rb', line 189

def to_lispy
  has_source_code!
rescue NotImplementedError
  "#{Support.rubycase_name(self.class)}{|t| [code unavailable] }"
end