Skip to content

Instantly share code, notes, and snippets.

@coldnebo
Created October 12, 2018 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coldnebo/0d72096caae07f36a665162eaabfb118 to your computer and use it in GitHub Desktop.
Save coldnebo/0d72096caae07f36a665162eaabfb118 to your computer and use it in GitHub Desktop.
playing around with recomposing predicates.
require 'csv'
class BooleanFunction
attr_reader :inputs, :input_values, :output, :output_values
BOOLEAN_SET = [true, false]
def initialize( inputs:, output: nil )
raise ArgumentError, "inputs must be an Enumerable of symbols" if (!inputs.kind_of?(Enumerable) || !inputs.map{|e| e.is_a?(Symbol) }.reduce(:&))
@output = output
@output = :output if output.nil?
@inputs = inputs
@input_values = BOOLEAN_SET.repeated_permutation(@inputs.count).to_a
@output_values = Array.new(@input_values.count)
nil
end
def table
input_values.zip(output_values).map{|e| e.flatten }
end
def output_values=(ary)
raise ArgumentError, "output_values must be an Enumerable of booleans" if (!ary.kind_of?(Enumerable) || !ary.map{|e| e.is_a?(TrueClass) || e.is_a?(FalseClass) }.reduce(:&))
raise ArgumentError, "output_values must be the same size as input_values (#{input_values.count})" if ary.count != input_values.count
@output_values = ary
end
def shorthand(ary)
ary.map{|e|
case
when e == true
"T"
when e == false
"F"
else
"?"
end
}
end
def logical(ary, mode:)
ary.each_with_index.map{|e, i|
v = inputs[i]
case mode
when :high
e ? "#{v}" : "!#{v}"
when :low
e ? "!#{v}" : "#{v}"
end
}
end
def to_csv
CSV.generate do |csv|
# headers
csv << inputs + [output]
# values
table.each {|r| csv << shorthand(r) }
end
end
def sum_of_products
raise ArgumentError, "output_values must be an Enumerable of booleans before using" if (!output_values.kind_of?(Enumerable) || !output_values.map{|e| e.is_a?(TrueClass) || e.is_a?(FalseClass) }.reduce(:&))
hi_outputs = output_values.each_with_index.map{|v, i| logical(input_values[i], mode: :high) if v }.compact
conjunctions = hi_outputs.map{|e| "(" + e.join(" && ") + ")" }
disjuctions = conjunctions.join(" || ")
end
def product_of_sums
raise ArgumentError, "output_values must be an Enumerable of booleans before using" if (!output_values.kind_of?(Enumerable) || !output_values.map{|e| e.is_a?(TrueClass) || e.is_a?(FalseClass) }.reduce(:&))
lo_outputs = output_values.each_with_index.map{|v, i| logical(input_values[i], mode: :low) if !v }.compact
disjuctions = lo_outputs.map{|e| "(" + e.join(" || ") + ")" }
conjunctions = disjuctions.join(" && ")
end
def test(&block)
test_outs = input_values.map{|ins|
yield(*ins)
}
end
def compare(expected:, actual:, truthy: true)
if truthy
puts expected.zip(actual).map{|p| (p[0] ? (true == true && p[1]) : (false == false || p[1])) ? "." : "F (expected #{p[0].inspect}, got #{p[1].inspect})"}.join("")
else
puts expected.zip(actual).map{|p| p[0] == p[1] ? "." : "F (expected #{p[0].inspect}, got #{p[1].inspect})"}.join("")
end
end
end
f = BooleanFunction.new(inputs: %i(a b c), output: :d )
f.output_values = [false, false, false, true, false, false, false, false]
puts f.product_of_sums
# (!a || !b || !c) && (!a || !b || c) && (!a || b || !c) && (a || !b || !c) && (a || !b || c) && (a || b || !c) && (a || b || c)
puts f.sum_of_products
# (a && !b && !c)
touts = f.test{|a,b,c|
(a && !b && !c)
}
f.compare( expected: f.output_values, actual: touts )
# ........
touts = f.test{|a,b,c|
if a
!b && !c
end
}
f.compare( expected: f.output_values, actual: touts )
# ........
f.compare( expected: f.output_values, actual: touts, truthy: false )
# ....F (expected false, got nil)F (expected false, got nil)F (expected false, got nil)F (expected false, got nil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment