-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTernary_logic
executable file
·168 lines (135 loc) · 4.84 KB
/
Ternary_logic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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
165
166
167
168
#!/usr/bin/env perl
#u# http://rosettacode.org/wiki/Ternary_logic
#c# 2018-08-11 <RC, 2023-03-21 >RC, 2023-03-22 >RC
#p# OK
#n# using modern Perl required cleaning up some dodgy practices, results in cleaner code
#n# factored out the logic values into %E hash table, all refs go through it now
my $result;
use v5.36.0;
package Trit;
use List::Util qw(min max);
our @ISA = qw(Exporter);
our @EXPORT = qw(%E);
my %E = (true => 1, false => -1, maybe => 0); # codes for logic values
use overload
'<=>' => sub ($a,$b) { $a->cmp($b) },
'cmp' => sub ($a,$b) { $a->cmp($b) },
'==' => sub ($a,$b,$) { $$a == $$b },
'eq' => sub ($a,$b,$) { $a->equiv($b) },
'>' => sub ($a,$b,$) { $$a > $E{$b} },
'<' => sub ($a,$b,$) { $$a < $E{$b} },
'>=' => sub ($a,$b,$) { $$a >= $$b },
'<=' => sub ($a,$b,$) { $$a <= $$b },
'|' => sub ($a,$b,$,$,$) { $a->or($b) },
'&' => sub ($a,$b,$,$,$) { $a->and($b) },
'!' => sub ($a,$,$) { $a->not() },
'~' => sub ($a,$,$,$,$) { $a->not() }, # why is this different from '!' ?
'neg' => sub ($a,$,$) { -$$a },
'""' => sub ($a,$,$) { $a->tostr() },
'0+' => sub ($a,$,$) { $a->tonum() },
;
# $a = ref(Trit), $b = string
sub eqv ($a,$b) {
$$a == $E{maybe} || $E{$b} == $E{maybe} ? $E{maybe} : # either arg 'maybe', return 'maybe'
$$a == $E{false} && $E{$b} == $E{false} ? $E{true} : # both args 'false', return 'true'
min $$a, $E{$b} # either arg 'false', return 'false', otherwise 'true'
}
# get in trouble here because the tests follow the overloading rules, resulting in unwanted ref(Trit) vs scalar comparisons
# so use tests that avoid the overloaded operations, even if ends up seeming a bit silly
sub new ($class, $v) {
my $value =
! defined $v ? $E{maybe} :
#$v eq 'true' ? $E{true} :
#$v eq 'false' ? $E{false} :
#$v eq 'maybe' ? $E{maybe} :
$v =~ /true/ ? $E{true} :
$v =~ /false/ ? $E{false} :
$v =~ /maybe/ ? $E{maybe} :
#$v > $E{maybe} ? $E{true} :
#$v < $E{maybe} ? $E{false} :
$v gt $E{maybe} ? $E{true} :
$v lt $E{maybe} ? $E{false} :
$E{maybe} ;
bless \$value, $class;
}
sub tostr ($a) { $$a > $E{maybe} ? 'true' : $$a < $E{maybe} ? 'false' : 'maybe' }
sub tonum ($a) { $$a }
sub not ($a) { Trit->new( -$a ) } # added overloaded operation 'neg' for this
sub cmp ($a,$b) { Trit->new( $a <=> $b ) }
sub and ($a,$b) { Trit->new( min $a, $b ) }
sub or ($a,$b) { Trit->new( max $a, $b ) }
sub equiv ($a,$b) { Trit->new( eqv $a, $b ) }
package main;
Trit->import;
my @a = ( Trit->new($E{true}), Trit->new($E{maybe}), Trit->new($E{false}) );
$result = sprintf "Codes for logic values: %6s = %d %6s = %d %6s = %d\n", @a[0, 0, 1, 1, 2, 2]; # easier than List::Util::mesh
# prefix ! (not) ['~' also can be used]
$result .= "\na\tNOT a" . "\n";
$result .= "$_\t" . (!$_) ."\n" for @a;
# infix & (and)
$result .= "\nAND\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a & $_) for @a; $result .= "\n" }
# infix | (or)
$result .= "\nOR\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a | $_) for @a; $result .= "\n" }
# infix eq (equivalence)
$result .= "\nEQV\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a eq $_) for @a; $result .= "\n" }
# infix == (equality)
$result .= "\n==\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a == $_) for @a; $result .= "\n" }
# infix >
$result .= "\n>\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a > $_) for @a; $result .= "\n" }
# infix >=
$result .= "\n>=\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a >= $_) for @a; $result .= "\n" }
# infix <
$result .= "\n<\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a < $_) for @a; $result .= "\n" }
# infix <=
$result .= "\n<=\t" . join("\t",@a) . "\n";
for my $a (@a) { $result .= $a; $result .= "\t" . ($a <= $_) for @a; $result .= "\n" }
say $result;
my $ref = <<'EOD';
Codes for logic values: true = 1 maybe = 0 false = -1
a NOT a
true false
maybe maybe
false true
AND true maybe false
true true maybe false
maybe maybe maybe false
false false false false
OR true maybe false
true true true true
maybe true maybe maybe
false true maybe false
EQV true maybe false
true true maybe false
maybe maybe maybe maybe
false false maybe true
== true maybe false
true 1
maybe 1
false 1
> true maybe false
true 1 1
maybe 1
false
>= true maybe false
true 1 1 1
maybe 1 1
false 1
< true maybe false
true
maybe 1
false 1 1
<= true maybe false
true 1
maybe 1 1
false 1 1 1
EOD
use Test::More;
is ($result, $ref);
done_testing;