mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-23 13:52:26 +08:00
test: add examples for function package
This commit is contained in:
@@ -5,11 +5,13 @@
|
|||||||
package function
|
package function
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"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 {
|
func After(n int, fn any) func(args ...any) []reflect.Value {
|
||||||
// Catch programming error while constructing the closure
|
// Catch programming error while constructing the closure
|
||||||
mustBeFunction(fn)
|
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 {
|
func Before(n int, fn any) func(args ...any) []reflect.Value {
|
||||||
// Catch programming error while constructing the closure
|
// Catch programming error while constructing the closure
|
||||||
mustBeFunction(fn)
|
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
|
// CurryFn is for make curry function
|
||||||
type Fn func(...any) any
|
type CurryFn[T any] func(...T) T
|
||||||
|
|
||||||
// Curry make a curry function
|
// New make a curry function for specific value.
|
||||||
func (f Fn) Curry(i any) func(...any) any {
|
// Play: Todo
|
||||||
return func(values ...any) any {
|
func (cf CurryFn[T]) New(val T) func(...T) T {
|
||||||
v := append([]any{i}, values...)
|
return func(vals ...T) T {
|
||||||
return f(v...)
|
args := append([]T{val}, vals...)
|
||||||
|
return cf(args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose compose the functions from right to left
|
// Compose compose the functions from right to left.
|
||||||
func Compose(fnList ...func(...any) any) func(...any) any {
|
// Play: Todo
|
||||||
return func(s ...any) any {
|
func Compose[T any](fnList ...func(...T) T) func(...T) T {
|
||||||
f := fnList[0]
|
return func(args ...T) T {
|
||||||
restFn := fnList[1:]
|
firstFn := fnList[0]
|
||||||
|
restFns := fnList[1:]
|
||||||
|
|
||||||
if len(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) {
|
func Delay(delay time.Duration, fn any, args ...any) {
|
||||||
// Catch programming error while constructing the closure
|
// Catch programming error while constructing the closure
|
||||||
mustBeFunction(fn)
|
mustBeFunction(fn)
|
||||||
|
|
||||||
time.Sleep(delay)
|
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() {
|
func Debounced(fn func(), duration time.Duration) func() {
|
||||||
// Catch programming error while constructing the closure
|
// Catch programming error while constructing the closure
|
||||||
mustBeFunction(fn)
|
mustBeFunction(fn)
|
||||||
@@ -92,7 +102,8 @@ func Debounced(fn func(), duration time.Duration) func() {
|
|||||||
return func() { timer.Reset(duration) }
|
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 {
|
func Schedule(d time.Duration, fn any, args ...any) chan bool {
|
||||||
// Catch programming error while constructing the closure
|
// Catch programming error while constructing the closure
|
||||||
mustBeFunction(fn)
|
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
|
// Pipeline takes a list of functions and returns a function whose param will be passed into
|
||||||
// the functions one by one.
|
// the functions one by one.
|
||||||
|
// Play: https://go.dev/play/p/mPdUVvj6HD6
|
||||||
func Pipeline[T any](funcs ...func(T) T) func(T) T {
|
func Pipeline[T any](funcs ...func(T) T) func(T) T {
|
||||||
return func(arg T) (result T) {
|
return func(arg T) (result T) {
|
||||||
result = arg
|
result = arg
|
||||||
@@ -123,3 +135,27 @@ func Pipeline[T any](funcs ...func(T) T) func(T) T {
|
|||||||
return
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
147
function/function_example_test.go
Normal file
147
function/function_example_test.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -62,24 +62,26 @@ func TestCurry(t *testing.T) {
|
|||||||
add := func(a, b int) int {
|
add := func(a, b int) int {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
var addCurry Fn = func(values ...any) any {
|
var addCurry CurryFn[int] = func(values ...int) int {
|
||||||
return add(values[0].(int), values[1].(int))
|
return add(values[0], values[1])
|
||||||
}
|
}
|
||||||
add1 := addCurry.Curry(1)
|
add1 := addCurry.New(1)
|
||||||
|
|
||||||
assert.Equal(3, add1(2))
|
assert.Equal(3, add1(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompose(t *testing.T) {
|
func TestCompose(t *testing.T) {
|
||||||
assert := internal.NewAssert(t, "TestCompose")
|
assert := internal.NewAssert(t, "TestCompose")
|
||||||
|
|
||||||
toUpper := func(a ...any) any {
|
toUpper := func(strs ...string) string {
|
||||||
return strings.ToUpper(a[0].(string))
|
return strings.ToUpper(strs[0])
|
||||||
}
|
}
|
||||||
toLower := func(a ...any) any {
|
toLower := func(strs ...string) string {
|
||||||
return strings.ToLower(a[0].(string))
|
return strings.ToLower(strs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := toUpper(toLower("aBCde"))
|
expected := toUpper(toLower("aBCde"))
|
||||||
|
|
||||||
cf := Compose(toUpper, toLower)
|
cf := Compose(toUpper, toLower)
|
||||||
res := cf("aBCde")
|
res := cf("aBCde")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user