Skip to content

Commit

Permalink
Add functional predicate XNOR (#181)
Browse files Browse the repository at this point in the history
Add new function, Xnor, designed to create a composed predicate representing
the logical Exclusive NOR (XNOR) operation applied to a list of predicates.
The XNOR operation is a logical operation that returns true only
if all operands have the same boolean value
  • Loading branch information
donutloop authored Feb 25, 2024
1 parent aebab7c commit a43bc55
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 0 deletions.
28 changes: 28 additions & 0 deletions function/predicate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package function
// And returns a composed predicate that represents the logical AND of a list of predicates.
// It evaluates to true only if all predicates evaluate to true for the given value.
func And[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if !predicate(value) {
Expand All @@ -23,6 +26,9 @@ func Negate[T any](predicate func(T) bool) func(T) bool {
// Or returns a composed predicate that represents the logical OR of a list of predicates.
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
func Or[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
Expand All @@ -36,6 +42,9 @@ func Or[T any](predicates ...func(T) bool) func(T) bool {
// Nor returns a composed predicate that represents the logical NOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
func Nor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
Expand All @@ -45,3 +54,22 @@ func Nor[T any](predicates ...func(T) bool) func(T) bool {
return true // Only returns true if all predicates evaluate to false
}
}

// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to true or false for the given value.
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
trueCount := 0
for _, predicate := range predicates {
if predicate(value) {
trueCount++
}
}
// XNOR is true if either all predicates are true or all are false
// This is the same as saying trueCount is 0 (all false) or trueCount is len(predicates) (all true)
return trueCount == 0 || trueCount == len(predicates)
}
}
16 changes: 16 additions & 0 deletions function/predicate_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ func ExampleNor() {
// false
}

func ExampleXnor() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }

match := Xnor(isEven, isPositive)

fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))

// Output:
// true
// true
// false
}

// func ExamplePredicatesMix() {
// a := Or(
// func(s string) bool { return strings.ContainsAny(s, "0123456789") },
Expand Down
18 changes: 18 additions & 0 deletions function/predicate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ func TestPredicatesNorPure(t *testing.T) {
assert.ShouldBeFalse(match("0123456789"))
}

func TestPredicatesXnorPure(t *testing.T) {
t.Parallel()

assert := internal.NewAssert(t, "TestPredicatesXnorPure")

isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }

match := Xnor(isEven, isPositive)

assert.ShouldBeTrue(match(2))
assert.ShouldBeTrue(match(-3))
assert.ShouldBeFalse(match(3))
}

func TestPredicatesMix(t *testing.T) {
t.Parallel()

Expand All @@ -95,4 +110,7 @@ func TestPredicatesMix(t *testing.T) {

c = Nor(a, b)
assert.ShouldBeFalse(c("hello!"))

c = Xnor(a, b)
assert.ShouldBeTrue(c("hello!"))
}

0 comments on commit a43bc55

Please sign in to comment.