Ternary logic
This page uses content from Wikipedia. The original article was at Ternary logic. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
In logic, a three-valued logic (also trivalent, ternary, or trinary logic, sometimes abbreviated 3VL) is any of several many-valued logic systems in which there are three truth values indicating true, false and some indeterminate third value. This is contrasted with the more commonly known bivalent logics (such as classical sentential or boolean logic) which provide only for true and false. Conceptual form and basic ideas were initially created by Łukasiewicz, Lewis and Sulski. These were then re-formulated by Grigore Moisil in an axiomatic algebraic form, and also extended to n-valued logics in 1945.
|
|
| ||||||||||||||||||||||||||||||||||||||||
|
|
Task:
- Define a new type that emulates ternary logic by storing data trits.
- Given all the binary logic operators of the original programming language, reimplement these operators for the new Ternary logic type trit.
- Generate a sampling of results using trit variables.
- Kudos (κῦδος) for actually thinking up a test case algorithm where ternary logic is intrinsically useful, optimises the test case algorithm and is preferable to binary logic.
Note: Setun (Сетунь) was a balanced ternary computer developed in 1958 at Moscow State University. The device was built under the lead of Sergei Sobolev and Nikolay Brusentsov. It was the only modern ternary computer, using three-valued ternary logic
ALGOL 68
File: Ternary_logic.a68 <lang algol68># -*- coding: utf-8 -*- #
MODE TRIT = STRUCT(BITS trit); INT trit width = 1, trit base = 3; FORMAT trit fmt = $c("⌊","⌈","?" #|"~"#)$;
- These values treated are as per "Balanced ternary" #
- eg true=1, maybe=0, false=-1 #
TRIT true =INITTRIT 4r1 #⌈#, maybe=INITTRIT 4r0 #?#,
false=INITTRIT 4r2 #⌊#;
TRIT flip=true, flop=false, flap=maybe;
OP REPR = (TRIT t)STRING:
IF t = false THEN "⌊" ELIF t = maybe THEN "?" ELIF t = true THEN "⌈" ELSE raise value error(("invalid TRIT value",INITINT t));~ FI;
- Define some OPerators for coercing MODES #
OP INITTRIT = (BOOL in)TRIT:
(in|true|false);
OP B = (TRIT in)BOOL:
(in=true|TRUE|:in=false|FALSE| raise value error(("invalid TRIT to BOOL coercion: """, REPR in,""""));~ );
- These values treated are as per "Balanced ternary" #
- n.b true=1, maybe=0, false=-1 #
- Warning: BOOL ABS FALSE (0) is not the same as TRIT ABS false (-1) #
OP INITINT = (TRIT t)INT:
IF t=true THEN 1 ELIF t=maybe THEN 0 ELIF t=false THEN -1 ELSE raise value error(("invalid TRIT value",REPR t));~ FI;
OP INITTRIT = (INT in)TRIT: (
TRIT out; trit OF out:= trit OF IF in= 1 THEN true ELIF in= 0 THEN maybe ELIF in=-1 THEN false ELSE raise value error(("invalid TRIT value",in));~ FI; out
);
OP INITTRIT = (BITS b)TRIT:
(TRIT out; trit OF out:=b; out);
- Define the OPerators for the TRIT MODE #
- These can be optimised by peekng at the binary value #
- These operators are as per "Balanced ternary" #
- Warning: "both" is ignored as it isn't Ternary #
OP LT = (TRIT a,b)BOOL: a EQ false AND b NE false OR a EQ maybe AND b EQ true,
LE = (TRIT a,b)BOOL: a EQ b OR a LT b, EQ = (TRIT a,b)BOOL: trit OF a = trit OF b, NE = (TRIT a,b)BOOL: NOT (a EQ b), GE = (TRIT a,b)BOOL: NOT (a LT b), GT = (TRIT a,b)BOOL: NOT (a LE b);
- A solo, unique and rather confusing CMP OPerator #
PRIO CMP = 5; OP CMP = (TRIT a,b)TRIT:
IF a < b THEN false ELIF a = b THEN maybe ELIF a > b THEN true FI;
- ASCII OPerators #
OP < = (TRIT a,b)BOOL: a LT b,
<= = (TRIT a,b)BOOL: a LE b, = = (TRIT a,b)BOOL: a EQ b, /= = (TRIT a,b)BOOL: a NE b, >= = (TRIT a,b)BOOL: a GE b, > = (TRIT a,b)BOOL: a GT b;
- Non ASCII OPerators
OP ≤ = (TRIT a,b)BOOL: a LE b,
≠ = (TRIT a,b)BOOL: a NE b, ≥ = (TRIT a,b)BOOL: a GE b;
OP - = (TRIT t)TRIT:
IF t=maybe THEN maybe ELIF t=true THEN false ELIF t=false THEN true ELSE raise value error(("invalid TRIT value",REPR t)); ~ FI;
- Warning: This routine ASSIGNS "out" AND returns "carry" #
OP +:= = (REF TRIT out, TRIT arg)TRIT:
IF out = maybe THEN out := arg; maybe ELIF arg = maybe THEN # out:= out# arg ELIF out = arg THEN out := -out; arg ELIF out = -arg THEN out:=maybe; maybe ELSE raise value error((REPR out," + ",REPR arg)); ~ FI;
OP + = (TRIT a, b)TRIT:
(TRIT out:=a; VOID(out+:=b); out);
OP - = (TRIT a, b)TRIT:
a + -b;
OP * = (TRIT a, b)TRIT:
IF a = maybe OR b = maybe THEN maybe ELIF a = b THEN true ELSE false FI;
OP ODD = (TRIT t)BOOL:
t /= maybe;
COMMENT
Kleene logic truth tables:
END COMMENT
OP AND = (TRIT a,b)TRIT: (
[,]TRIT( # ∧ maybe, true, false, # #maybe# (maybe, maybe, false), #true# (maybe, true, false), #false# (false, false, false) )[@0,@0][ABS trit OF a, ABS trit OF b]
);
OP OR = (TRIT a,b)TRIT: (
[,]TRIT( # ∨ maybe, true, false, # #maybe# (maybe, true, maybe), #true# (true, true, true), #false# (maybe, true, false) )[@0,@0][ABS trit OF a, ABS trit OF b]
);
PRIO IMPLIES = 1; # 1.9 # OP IMPLIES = (TRIT a,b)TRIT: (
[,]TRIT( # ⊃ maybe, true, false, # #maybe# (maybe, true, maybe), #true# (maybe, true, false), #false# (true, true, true) )[@0,@0][ABS trit OF a, ABS trit OF b]
);
PRIO EQV = 1; # 1.8 # OP EQV = (TRIT a,b)TRIT: (
[,]TRIT( # ≡ maybe, true, false, # #maybe# (maybe, maybe, maybe), #true# (maybe, true, false), #false# (maybe, false, true) )[@0,@0][ABS trit OF a, ABS trit OF b]
);
- Non ASCII OPerators
OP ¬ = (TRIT a)TRIT: NOT b,
∨ = (TRIT a,b)TRIT: a OR b, ∧ = (TRIT a,b)TRIT: a AND b, & = (TRIT a,b)TRIT: a AND b, ⊃ = (TRIT a,b)TRIT: a IMPLIES b, ≡ = (TRIT a,b)TRIT: a EQV b;
- </lang>File: test_Ternary_logic.a68
<lang algol68>#!/usr/local/bin/a68g --script #
- -*- coding: utf-8 -*- #
PR READ "prelude/general.a68" PR PR READ "Ternary_logic.a68" PR
[]TRIT trits = (false, maybe, true);
FORMAT col fmt = $" "g" "$; FORMAT row fmt = $l3(f(col fmt)"|")f(col fmt)$; FORMAT row sep fmt = $l3("---+")"---"l$;
PROC row sep = VOID:
printf(row sep fmt);
PROC title = (UTF op)VOID:(
print(("Operator: ",op)); printf((row fmt," ",REPR false, REPR maybe, REPR true))
);
PROC print bool op table = (STRING op name, PROC(TRIT,TRIT)BOOL op)VOID: (
title(op name); FOR i FROM LWB trits TO UPB trits DO row sep; TRIT ti = trits[i]; printf((col fmt, REPR ti)); FOR j FROM LWB trits TO UPB trits DO TRIT tj = trits[j]; printf(($"|"$, col fmt, op(ti,tj))) OD OD; print(new line)
);
PROC print trit op table = (STRING op name, PROC(TRIT,TRIT)TRIT op)VOID: (
title(op name); FOR i FROM LWB trits TO UPB trits DO row sep; TRIT ti = trits[i]; printf((col fmt, REPR ti)); FOR j FROM LWB trits TO UPB trits DO TRIT tj = trits[j]; printf(($"|"$, col fmt, REPR op(ti,tj))) OD OD; print(new line)
);
printf((
$"Comparitive table of coercions:"l$, $" TRIT BOOL INT"l$
));
FOR it FROM LWB trits TO UPB trits DO
TRIT t = trits[it]; IF t = maybe THEN printf(($" "g" "$, REPR t, " ", INITINT t, $l$)) ELSE printf(($" "g" "$, REPR t, B t, INITINT t, $l$)) FI
OD;
printf((
$l"Specific test of the IMPLIES operator:"l$, $" "g" implies "g" is "b("not ","")"a contradiction!"l$, B false, B false, B(false IMPLIES false), B false, B true, B(false IMPLIES true), B false, REPR maybe, B(false IMPLIES maybe), B true, B false, B(true IMPLIES false), B true, B true, B(true IMPLIES true), REPR maybe, Btrue, B(maybe IMPLIES true), $" "g" implies "g" is "g" a contradiction!"l$, B true, REPR maybe, REPR (true IMPLIES maybe), REPR maybe, B false, REPR (maybe IMPLIES false), REPR maybe, REPR maybe, REPR (maybe IMPLIES maybe), $l$
));
printf($l"Kleene logic truth table samples:"l$);
print trit op table("CMP", (TRIT a,b)TRIT: a CMP b); print trit op table("EQV", (TRIT a,b)TRIT: a EQV b); print trit op table("IMPLIES", (TRIT a,b)TRIT: a IMPLIES b); print trit op table("AND", (TRIT a,b)TRIT: a AND b); print trit op table("OR", (TRIT a,b)TRIT: a OR b) CO; print trit op table("+", (TRIT a,b)TRIT: a + b); print trit op table("-", (TRIT a,b)TRIT: a - b); print trit op table("*", (TRIT a,b)TRIT: a * b); print bool op table("EQ", (TRIT a,b)BOOL: a EQ b); print bool op table("<=", (TRIT a,b)BOOL: a <= b) END CO</lang> Output:
Comparitive table of coercions: TRIT BOOL INT ⌊ F -1 ? +0 ⌈ T +1 Specific test of the IMPLIES operator: F implies F is not a contradiction! F implies T is not a contradiction! F implies ? is not a contradiction! T implies F is a contradiction! T implies T is not a contradiction! ? implies T is not a contradiction! T implies ? is ? a contradiction! ? implies F is ? a contradiction! ? implies ? is ? a contradiction! Kleene logic truth table samples: Operator: CMP | ⌊ | ? | ⌈ ---+---+---+--- ⌊ | ? | ⌊ | ⌊ ---+---+---+--- ? | ⌈ | ? | ⌊ ---+---+---+--- ⌈ | ⌈ | ⌈ | ? Operator: EQV | ⌊ | ? | ⌈ ---+---+---+--- ⌊ | ⌈ | ? | ⌊ ---+---+---+--- ? | ? | ? | ? ---+---+---+--- ⌈ | ⌊ | ? | ⌈ Operator: IMPLIES | ⌊ | ? | ⌈ ---+---+---+--- ⌊ | ⌈ | ⌈ | ⌈ ---+---+---+--- ? | ? | ? | ⌈ ---+---+---+--- ⌈ | ⌊ | ? | ⌈ Operator: AND | ⌊ | ? | ⌈ ---+---+---+--- ⌊ | ⌊ | ⌊ | ⌊ ---+---+---+--- ? | ⌊ | ? | ? ---+---+---+--- ⌈ | ⌊ | ? | ⌈ Operator: OR | ⌊ | ? | ⌈ ---+---+---+--- ⌊ | ⌊ | ? | ⌈ ---+---+---+--- ? | ? | ? | ⌈ ---+---+---+--- ⌈ | ⌈ | ⌈ | ⌈
C
Implementing logic using lookup tables. <lang c>#include <stdio.h>
typedef enum {
TRITTRUE, TRITMAYBE, TRITFALSE
} trit;
const char* tritString[3] = {"TRUE", "MAYBE", "FALSE"};
trit tritNot[3] = {TRITFALSE , TRITMAYBE, TRITTRUE}; trit tritAnd[3][3] = { {TRITTRUE, TRITMAYBE, TRITFALSE},
{TRITMAYBE, TRITMAYBE, TRITMAYBE}, {TRITFALSE, TRITFALSE, TRITFALSE} };
trit tritOr[3][3] = { {TRITTRUE, TRITTRUE, TRITTRUE},
{TRITTRUE, TRITMAYBE, TRITMAYBE}, {TRITTRUE, TRITMAYBE, TRITFALSE} };
trit tritThen[3][3] = { { TRITTRUE, TRITMAYBE, TRITFALSE},
{ TRITTRUE, TRITMAYBE, TRITMAYBE}, { TRITTRUE, TRITTRUE, TRITTRUE } };
trit tritEquiv[3][3] = { { TRITTRUE, TRITMAYBE, TRITFALSE},
{ TRITMAYBE, TRITMAYBE, TRITMAYBE}, { TRITFALSE, TRITMAYBE, TRITTRUE } };
int main() {
trit op1 = TRITTRUE; /* Declare. Initialize for CYA */ trit op2 = TRITTRUE; /* Declare. Initialize for CYA */
/* Demo 'not' */ for( op1 = TRITTRUE; op1 <= TRITFALSE; ++op1 ) { printf("Not %s: %s\n", tritString[op1], tritString[tritNot[op1]]); }
/* Blank line */ printf("\n");
/* Demo And */ for( op1 = TRITTRUE; op1 <= TRITFALSE; ++op1 ) { for( op2 = TRITTRUE; op2 <= TRITFALSE; ++op2 ) { printf("%s AND %s: %s\n", tritString[op1], tritString[op2], tritString[tritAnd[op1][op2]]); } }
return 0;
}</lang>
Output: <lang text>Not TRUE: FALSE Not MAYBE: MAYBE Not FALSE: TRUE
TRUE AND TRUE: TRUE TRUE AND MAYBE: MAYBE TRUE AND FALSE: FALSE MAYBE AND TRUE: MAYBE MAYBE AND MAYBE: MAYBE MAYBE AND FALSE: MAYBE FALSE AND TRUE: FALSE FALSE AND MAYBE: FALSE FALSE AND FALSE: FALSE</lang>
J
The designers of J felt that user defined types were harmful, so that part of the task will not be supported here.
Instead:
true: 1 false: 0 maybe: 0.5
<lang j>not=: -. and=: <. or =: >. if =: (-.@[ >. ])"0 eq =: (1 - -:@(= -.) >. |@-)"0</lang>
Example use:
<lang j> 0 0.5 1 and/ 0 0.5 1 0 0 0 0 0.5 0.5 0 0.5 1
0 0.5 1 or/ 0 0.5 1 0 0.5 1
0.5 0.5 1
1 1 1
0 0.5 1 if/ 0 0.5 1 1 1 1
0.5 0.5 1
0 0.5 1
0 0.5 1 eq/ 0 0.5 1 1 0.5 0
0.5 0.5 0.5
0 0.5 1</lang>
Note that this implementation is a special case of "fuzzy logic" (using a limited set of values).
Java
<lang java5>public class Logic{ public static enum Trit{ TRUE, MAYBE, FALSE;
public Trit and(Trit other){ if(this == TRUE){ return other; }else if(this == MAYBE){ return MAYBE; }else{ return FALSE; } }
public Trit or(Trit other){ if(this == TRUE){ return TRUE; }else if(this == MAYBE){ return (other == TRUE) ? TRUE : MAYBE; }else{ return other; } }
public Trit tIf(Trit other){ if(this == TRUE){ return other; }else if(this == MAYBE){ return (other == TRUE) ? TRUE : MAYBE; }else{ return TRUE; } }
public Trit not(){ if(this == TRUE){ return FALSE; }else if(this == MAYBE){ return MAYBE; }else{ return TRUE; } }
public Trit equals(Trit other){ if(this == TRUE){ return other; }else if(this == MAYBE){ return MAYBE; }else{ return other.not(); } } } public static void main(String[] args){ for(Trit a:Trit.values()){ System.out.println("not " + a + ": " + a.not()); } for(Trit a:Trit.values()){ for(Trit b:Trit.values()){ System.out.println(a+" and "+b+": "+a.and(b)+ "\t "+a+" or "+b+": "+a.or(b)+ "\t "+a+" implies "+b+": "+a.tIf(b)+ "\t "+a+" = "+b+": "+a.equals(b)); } } } }</lang> Output:
not TRUE: FALSE not MAYBE: MAYBE not FALSE: TRUE TRUE and TRUE: TRUE TRUE or TRUE: TRUE TRUE implies TRUE: TRUE TRUE = TRUE: TRUE TRUE and MAYBE: MAYBE TRUE or MAYBE: TRUE TRUE implies MAYBE: MAYBE TRUE = MAYBE: MAYBE TRUE and FALSE: FALSE TRUE or FALSE: TRUE TRUE implies FALSE: FALSE TRUE = FALSE: FALSE MAYBE and TRUE: MAYBE MAYBE or TRUE: TRUE MAYBE implies TRUE: TRUE MAYBE = TRUE: MAYBE MAYBE and MAYBE: MAYBE MAYBE or MAYBE: MAYBE MAYBE implies MAYBE: MAYBE MAYBE = MAYBE: MAYBE MAYBE and FALSE: MAYBE MAYBE or FALSE: MAYBE MAYBE implies FALSE: MAYBE MAYBE = FALSE: MAYBE FALSE and TRUE: FALSE FALSE or TRUE: TRUE FALSE implies TRUE: TRUE FALSE = TRUE: FALSE FALSE and MAYBE: FALSE FALSE or MAYBE: MAYBE FALSE implies MAYBE: TRUE FALSE = MAYBE: MAYBE FALSE and FALSE: FALSE FALSE or FALSE: FALSE FALSE implies FALSE: TRUE FALSE = FALSE: TRUE
Perl 6
<lang perl6>enum Trit <Foo Moo Too>;
multi prefix:<¬> ($a) { Trit(1-($a-1)) }
multi infix:<∧> ($a,$b) { $a min $b } multi infix:<∨> ($a,$b) { $a max $b }
multi infix:<→> ($a,$b) { ¬$a max $b } multi infix:<≡> ($a,$b) { Trit(1 + ($a-1) * ($b-1)) }
say '¬'; say "Too {¬Too}"; say "Moo {¬Moo}"; say "Foo {¬Foo}";
sub tbl (&op,$name) {
say ; say "$name Too Moo Foo"; say " ╔═══════════"; say "Too║{op Too,Too} {op Too,Moo} {op Too,Foo}"; say "Moo║{op Moo,Too} {op Moo,Moo} {op Moo,Foo}"; say "Foo║{op Foo,Too} {op Foo,Moo} {op Foo,Foo}";
}
tbl(&infix:<∧>, '∧'); tbl(&infix:<∨>, '∨'); tbl(&infix:<→>, '→'); tbl(&infix:<≡>, '≡');</lang> Output:
¬ Too Foo Moo Moo Foo Too ∧ Too Moo Foo ╔═══════════ Too║Too Moo Foo Moo║Moo Moo Foo Foo║Foo Foo Foo ∨ Too Moo Foo ╔═══════════ Too║Too Too Too Moo║Too Moo Moo Foo║Too Moo Foo → Too Moo Foo ╔═══════════ Too║Too Moo Foo Moo║Too Moo Moo Foo║Too Too Too ≡ Too Moo Foo ╔═══════════ Too║Too Moo Foo Moo║Moo Moo Moo Foo║Foo Moo Too
Python
In Python, the keywords 'and', 'not', and 'or' are coerced to always work as boolean operators. I have therefore overloaded the boolean bitwise operators &, |, ^ to provide the required functionality. <lang python>class Trit(int):
def __new__(cls, value): if value == 'TRUE': value = 1 elif value == 'FALSE': value = 0 elif value == 'MAYBE': value = -1 return super(Trit, cls).__new__(cls, value // (abs(value) or 1))
def __repr__(self): if self > 0: return 'TRUE' elif self == 0: return 'FALSE' return 'MAYBE'
def __str__(self): return repr(self)
def __bool__(self): if self > 0: return True elif self == 0: return False else: raise ValueError("invalid literal for bool(): '%s'" % self)
def __or__(self, other): if isinstance(other, Trit): return _ttable[(self, other)][1] else: try: return _ttable[(self, Trit(bool(other)))][1] except: return NotImplemented
def __ror__(self, other): if isinstance(other, Trit): return _ttable[(self, other)][1] else: try: return _ttable[(self, Trit(bool(other)))][1] except: return NotImplemented
def __and__(self, other): if isinstance(other, Trit): return _ttable[(self, other)][0] else: try: return _ttable[(self, Trit(bool(other)))][0] except: return NotImplemented
def __rand__(self, other): if isinstance(other, Trit): return _ttable[(self, other)][0] else: try: return _ttable[(self, Trit(bool(other)))][0] except: return NotImplemented
def __xor__(self, other): if isinstance(other, Trit): return _ttable[(self, other)][2] else: try: return _ttable[(self, Trit(bool(other)))][2] except: return NotImplemented
def __rxor__(self, other): if isinstance(other, Trit): return _ttable[(self, other)][2] else: try: return _ttable[(self, Trit(bool(other)))][2] except: return NotImplemented
def __invert__(self): return _ttable[self] def __getattr__(self, name): if name in ('_n', 'flip'): # So you can do x._n == x.flip; the inverse of x # In Python 'not' is strictly boolean so we can't write `not x` # Same applies to keywords 'and' and 'or'. return _ttable[self] else: raise AttributeError
TRUE, FALSE, MAYBE = Trit(1), Trit(0), Trit(-1)
_ttable = {
# A: -> flip_A TRUE: FALSE, FALSE: TRUE, MAYBE: MAYBE, # (A, B): -> (A_and_B, A_or_B, A_xor_B) (MAYBE, MAYBE): (MAYBE, MAYBE, MAYBE), (MAYBE, FALSE): (FALSE, MAYBE, MAYBE), (MAYBE, TRUE): (MAYBE, TRUE, MAYBE), (FALSE, MAYBE): (FALSE, MAYBE, MAYBE), (FALSE, FALSE): (FALSE, FALSE, FALSE), (FALSE, TRUE): (FALSE, TRUE, TRUE), ( TRUE, MAYBE): (MAYBE, TRUE, MAYBE), ( TRUE, FALSE): (FALSE, TRUE, TRUE), ( TRUE, TRUE): ( TRUE, TRUE, FALSE), }
values = ('FALSE', 'TRUE ', 'MAYBE')
print("\nTrit logical inverse, '~'") for a in values:
expr = '~%s' % a print(' %s = %s' % (expr, eval(expr)))
for op, ophelp in (('&', 'and'), ('|', 'or'), ('^', 'exclusive-or')):
print("\nTrit logical %s, '%s'" % (ophelp, op)) for a in values: for b in values: expr = '%s %s %s' % (a, op, b) print(' %s = %s' % (expr, eval(expr)))</lang>
- Output
Trit logical inverse, '~' ~FALSE = TRUE ~TRUE = FALSE ~MAYBE = MAYBE Trit logical and, '&' FALSE & FALSE = FALSE FALSE & TRUE = FALSE FALSE & MAYBE = FALSE TRUE & FALSE = FALSE TRUE & TRUE = TRUE TRUE & MAYBE = MAYBE MAYBE & FALSE = FALSE MAYBE & TRUE = MAYBE MAYBE & MAYBE = MAYBE Trit logical or, '|' FALSE | FALSE = FALSE FALSE | TRUE = TRUE FALSE | MAYBE = MAYBE TRUE | FALSE = TRUE TRUE | TRUE = TRUE TRUE | MAYBE = TRUE MAYBE | FALSE = MAYBE MAYBE | TRUE = TRUE MAYBE | MAYBE = MAYBE Trit logical exclusive-or, '^' FALSE ^ FALSE = FALSE FALSE ^ TRUE = TRUE FALSE ^ MAYBE = MAYBE TRUE ^ FALSE = TRUE TRUE ^ TRUE = FALSE TRUE ^ MAYBE = MAYBE MAYBE ^ FALSE = MAYBE MAYBE ^ TRUE = MAYBE MAYBE ^ MAYBE = MAYBE
- Extra doodling in the Python shell
>>> values = (TRUE, FALSE, MAYBE) >>> for a in values: for b in values: assert (a & ~b) | (b & ~a) == a ^ b >>>
Ruby
Ruby, like Smalltalk, has two boolean classes: TrueClass for true
and FalseClass for false
. We add a third class, MaybeClass for MAYBE
, and define ternary logic for all three classes.
We keep !a
, a & b
and so on for binary logic. We add !a.trit
, a.trit & b
and so on for ternary logic. The code for !a.trit
uses def !
, which works with Ruby 1.9, but fails as a syntax error with Ruby 1.8.
<lang ruby># trit.rb - ternary logic
require 'singleton'
- MAYBE, the only instance of MaybeClass, enables a system of ternary
- logic using TrueClass#trit, MaybeClass#trit and FalseClass#trit.
- !a.trit # ternary not
- a.trit & b # ternary and
- a.trit | b # ternary or
- a.trit ^ b # ternary exclusive or
- a.trit == b # ternary equal
- Though +true+ and +false+ are internal Ruby values, +MAYBE+ is not.
- Programs may want to assign +maybe = MAYBE+ in scopes that use
- ternary logic. Then programs can use +true+, +maybe+ and +false+.
class MaybeClass
include Singleton
# maybe.to_s # => "maybe" def to_s; "maybe"; end
end
MAYBE = MaybeClass.instance
class TrueClass
TritMagic = Object.new class << TritMagic def index; 0; end def !; false; end def & other; other; end def | other; true; end def ^ other; [false, MAYBE, true][other.trit.index]; end def == other; other; end end
# Performs ternary logic. See MaybeClass. # !true.trit # => false # true.trit & obj # => obj # true.trit | obj # => true # true.trit ^ obj # => false, maybe or true # true.trit == obj # => obj def trit; TritMagic; end
end
class MaybeClass
TritMagic = Object.new class << TritMagic def index; 1; end def !; MAYBE; end def & other; [MAYBE, MAYBE, false][other.trit.index]; end def | other; [true, MAYBE, MAYBE][other.trit.index]; end def ^ other; MAYBE; end def == other; MAYBE; end end
# Performs ternary logic. See MaybeClass. # !maybe.trit # => maybe # maybe.trit & obj # => maybe or false # maybe.trit | obj # => true or maybe # maybe.trit ^ obj # => maybe # maybe.trit == obj # => maybe def trit; TritMagic; end
end
class FalseClass
TritMagic = Object.new class << TritMagic def index; 2; end def !; true; end def & other; false; end def | other; other; end def ^ other; other; end def == other; [false, MAYBE, true][other.trit.index]; end end
# Performs ternary logic. See MaybeClass. # !false.trit # => true # false.trit & obj # => false # false.trit | obj # => obj # false.trit ^ obj # => obj # false.trit == obj # => false, maybe or true def trit; TritMagic; end
end</lang>
This IRB session shows ternary not, and, or, equal.
<lang ruby>$ irb irb(main):001:0> require './trit' => true irb(main):002:0> maybe = MAYBE => maybe irb(main):003:0> !true.trit => false irb(main):004:0> !maybe.trit => maybe irb(main):005:0> maybe.trit & false => false irb(main):006:0> maybe.trit | true => true irb(main):007:0> false.trit == true => false irb(main):008:0> false.trit == maybe => maybe</lang>
This program shows all 9 outcomes from a.trit ^ b
.
<lang ruby>require 'trit' maybe = MAYBE
[true, maybe, false].each do |a|
[true, maybe, false].each do |b| printf "%5s ^ %5s => %5s\n", a, b, a.trit ^ b end
end</lang>
$ ruby -I. trit-xor.rb true ^ true => false true ^ maybe => maybe true ^ false => true maybe ^ true => maybe maybe ^ maybe => maybe maybe ^ false => maybe false ^ true => true false ^ maybe => maybe false ^ false => false