Class: Factbase::Term
Overview
Term.
This is an internal class, it is not supposed to be instantiated directly.
It is possible to use for testing directly, for example to make a term with two arguments:
require 'factbase/fact'
require 'factbase/term'
f = Factbase::Fact.new({ 'foo' => [42, 256, 'Hello, world!'] })
t = Factbase::Term.new(:lt, [:foo, 50])
assert(t.evaluate(f))
The design of this class may look ugly, since it has a large number of methods, each of which corresponds to a different type of a Term. A much better design would definitely involve many classes, one per each type of a term. It’s not done this way because of an experimental nature of the project. Most probably we should keep current design intact, since it works well and is rather simple to extend (by adding new term types). Moreover, it looks like the number of possible term types is rather limited and currently we implement most of them.
It is NOT thread-safe!
- Author
-
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
-
Copyright © 2024-2026 Yegor Bugayenko
- License
-
MIT
Instance Attribute Summary collapse
-
#op ⇒ Symbol
readonly
The operator of this term.
-
#operands ⇒ Array
readonly
The operands of this term.
Instance Method Summary collapse
-
#abstract? ⇒ Boolean
Does it have any variables (+$foo+, for example) inside?.
- #at(fact, maps, fb) ⇒ Object
-
#evaluate(fact, maps, fb) ⇒ Object
Evaluate term on a fact.
-
#initialize(operator, operands) ⇒ Term
constructor
Ctor.
-
#predict(maps, fb, params) ⇒ Array<Hash>
Try to predict which facts from the provided list should be evaluated.
-
#redress!(type, **args) ⇒ Object
Extend it with the module.
-
#simplify ⇒ Factbase::Term
Simplify it if possible.
-
#static? ⇒ Boolean
Does it have any dependencies on a fact?.
Methods inherited from TermBase
Constructor Details
#initialize(operator, operands) ⇒ Term
Ctor.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/factbase/term.rb', line 104 def initialize(operator, operands) super() @op = operator @operands = operands @terms = { unique: Factbase::Unique.new(operands), prev: Factbase::Prev.new(operands), concat: Factbase::Concat.new(operands), sprintf: Factbase::Sprintf.new(operands), matches: Factbase::Matches.new(operands), contains: Factbase::Contains.new(operands), starts_with: Factbase::StartsWith.new(operands), ends_with: Factbase::EndsWith.new(operands), traced: Factbase::Traced.new(operands), assert: Factbase::Assert.new(operands), env: Factbase::Env.new(operands), defn: Factbase::Defn.new(operands), undef: Factbase::Undef.new(operands), as: Factbase::As.new(operands), join: Factbase::Join.new(operands), exists: Factbase::Exists.new(operands), absent: Factbase::Absent.new(operands), size: Factbase::Size.new(operands), type: Factbase::Type.new(operands), nil: Factbase::Nil.new(operands), many: Factbase::Many.new(operands), one: Factbase::One.new(operands), to_string: Factbase::ToString.new(operands), to_integer: Factbase::ToInteger.new(operands), to_float: Factbase::ToFloat.new(operands), to_time: Factbase::ToTime.new(operands), sorted: Factbase::Sorted.new(operands), inverted: Factbase::Inverted.new(operands), head: Factbase::Head.new(operands), plus: Factbase::Plus.new(operands), minus: Factbase::Minus.new(operands), times: Factbase::Times.new(operands), div: Factbase::Div.new(operands), zero: Factbase::Zero.new(operands), eq: Factbase::Eq.new(operands), lt: Factbase::Lt.new(operands), lte: Factbase::Lte.new(operands), gt: Factbase::Gt.new(operands), gte: Factbase::Gte.new(operands), always: Factbase::Always.new(operands), never: Factbase::Never.new(operands), not: Factbase::Not.new(operands), or: Factbase::Or.new(operands), and: Factbase::And.new(operands), when: Factbase::When.new(operands), either: Factbase::Either.new(operands), count: Factbase::Count.new(operands), first: Factbase::First.new(operands), nth: Factbase::Nth.new(operands), sum: Factbase::Sum.new(operands), agg: Factbase::Agg.new(operands), empty: Factbase::Empty.new(operands), min: Factbase::Min.new(operands), max: Factbase::Max.new(operands) } end |
Instance Attribute Details
#op ⇒ Symbol (readonly)
The operator of this term
95 96 97 |
# File 'lib/factbase/term.rb', line 95 def op @op end |
#operands ⇒ Array (readonly)
The operands of this term
99 100 101 |
# File 'lib/factbase/term.rb', line 99 def operands @operands end |
Instance Method Details
#abstract? ⇒ Boolean
Does it have any variables (+$foo+, for example) inside?
254 255 256 257 258 259 260 |
# File 'lib/factbase/term.rb', line 254 def abstract? @operands.each do |o| return true if o.is_a?(Factbase::Term) && o.abstract? return true if o.is_a?(Symbol) && o.to_s.start_with?('$') end false end |
#at(fact, maps, fb) ⇒ Object
262 263 264 265 266 267 268 269 270 271 |
# File 'lib/factbase/term.rb', line 262 def at(fact, maps, fb) assert_args(2) i = _values(0, fact, maps, fb) raise "Too many values (#{i.size}) at first position, one expected" unless i.size == 1 i = i[0] return nil if i.nil? v = _values(1, fact, maps, fb) return nil if v.nil? v[i] end |
#evaluate(fact, maps, fb) ⇒ Object
Evaluate term on a fact
209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/factbase/term.rb', line 209 def evaluate(fact, maps, fb) if @terms.key?(@op) @terms[@op].evaluate(fact, maps, fb) else send(@op, fact, maps, fb) end rescue NoMethodError => e raise "Probably the term '#{@op}' is not defined at #{self}: #{e.}" rescue StandardError => e raise "#{e..inspect} at #{self} at #{e.backtrace[0]}" end |
#predict(maps, fb, params) ⇒ Array<Hash>
Try to predict which facts from the provided list should be evaluated. If no prediction can be made, the same list is returned.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/factbase/term.rb', line 188 def predict(maps, fb, params) m = :"#{@op}_predict" if @terms.key?(@op) t = @terms[@op] if t.respond_to?(:predict) t.predict(maps, fb, params) else maps end elsif respond_to?(m) send(m, maps, fb, params) else maps end end |
#redress!(type, **args) ⇒ Object
Extend it with the module.
169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/factbase/term.rb', line 169 def redress!(type, **args) extend type args.each { |k, v| send(:instance_variable_set, :"@#{k}", v) } @operands.map do |op| if op.is_a?(Factbase::Term) op.redress!(type, **args) else op end end end |
#simplify ⇒ Factbase::Term
Simplify it if possible.
223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/factbase/term.rb', line 223 def simplify if @terms.key?(@op) && @terms[@op].respond_to?(:simplify) @terms[@op].simplify else m = "#{@op}_simplify" if respond_to?(m, true) send(m) else self end end end |
#static? ⇒ Boolean
Does it have any dependencies on a fact?
If a term is static, it will return the same value for evaluate, no matter what is the fact given.
242 243 244 245 246 247 248 249 |
# File 'lib/factbase/term.rb', line 242 def static? return true if @op == :agg @operands.each do |o| return false if o.is_a?(Factbase::Term) && !o.static? return false if o.is_a?(Symbol) && !o.to_s.start_with?('$') end true end |