Skip to content

Commit

Permalink
feat: add function package for funcational programming
Browse files Browse the repository at this point in the history
  • Loading branch information
duke-git committed Dec 11, 2021
1 parent 0cf5932 commit 72a89be
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
72 changes: 72 additions & 0 deletions function/function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2021 [email protected]. 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
}
95 changes: 95 additions & 0 deletions function/function_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
23 changes: 23 additions & 0 deletions function/function_util.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 72a89be

Please sign in to comment.