From 805e2543d06b54af120acf76c35fdb86d8e80616 Mon Sep 17 00:00:00 2001 From: donutloop Date: Mon, 26 Feb 2024 02:58:19 +0100 Subject: [PATCH] Add functional predicate NAND (#182) Add new function, NAND, designed to create a composed predicate representing the logical NAND operation applied to a list of predicates. The NAND operation is a logical operation that returns true only if all perdicate result in false otherwise false --- function/predicate.go | 16 ++++++++++++++++ function/predicate_example_test.go | 16 ++++++++++++++++ function/predicate_test.go | 26 ++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/function/predicate.go b/function/predicate.go index 362ac28..dbdd795 100644 --- a/function/predicate.go +++ b/function/predicate.go @@ -16,6 +16,22 @@ func And[T any](predicates ...func(T) bool) func(T) bool { } } +// And returns a composed predicate that represents the logical NAND of a list of predicates. +// It evaluates to true only if all predicates evaluate to false for the given value. +func Nand[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) { + return false // Short-circuit on the first true predicate + } + } + return true // True if all predicates are false + } +} + // Negate returns a predicate that represents the logical negation of this predicate. func Negate[T any](predicate func(T) bool) func(T) bool { return func(value T) bool { diff --git a/function/predicate_example_test.go b/function/predicate_example_test.go index b1e43ab..22bb097 100644 --- a/function/predicate_example_test.go +++ b/function/predicate_example_test.go @@ -53,6 +53,22 @@ func ExampleAnd() { // false } +func ExampleNand() { + isNumericAndLength5 := Nand( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcdef")) + + // Output: + // false + // false + // true +} + func ExampleNor() { match := Nor( func(s string) bool { return strings.ContainsAny(s, "0123456789") }, diff --git a/function/predicate_test.go b/function/predicate_test.go index 7e94d7c..95bde2b 100644 --- a/function/predicate_test.go +++ b/function/predicate_test.go @@ -54,6 +54,21 @@ func TestPredicatesAndPure(t *testing.T) { assert.ShouldBeFalse(isNumericAndLength5("abcde")) } +func TestPredicatesNandPure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesNandPure") + + isNumericAndLength5 := Nand( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + assert.ShouldBeFalse(isNumericAndLength5("12345")) + assert.ShouldBeFalse(isNumericAndLength5("1234")) + assert.ShouldBeTrue(isNumericAndLength5("abcdef")) +} + func TestPredicatesNorPure(t *testing.T) { t.Parallel() @@ -108,9 +123,12 @@ func TestPredicatesMix(t *testing.T) { assert.ShouldBeFalse(c("hello!")) - c = Nor(a, b) - assert.ShouldBeFalse(c("hello!")) + k := Nor(a, b) + assert.ShouldBeFalse(k("hello!")) - c = Xnor(a, b) - assert.ShouldBeTrue(c("hello!")) + o := Xnor(a, b) + assert.ShouldBeTrue(o("hello!")) + + p := Nand(c, k) + assert.ShouldBeTrue(p("hello!")) }