From 72a89be8c1450d77c526aab523b8c02187ea9e80 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Sat, 11 Dec 2021 13:30:11 +0800 Subject: [PATCH] feat: add function package for funcational programming --- function/function.go | 72 +++++++++++++++++++++++++++++ function/function_test.go | 95 +++++++++++++++++++++++++++++++++++++++ function/function_util.go | 23 ++++++++++ 3 files changed, 190 insertions(+) create mode 100644 function/function.go create mode 100644 function/function_test.go create mode 100644 function/function_util.go diff --git a/function/function.go b/function/function.go new file mode 100644 index 00000000..873579e9 --- /dev/null +++ b/function/function.go @@ -0,0 +1,72 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package function implements some functions for functional programming. + +package function + +import ( + "reflect" + "time" +) + +// After creates a function that invokes func once it's called n or more times +func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value { + return func(args ...interface{}) []reflect.Value { + n-- + if n < 1 { + return invokeFunc(fn, args...) + } + return nil + } +} + +// Before creates a function that invokes func once it's called less than n times +func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value { + var res []reflect.Value + + return func(args ...interface{}) []reflect.Value { + if n > 0 { + res = invokeFunc(fn, args...) + } + if n <= 0 { + fn = nil + } + n-- + return res + } +} + +type Fn func(...interface{}) interface{} + +// Curry make a curryed function +func (f Fn) Curry(i interface{}) func(...interface{}) interface{} { + return func(values ...interface{}) interface{} { + v := append([]interface{}{i}, values...) + return f(v...) + } +} + +// Delay make the function excution after delayed time +func Delay(delay time.Duration, fn interface{}, args ...interface{}) { + time.Sleep(delay) + invokeFunc(fn, args...) +} + +// Schedule invoke function every duration time, util close the returned bool chan +func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool { + quit := make(chan bool) + + go func() { + for { + invokeFunc(fn, args...) + select { + case <-time.After(d): + case <-quit: + return + } + } + }() + + return quit +} diff --git a/function/function_test.go b/function/function_test.go new file mode 100644 index 00000000..93f271cd --- /dev/null +++ b/function/function_test.go @@ -0,0 +1,95 @@ +package function + +import ( + "fmt" + "reflect" + "testing" + "time" +) + +func TestAfter(t *testing.T) { + arr := []string{"a", "b"} + f := After(len(arr), func(i int) int { + fmt.Println("print done") + return i + }) + type cb func(args ...interface{}) []reflect.Value + print := func(i int, s string, fn cb) { + fmt.Printf("print: arr[%d] is %s \n", i, s) + v := fn(i) + if v != nil { + vv := v[0].Int() + if vv != 1 { + t.FailNow() + } + } + } + fmt.Println("print: arr is", arr) + for i := 0; i < len(arr); i++ { + print(i, arr[i], f) + } +} + +func TestBefore(t *testing.T) { + arr := []string{"a", "b", "c", "d", "e"} + f := Before(3, func(i int) int { + return i + }) + + var res []int64 + type cb func(args ...interface{}) []reflect.Value + appendStr := func(i int, s string, fn cb) { + fmt.Printf("appendStr: arr[%d] is %s \n", i, s) + v := fn(i) + res = append(res, v[0].Int()) + } + + for i := 0; i < len(arr); i++ { + appendStr(i, arr[i], f) + } + + expect := []int64{0, 1, 2, 2, 2} + if !reflect.DeepEqual(expect, res) { + t.FailNow() + } +} + +func TestCurry(t *testing.T) { + add := func(a, b int) int { + return a + b + } + var addCurry Fn = func(values ...interface{}) interface{} { + return add(values[0].(int), values[1].(int)) + } + + add1 := addCurry.Curry(1) + v := add1(2) + if v != 3 { + t.FailNow() + } +} + +func TestDelay(t *testing.T) { + var print = func(s string) { + fmt.Println(s) + } + Delay(2*time.Second, print, "test delay") +} + +func TestSchedule(t *testing.T) { + var res []string + appendStr := func(s string) { + fmt.Println(s) + res = append(res, s) + } + + stop := Schedule(1*time.Second, appendStr, "*") + time.Sleep(5 * time.Second) + close(stop) + + expect := []string{"*", "*", "*", "*", "*"} + if !reflect.DeepEqual(expect, res) { + t.FailNow() + } + fmt.Println("done") +} diff --git a/function/function_util.go b/function/function_util.go new file mode 100644 index 00000000..b809c566 --- /dev/null +++ b/function/function_util.go @@ -0,0 +1,23 @@ +package function + +import ( + "fmt" + "reflect" +) + +func invokeFunc(fn interface{}, args ...interface{}) []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 functionValue(function interface{}) reflect.Value { + v := reflect.ValueOf(function) + if v.Kind() != reflect.Func { + panic(fmt.Sprintf("Invalid function type, value of type %T", function)) + } + return v +}