class Porolog::Instantiation

A Porolog::Instantiation implements an instantiation of a Variable of a Goal. An Instantiation joins two expressions. At least one expression must have a variable. An index may be provided to index into the value of either expression, or both may have an index. Thus, an element of each list could be instantiated without reference to the other elements:

x = [a,b,c,d,e]
         |
y =   [p,q,r,s]

@author Luis Esteban

@!attribute variable1

@return [Variable,Object] one end of the instantiation.

@!attribute variable2

@return [Variable,Object] the other end of the instantiation.

@!attribute index1

@return [Object,nil] the index into the value of variable1.

@!attribute index2

@return [Object,nil] the index into the value of variable2.

Attributes

index1[RW]
index2[RW]
variable1[RW]
variable2[RW]

Public Class Methods

instantiations() click to toggle source

@return [Hash{Array => Porolog::Instantiation}] all Instantiations

# File lib/porolog/instantiation.rb, line 50
def self.instantiations
  @@instantiations
end
new(variable1, index1, variable2, index2) click to toggle source

Finds or creates a new instantiation. At least one of the variables must be a Variable. @param variable1 [Porolog::Variable,Porolog::Value,Object] one end of the instantiation to be made. @param variable2 [Porolog::Variable,Porolog::Value,Object] the other end of the instantiation to be made. @param index1 [Integer,Symbol,nil] index into the value of variable1. @param index2 [Integer,Symbol,nil] index into the value of variable2. @return [Porolog::Instantiation] the found or created Instantiation.

Calls superclass method
# File lib/porolog/instantiation.rb, line 60
def self.new(variable1, index1, variable2, index2)
  raise NoVariableError, "Cannot instantiate non-variables: #{variable1.inspect} and #{variable2.inspect}" unless variable1.is_a?(Variable) || variable2.is_a?(Variable)
  
  variable1 = Value.new variable1, variable2.goal unless variable1.is_a?(Variable) || variable1.is_a?(Value)
  variable2 = Value.new variable2, variable1.goal unless variable2.is_a?(Variable) || variable2.is_a?(Value)
  
  instantiation = \
    @@instantiations[[variable1, index1, variable2, index2]] ||
    @@instantiations[[variable2, index2, variable1, index1]] ||
    super
  
  instantiation
end
new(variable1, index1, variable2, index2) click to toggle source

Initializes and registers a new Instantiation. At least one of the variables must be a Variable. @param variable1 [Porolog::Variable,Porolog::Value,Object] one end of the instantiation to be made. @param variable2 [Porolog::Variable,Porolog::Value,Object] the other end of the instantiation to be made. @param index1 [Integer,Symbol,nil] index into the value of variable1. @param index2 [Integer,Symbol,nil] index into the value of variable2. @return [Porolog::Instantiation] the found or created Instantiation.

# File lib/porolog/instantiation.rb, line 80
def initialize(variable1, index1, variable2, index2)
  @variable1 = variable1
  @variable2 = variable2
  @index1    = index1
  @index2    = index2
  
  @variable1.instantiations << self
  @variable2.instantiations << self
  
  @@instantiations[signature] = self
end
reset() click to toggle source

Clears all instantiations. @return [Boolean] success

# File lib/porolog/instantiation.rb, line 42
def self.reset
  @@instantiations = {}
  true
end

Public Instance Methods

belongs_to?(goal) click to toggle source

@param goal [Porolog::Goal] the provided Goal. @return [Boolean] whether the Instantiation attaches to a variable in the provided Goal.

# File lib/porolog/instantiation.rb, line 337
def belongs_to?(goal)
  [
    @variable1.goal,
    @variable2.goal,
  ].include?(goal)
end
deleted?() click to toggle source

@return [Boolean] whether the Instantiation has been deleted (memoized).

# File lib/porolog/instantiation.rb, line 330
def deleted?
  @deleted ||= @variable1.goal.deleted? || @variable2.goal.deleted?
  @deleted
end
goals() click to toggle source

@return [Array<Porolog::Goal>] the Goals of the Variables of the Instantiation.

# File lib/porolog/instantiation.rb, line 128
def goals
  [@variable1.goal,@variable2.goal]
end
inspect(indent = 0) click to toggle source

@return [String] pretty representation.

# File lib/porolog/instantiation.rb, line 99
def inspect(indent = 0)
  index1 = @index1 ? "[#{@index1.inspect}]" : ''
  index2 = @index2 ? "[#{@index2.inspect}]" : ''
  
  "#{'  ' * indent}#{@variable1.inspect}#{index1} = #{@variable2.inspect}#{index2}"
end
other_goal_to(variable) click to toggle source

Returns the Goal of the other Variable to the one provided. @param variable [Porolog::Variable] the provided Variable. @return [Porolog::Goal,nil] the Goal of the other Variable.

# File lib/porolog/instantiation.rb, line 120
def other_goal_to(variable)
  return nil unless variables.include?(variable)
  
  other_variable = (variables - [variable]).first
  other_variable&.goal
end
remove() click to toggle source

Removes itself from its variables' Instantiations and unregisters itself. @return [Boolean] success

# File lib/porolog/instantiation.rb, line 108
def remove
  @variable1.instantiations.delete(self)  if @variable1.is_a?(Variable) || @variable1.is_a?(Value)
  @variable2.instantiations.delete(self)  if @variable2.is_a?(Variable) || @variable2.is_a?(Value)
  
  @deleted = true
  @@instantiations.delete(signature)
  @deleted
end
signature() click to toggle source

@return [Array] the signature of the Instantiation, which aids in avoiding duplication instantiations.

# File lib/porolog/instantiation.rb, line 93
def signature
  @signature ||= [@variable1, @index1, @variable2, @index2].freeze
  @signature
end
value_at_index(value, index) click to toggle source

@param value [Object] the known value. @param index [Integer,Symbol,Array] the known index. @return [Array] an Array where the known value is at the known index.

# File lib/porolog/instantiation.rb, line 280
def value_at_index(value, index)
  value && case index
    when Integer
      result        = []
      result[index] = value
      result       << UNKNOWN_TAIL
      result
    
    when Symbol
      case index
        when :flathead
          [*value, UNKNOWN_TAIL]
        when :head
          [value, UNKNOWN_TAIL]
        when :tail
          [nil, *value]
        when :flattail
          value
        else
          raise UnhandledIndexError, "Unhandled index: #{index.inspect} for #{value.inspect}"
      end
    
    when Array
      if index.empty?
        [nil, *value]
      else
        [value, UNKNOWN_TAIL]
      end
    
    else
      if index
        raise UnhandledIndexError, "Unhandled index: #{index.inspect} for #{value.inspect}"
      else
        value
      end
  end
end
value_indexed(value, index, visited = []) click to toggle source

@param value [Object] the provided value. @param index [Integer,Symbol,Array<Integer>] the index. @param visited [Array] prevents infinite recursion. @return [Object] the indexed value of the provided value.

# File lib/porolog/instantiation.rb, line 152
def value_indexed(value, index, visited = [])
  return nil unless value
  return nil if value.type == :variable
  return nil if value == [] && index == :tail
  
  visit = [value, index]
  return nil if visited.include?(visit)
  visited = visited + [visit]
  
  case index
    when Integer
      if value.respond_to?(:last) && value.last == UNKNOWN_TAIL
        if index >= value.length - 1
          UNKNOWN_TAIL
        else
          value[index]
        end
      elsif value.is_a?(Numeric)
        value
      else
        value[index]
      end
    
    when Symbol
      value_value = value.value(visited)
      if index == :flathead
        # value_value = [..., 4, 5, 6]
        if value_value.first == UNKNOWN_TAIL
          UNKNOWN_ARRAY
        else
          nil
        end
      elsif index == :flattail
        # value_value = [1, 2, 3, ...]
        if value_value.first == UNKNOWN_TAIL
          nil
        elsif value_value.last == UNKNOWN_TAIL
          UNKNOWN_ARRAY
        end
      elsif value_value.respond_to?(index)
        value_value.send(index)
      else
        value
      end
    
    when Array
      if index.empty?
        value[1..-1]
      else
        value[0...index.first]
      end
    
    else
      if index
        raise UnhandledIndexError, "Unhandled index: #{index.inspect} of #{value.inspect}"
      else
        value
      end
  end
end
values(visited = []) click to toggle source

@param visited [Array] prevents infinite recursion. @return [Array] the values of the Variables of the Instantiation.

# File lib/porolog/instantiation.rb, line 139
def values(visited = [])
  return [] if visited.include?(self)
  
  values_for_variable1 = values_for(@variable1, visited)
  values_for_variable2 = values_for(@variable2, visited)
  
  (values_for_variable1 + values_for_variable2).uniq
end
values_for(variable, visited = []) click to toggle source

@param variable [Porolog::Variable,Porolog::Value] the specified variable (or end of the Instantiation). @param visited [Array] prevents infinite recursion. @return [Array<Object>] the values for the specified variable by finding the values of the other variable (i.e. the other end) and applying any indexes.

# File lib/porolog/instantiation.rb, line 216
def values_for(variable, visited = [])
  return [] if visited.include?(self)
  visited = visited + [self]
  
  if variable == @variable1
    if @index1 == :flathead
      flathead = value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1).value.value
      if flathead
        return [[*flathead]]
      else
        return []
      end
    end
    if @index1 == :flattail
      flattail = value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1).value.value
      if flattail
        return [[UNKNOWN_TAIL, *flattail]]
      else
        return []
      end
    end
    if @index1
      [value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1)]
    else
      if @variable2.is_a?(Variable)
        [value_indexed(@variable2.value(visited), @index2, visited)]
      else
        [value_indexed(@variable2, @index2, visited)]
      end
    end
  elsif variable == @variable2
    if @index2 == :flathead
      flathead = value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2).value.value
      if flathead
        return [[*flathead]]
      else
        return []
      end
    end
    if @index2 == :flattail
      flattail = value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2).value.value
      if flattail
        return [[UNKNOWN_TAIL, *flattail]]
      else
        return []
      end
    end
    if @index2
      [value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2)]
    else
      if @variable1.is_a?(Variable)
        [value_indexed(@variable1.value(visited), @index1, visited)]
      else
        [value_indexed(@variable1, @index1, visited)]
      end
    end
  else
    []
  end.compact
end
variables() click to toggle source

@return [Array<Porolog::Variable,Object>] the Variables of the Instantiation.

# File lib/porolog/instantiation.rb, line 133
def variables
  [@variable1,@variable2]
end
without_index_on?(variable) click to toggle source

@param variable [Porolog::Variable,Porolog::Value] the specified variable. @return [Boolean] whether the specified variable is not indexed.

# File lib/porolog/instantiation.rb, line 320
def without_index_on?(variable)
  [
    [@variable1,@index1],
    [@variable2,@index2],
  ].any?{|pair|
    pair.first == variable && pair.last.nil?
  }
end