Class: Alf::Types::Ordering

Inherits:
Object
  • Object
show all
Defined in:
lib/alf/types/ordering.rb

Overview

Defines an ordering on tuple attributes

Constant Summary collapse

ArrayOfArray =
->(x){
  x.is_a?(Array) && x.all?{|y| y.is_a?(Array)}
}
ArrayWithDirections =
->(x){
  x.each_with_index.all?{|elm,i| (i%2==0) || elm.to_s =~ /^asc|desc$/ }
}
EMPTY =
Ordering.new([])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ordering = []) ⇒ Ordering

Creates an ordering instance

Parameters:

  • ordering (Array) (defaults to: [])

    the underlying ordering info


40
41
42
43
# File 'lib/alf/types/ordering.rb', line 40

def initialize(ordering = [])
  super
  @sorter = lambda{|t1,t2| compare(t1, t2) }
end

Instance Attribute Details

#sorterProc (readonly)

Returns a Proc object that sorts tuples according to this ordering.

Returns:

  • (Proc)

    a Proc object that sorts tuples according to this ordering.


35
36
37
# File 'lib/alf/types/ordering.rb', line 35

def sorter
  @sorter
end

Class Method Details

.new(array) ⇒ Object


9
10
11
# File 'lib/alf/types/ordering.rb', line 9

def self.new(array)
  super(array.map{|(x,y)| [Selector.coerce(x), y.to_sym] })
end

Instance Method Details

#<=(other) ⇒ Object

Check if this ordering subsumes another one.


154
155
156
157
# File 'lib/alf/types/ordering.rb', line 154

def <=(other)
  size = reused_instance.size
  reused_instance == other.to_a[0...size]
end

#[](attribute) ⇒ Symbol

Returns the directions associated with an attribute name, nil if no such attribute.

Parameters:

  • an (Symbol)

    attribute name.

Returns:

  • (Symbol)

    the associated direction or nil


52
53
54
55
56
# File 'lib/alf/types/ordering.rb', line 52

def [](attribute)
  attribute = Selector.coerce(attribute)
  pair = reused_instance.find{|p| p.first == attribute }
  pair && pair.last
end

#compare(t1, t2) ⇒ -1, 0 or 1

Compares two tuples according to this ordering.

Both t1 and t2 should have all attributes used by this ordering. Strange results may appear if it's not the case.

Parameters:

  • t1 (Tuple)

    a tuple

  • t2 (Tuple)

    another tuple

Returns:

  • (-1, 0 or 1)

    according to the classical ruby semantics of `(t1 <=> t2)`


67
68
69
70
71
72
73
74
75
76
# File 'lib/alf/types/ordering.rb', line 67

def compare(t1, t2)
  extract = proc{|t,x| Array(x).inject(t){|m,a| m[a]} }
  reused_instance.each do |atr, dir|
    x, y = extract[t1,atr], extract[t2,atr]
    comp = Support.robust_compare(x, y)
    comp *= -1 if dir == :desc
    return comp unless comp == 0
  end
  return 0
end

#dive(attr) ⇒ Ordering

Dives into a relation/tuple valued attribute `attr`.

Returns:

  • (Ordering)

    the sub-ordering to use for `attr`


139
140
141
142
143
144
# File 'lib/alf/types/ordering.rb', line 139

def dive(attr)
  attrs = reused_instance
        .map   {|(s,d)| [s.dive(attr), d] }
        .reject{|(s,d)| s.nil? }
  Ordering.new(attrs)
end

#merge(other, &bl) ⇒ Ordering Also known as: +

Computes the union of this ordering with another one.

The union is simply defined by extension of self with other's attributes and directions. Duplicates are automatically removed.

When a conflict arises (same attribute but not same direction), the block is yield with the attribute name, then `self`'s and `other`'s directions as arguments. The block is expected to return the direction to use to the attribute. A default block is provided that always favors the direction found in `other`.

Parameters:

  • other (Ordering)

    another Ordering (coercions will apply)

Returns:


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/alf/types/ordering.rb', line 91

def merge(other, &bl)
  bl ||= lambda{|attr,d1,d2| d2 }
  other = Ordering.coerce(other)
  attributes = self.selectors | other.selectors
  directions = attributes.to_a.map{|a|
    left, right = self[a], other[a]
    direction = if left.nil? or right.nil?
                  left || right
                elsif left == right
                  left
                else
                  bl.call(a, left, right)
                end
    [a, direction]
  }
  Ordering.new(directions)
end

#reverseOrdering

Reverse this ordering.

Returns:

  • (Ordering)

    another ordering where the direction of every attribute has been flipped (asc <-> desc)


114
115
116
117
118
# File 'lib/alf/types/ordering.rb', line 114

def reverse
  Ordering.new(reused_instance.map{|attr,dir|
    [ attr, dir == :asc ? :desc : :asc ]
  })
end

#selectorsArray[Selector]

Returns the list of selectors

Returns:

  • (Array[Selector])

    the list of selectors


149
150
151
# File 'lib/alf/types/ordering.rb', line 149

def selectors
  reused_instance.map(&:first)
end

#to_attr_listAttrList

Converts to an attribute list.

Returns:

  • (AttrList)

    a list of attribute names that participate to the ordering


163
164
165
# File 'lib/alf/types/ordering.rb', line 163

def to_attr_list
  AttrList.new(selectors.map{|x| Array(x).first })
end

#to_lispyString

Returns a lispy expression.

Returns:

  • (String)

    a lispy expression for this ordering


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

def to_lispy
  Support.to_ruby_literal(to_a.map{|(x,d)| [x.outcoerce, d]})
end

#to_ruby_literalString Also known as: to_s, inspect

Returns a ruby literal for this ordering.

Returns:

  • (String)

    a literal s.t. `eval(self.to_ruby_literal) == self`


177
178
179
# File 'lib/alf/types/ordering.rb', line 177

def to_ruby_literal
  "Alf::Ordering[#{Support.to_ruby_literal(reused_instance)}]"
end

#total(keys, &bl) ⇒ Ordering

Returns a total ordering given some key definitions.

Returns:


123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/alf/types/ordering.rb', line 123

def total(keys, &bl)
  list = to_attr_list
  if k = keys.to_a.find{|k| k.to_attr_list.subset_of?(list) }
    self
  elsif k = keys.first
    merge(k.to_ordering){|k,d1,d2| d1 }
  elsif bl && (key = bl.call.to_attr_list)
    merge(key.to_ordering){|k,d1,d2| d1 }
  else
    raise NotSupportedError, "Unable to find a total order (no key available)"
  end
end