函数编程

仅当一个函数将一个或多个函数作为参数或作为结果返回另一个函数时,才可以将其视为高阶函数。

正宗的函数编程的概念是什么?

  • stateless:函数不维护任何状态。函数式编程的核心精神是stateless,简而言之就是它不能存在状态,打个比方,你给我数据我处理完扔出来。里面的数据是不变的。
  • immutable:输入数据是不能动的,动了输入数据就有危险,所以要返回新的数据集。

劣势:

数据复制比较严重。

注:有一些人可能会觉得这会对性能造成影响。其实,这个劣势不见得会导致性能不好。因为没有状态,所以代码在并行上根本不需要锁(不需要对状态修改的锁),所以可以拼命地并发,反而可以让性能很不错。比如:Erlang 就是其中的代表。reference

闭包函数

它的实际是:它允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程(对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联)–>所以闭包函数可以看作,就是一个类(它只有一个方法)的对象的方法。

example:DoTake()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
type tokenCallback func(pb.TokenServiceClient) error

var mockToken pb.TokenServiceClient

func tokenDo(callback tokenCallback) error {
	client := mockToken
	if client == nil {
		conn, err := grpc.Dial(TokenAddr, grpc.WithInsecure())
		if err != nil {
			return fmt.Errorf("Dial token service err: %v", err)
		}
		defer conn.Close()
		client = pb.NewTokenServiceClient(conn)
	}
	return callback(client)
}

func NewCloser(source, appID, UID string, expiry time.Duration, res *pb.Token, err error)*Closer{
	return &Closer{
		source:source,
		appID:appID,
		UID:UID,
		expiry:expiry,
		res:res,
		err:err,
	}
}

type Closer struct{
	source, appID, UID string; expiry time.Duration
	res *pb.Token; err error
}

func(this *Closer)XXX(client pb.TokenServiceClient)(err error){
	this.res, err = client.Create(context.Background(), &pb.CreateRequest{
		Subject:   fmt.Sprintf("%s,%s", this.source, this.appID),
		Uid:       this.UID,
		ExpiredAt: &timestamp.Timestamp{Seconds: time.Now().Add(this.expiry).Unix(), Nanos: 0},
	})
	return
}

func CreateToken(source, appID, UID string, expiry time.Duration) (res *pb.Token, err error) {
	// first: using class member function
	err = tokenDo(NewCloser(source, appID, UID, expiry, res, err).XXX)

	// second: using closer
	/*err = tokenDo(func(client pb.TokenServiceClient) (err error) {
	 	res, err = client.Create(context.Background(), &pb.CreateRequest{
	 		Subject:   fmt.Sprintf("%s,%s", source, appID),
	 		Uid:       UID,
	 		ExpiredAt: &timestamp.Timestamp{Seconds: time.Now().Add(expiry).Unix(), Nanos: 0},
	 	})
	 	return
	 })
	*/
	return
}

example:function Option

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
	"crypto/tls"
	"time"
)

type Server struct {
	Addr string
	Port int
	Protocol string
	Timeout time.Duration
	MaxConns int
	TLS *tls.Config
}

type Option func(*Server)

func Protocol(p string) Option {
	return func(s *Server) {
		s.Protocol = p
	}
}

func Timeout(timeout time.Duration) Option {
	return func(s *Server) {
		s.Timeout = timeout
	}
}

func MaxConns(maxconns int) Option {
	return func(s *Server) {
		s.MaxConns = maxconns
	}
}

func TLS(tls *tls.Config) Option {
	return func(s *Server) {
		s.TLS = tls
	}
}

func NewServer(addr string, port int,
	options ...func(*Server)) (*Server, error) {
	srv := Server{
		Addr: addr,
		Port: port,
		Protocol: "tcp",
		Timeout: 30 * time.Second,
		MaxConns: 1000,
		TLS: nil, }
	for _, option := range options {
		option(&srv) }
	//...
	return &srv, nil
}

func main() {
	s1, _ := NewServer("localhost", 1024)
	s2, _ := NewServer("localhost", 2048, Protocol("udp"))
	s3, _ := NewServer("0.0.0.0", 8080, Timeout(300*time.Second), MaxConns(1000))

	_ = s1
	_ = s2
	_ = s3
}

lazy evaluations

  • 示例:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package main

import (
	"fmt"
)

func add(x int) int {
	fmt.Println("executing add") // this is printed since the functions are evaluated first
	return x + x
}

func multiply(x int) int {
	fmt.Println("executing multiply") // this is printed since the functions are evaluated first
	return x * x
}

func addOrMultiply(add bool, onAdd, onMultiply int) int {
	if add {
		return onAdd
	}
	return onMultiply
}

func NotLazy(){
	fmt.Println(addOrMultiply(true, add(4), multiply(4)))  // 8
	fmt.Println(addOrMultiply(false, add(4), multiply(4))) // 16
}

func main() {
	NotLazy()
	fmt.Println("---")
	Lazy()
}

func Lazy() {
	fmt.Println(addOrMultiplyLazy(true, add, multiply, 4))
	fmt.Println(addOrMultiplyLazy(false, add, multiply, 4))
}

// This is now a higher-order-function hence evaluation of the functions are delayed in if-else
func addOrMultiplyLazy(add bool, onAdd, onMultiply func(t int) int, t int) int {
	if add {
		return onAdd(t)
	}
	return onMultiply(t)
}

// result
/*
executing add
executing multiply
8
executing add
executing multiply
16
---
executing add
8
executing multiply
16
*/

visitor model

visitor实战

currying(柯里化)

currying(柯里化) :将一个函数的多个参数分解成多个函数, 然后将函数多层封装起来,每层函数都返回一个函数去接收下一个参数,这可以简化函数的多个参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func add(x int) func(y int) int {
    return func(y int) int {
        return x + y
    }
}
func main() {
    // Create more variations
    add10 := add(10)
    add20 := add(20)

    // Currying
    fmt.Println(add10(1)) // 11
    fmt.Println(add20(1)) // 21
}

Map、Reduce 和 Filter

函数式语言有三套件,Map、Reduce 和 Filter。

因为没有泛型, 所以类型不能泛型,容器不能泛型,算法不能泛型。

1
2
3
4
5
6
7
8
9
template<class Iter, class T, class Op>
T reduce (Iter start, Iter end, T init, Op op) {
  T result = init;
  while ( start != end ) {
    result = op( result, *start );
    start++;
  }
  return result;
}

使用这个工具

  • Map
1
2
3
4
5
mapper := func (i interface{}) interface{} {
    return strings.ToUpper(i.(string))
}
Map(mapper, New(milu, rantanplan))
//[“MILU”, “RANTANPLAN”]
  • Filter
1
2
3
4
5
pred := func (i interface{}) bool {
    return i.(uint64) > 5
}
Filter(pred, Uint64(1,2,3,4,5,6,7,8,9,10))
//[6, 7, 8, 9, 10]
  • Reduce
1
2
3
4
5
acumullator := func (memo interface{}, el interface{}) interface{} {
    return len(memo.(string)) + len(el.(string))
}
Reduce(New(milu, rantanplan), acumullator, string).(uint64)
// result 14

装饰函数

所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出。

add print content

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func decorator(f func(s string)) func(s string) {
	return func(s string) {
		fmt.Println("Started")
		f(s)
		fmt.Println("Done")
	}
}

func Hello(s string) {
	fmt.Println(s)
}
func main() {
	decorator(Hello)("Hello, World!")
}

lazy evalutions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
	"fmt"
	"reflect"
	"runtime"
	"time"
)

type SumFunc func(int64, int64) int64

func getFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func timedSumFunc(f SumFunc) SumFunc {
	return func(start, end int64) int64 {
		defer func(t time.Time) {
			fmt.Printf("--- Time Elapsed (%s): %v ---\n",
				getFunctionName(f), time.Since(t))
		}(time.Now())
		return f(start, end)
	}
}

func Sum1(start, end int64) int64 {
	var sum int64
	sum = 0
	if start > end {
		start, end = end, start
	}
	for i := start; i <= end; i++ {
		sum += i
	}
	return sum
}

func Sum2(start, end int64) int64 {
	if start > end {
		start, end = end, start
	}
	return (end - start + 1) * (end + start) / 2
}

func main() {
	sum1 := timedSumFunc(Sum1)
	sum2 := timedSumFunc(Sum2)

	fmt.Printf("%d, %d\n", sum1(-10000, 10000000), sum2(-10000, 10000000))
}

附录

7 Easy functional programming techniques in Go

https://medium.com/@geisonfgfg/functional-go-bc116f4c96a4

Learning Functional Programming in Go