From be444f521da49e64e0aadc354b0a246f8dd53c22 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Fri, 6 Jan 2023 16:12:44 +0800 Subject: [PATCH] test: add examples for function package --- function/function.go | 76 +++++++++++---- function/function_example_test.go | 147 ++++++++++++++++++++++++++++++ function/function_internal.go | 39 -------- function/function_test.go | 16 ++-- 4 files changed, 212 insertions(+), 66 deletions(-) create mode 100644 function/function_example_test.go delete mode 100644 function/function_internal.go diff --git a/function/function.go b/function/function.go index 6846696..d57fb6c 100644 --- a/function/function.go +++ b/function/function.go @@ -5,11 +5,13 @@ package function import ( + "fmt" "reflect" "time" ) -// After creates a function that invokes func once it's called n or more times +// After creates a function that invokes func once it's called n or more times. +// Play: https://go.dev/play/p/8mQhkFmsgqs func After(n int, fn any) func(args ...any) []reflect.Value { // Catch programming error while constructing the closure mustBeFunction(fn) @@ -23,7 +25,8 @@ func After(n int, fn any) func(args ...any) []reflect.Value { } } -// Before creates a function that invokes func once it's called less than n times +// Before creates a function that invokes func once it's called less than n times. +// Play: https://go.dev/play/p/0HqUDIFZ3IL func Before(n int, fn any) func(args ...any) []reflect.Value { // Catch programming error while constructing the closure mustBeFunction(fn) @@ -40,41 +43,48 @@ func Before(n int, fn any) func(args ...any) []reflect.Value { } } -// Fn is for curry function which is func(...any) any -type Fn func(...any) any +// CurryFn is for make curry function +type CurryFn[T any] func(...T) T -// Curry make a curry function -func (f Fn) Curry(i any) func(...any) any { - return func(values ...any) any { - v := append([]any{i}, values...) - return f(v...) +// New make a curry function for specific value. +// Play: Todo +func (cf CurryFn[T]) New(val T) func(...T) T { + return func(vals ...T) T { + args := append([]T{val}, vals...) + return cf(args...) } } -// Compose compose the functions from right to left -func Compose(fnList ...func(...any) any) func(...any) any { - return func(s ...any) any { - f := fnList[0] - restFn := fnList[1:] +// Compose compose the functions from right to left. +// Play: Todo +func Compose[T any](fnList ...func(...T) T) func(...T) T { + return func(args ...T) T { + firstFn := fnList[0] + restFns := fnList[1:] if len(fnList) == 1 { - return f(s...) + return firstFn(args...) } - return f(Compose(restFn...)(s...)) + fn := Compose(restFns...) + arg := fn(args...) + + return firstFn(arg) } } -// Delay make the function execution after delayed time +// Delay make the function execution after delayed time. +// Play: https://go.dev/play/p/Ivtc2ZE-Tye func Delay(delay time.Duration, fn any, args ...any) { // Catch programming error while constructing the closure mustBeFunction(fn) time.Sleep(delay) - invokeFunc(fn, args...) + unsafeInvokeFunc(fn, args...) } -// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked. +// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.. +// Play: https://go.dev/play/p/absuEGB_GN7 func Debounced(fn func(), duration time.Duration) func() { // Catch programming error while constructing the closure mustBeFunction(fn) @@ -92,7 +102,8 @@ func Debounced(fn func(), duration time.Duration) func() { return func() { timer.Reset(duration) } } -// Schedule invoke function every duration time, util close the returned bool chan +// Schedule invoke function every duration time, util close the returned bool channel. +// Play: https://go.dev/play/p/hbON-Xeyn5N func Schedule(d time.Duration, fn any, args ...any) chan bool { // Catch programming error while constructing the closure mustBeFunction(fn) @@ -114,6 +125,7 @@ func Schedule(d time.Duration, fn any, args ...any) chan bool { // Pipeline takes a list of functions and returns a function whose param will be passed into // the functions one by one. +// Play: https://go.dev/play/p/mPdUVvj6HD6 func Pipeline[T any](funcs ...func(T) T) func(T) T { return func(arg T) (result T) { result = arg @@ -123,3 +135,27 @@ func Pipeline[T any](funcs ...func(T) T) func(T) T { return } } + +func unsafeInvokeFunc(fn any, args ...any) []reflect.Value { + fv := reflect.ValueOf(fn) + params := make([]reflect.Value, len(args)) + for i, item := range args { + params[i] = reflect.ValueOf(item) + } + return fv.Call(params) +} + +func functionValue(function any) reflect.Value { + v := reflect.ValueOf(function) + if v.Kind() != reflect.Func { + panic(fmt.Sprintf("Invalid function type, value of type %T", function)) + } + return v +} + +func mustBeFunction(function any) { + v := reflect.ValueOf(function) + if v.Kind() != reflect.Func { + panic(fmt.Sprintf("Invalid function type, value of type %T", function)) + } +} diff --git a/function/function_example_test.go b/function/function_example_test.go new file mode 100644 index 0000000..823d5fd --- /dev/null +++ b/function/function_example_test.go @@ -0,0 +1,147 @@ +package function + +import ( + "fmt" + "strings" + "time" +) + +func ExampleAfter() { + fn := After(2, func() { + fmt.Println("test") + }) + + fn() + fn() + + // Output: + // test +} + +func ExampleBefore() { + fn := Before(2, func() { + fmt.Println("test") + }) + + fn() + fn() + fn() + fn() + + // Output: + // test + // test +} + +func ExampleCurryFn_New() { + add := func(a, b int) int { + return a + b + } + + var addCurry CurryFn[int] = func(values ...int) int { + return add(values[0], values[1]) + } + add1 := addCurry.New(1) + + result := add1(2) + + fmt.Println(result) + + // Output: + // 3 +} + +func ExampleCompose() { + toUpper := func(strs ...string) string { + return strings.ToUpper(strs[0]) + } + toLower := func(strs ...string) string { + return strings.ToLower(strs[0]) + } + transform := Compose(toUpper, toLower) + + result := transform("aBCde") + + fmt.Println(result) + + // Output: + // ABCDE +} + +func ExampleDelay() { + var print = func(s string) { + fmt.Println(s) + } + + Delay(2*time.Second, print, "hello") + + // Output: + // hello +} + +func ExampleDebounced() { + count := 0 + add := func() { + count++ + } + + debouncedAdd := Debounced(add, 50*time.Microsecond) + + debouncedAdd() + debouncedAdd() + debouncedAdd() + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + // Output: + // 1 + // 2 +} + +func ExampleSchedule() { + var result []string + + appendFn := func(s string) { + result = append(result, s) + } + + stop := Schedule(1*time.Second, appendFn, "*") + + time.Sleep(3 * time.Second) + close(stop) + + fmt.Println(result) + + // Output: + // [* * *] +} + +func ExamplePipeline() { + addOne := func(x int) int { + return x + 1 + } + double := func(x int) int { + return 2 * x + } + square := func(x int) int { + return x * x + } + + fn := Pipeline(addOne, double, square) + + result := fn(2) + + fmt.Println(result) + + // Output: + // 36 +} diff --git a/function/function_internal.go b/function/function_internal.go deleted file mode 100644 index 0de3f65..0000000 --- a/function/function_internal.go +++ /dev/null @@ -1,39 +0,0 @@ -package function - -import ( - "fmt" - "reflect" -) - -func invokeFunc(fn any, args ...any) []reflect.Value { - fv := functionValue(fn) - params := make([]reflect.Value, len(args)) - for i, item := range args { - params[i] = reflect.ValueOf(item) - } - return fv.Call(params) -} - -func unsafeInvokeFunc(fn any, args ...any) []reflect.Value { - fv := reflect.ValueOf(fn) - params := make([]reflect.Value, len(args)) - for i, item := range args { - params[i] = reflect.ValueOf(item) - } - return fv.Call(params) -} - -func functionValue(function any) reflect.Value { - v := reflect.ValueOf(function) - if v.Kind() != reflect.Func { - panic(fmt.Sprintf("Invalid function type, value of type %T", function)) - } - return v -} - -func mustBeFunction(function any) { - v := reflect.ValueOf(function) - if v.Kind() != reflect.Func { - panic(fmt.Sprintf("Invalid function type, value of type %T", function)) - } -} diff --git a/function/function_test.go b/function/function_test.go index 1f5b7e1..a58edd2 100644 --- a/function/function_test.go +++ b/function/function_test.go @@ -62,24 +62,26 @@ func TestCurry(t *testing.T) { add := func(a, b int) int { return a + b } - var addCurry Fn = func(values ...any) any { - return add(values[0].(int), values[1].(int)) + var addCurry CurryFn[int] = func(values ...int) int { + return add(values[0], values[1]) } - add1 := addCurry.Curry(1) + add1 := addCurry.New(1) + assert.Equal(3, add1(2)) } func TestCompose(t *testing.T) { assert := internal.NewAssert(t, "TestCompose") - toUpper := func(a ...any) any { - return strings.ToUpper(a[0].(string)) + toUpper := func(strs ...string) string { + return strings.ToUpper(strs[0]) } - toLower := func(a ...any) any { - return strings.ToLower(a[0].(string)) + toLower := func(strs ...string) string { + return strings.ToLower(strs[0]) } expected := toUpper(toLower("aBCde")) + cf := Compose(toUpper, toLower) res := cf("aBCde")