• go函数作为一等民


    定义函数类型

    声明函数类型的变量和为变量赋值

    package main
    
    import "fmt"
    
    type Operation func(a, b int) int
    
    func Add(a, b int) int {
    	return a + b
    }
    
    func main() {
    	var op Operation
    	op = Add
    	fmt.Println(op(1, 2))
    }
    

    高阶函数

    函数作为其他函数入参

    package main
    
    import "fmt"
    
    type Operation func(a, b int) int
    
    func Add(a, b int) int {
    	return a + b
    }
    
    type Calculator struct {
    	v int
    }
    
    func (c *Calculator) Do(op Operation, a int) {
    	c.v = op(c.v, a)
    }
    
    func main() {
    	var calc Calculator
    
    	calc.Do(Add, 1)
    
    	fmt.Println(calc.v)
    }
    
    

    函数作为返回值加动态创建函数

    import "fmt"
    
    type Operation func(b int) int
    
    func Add(b int) Operation {
    	addB := func(a int) int {
    		return a + b
    	}
    
    	return addB
    }
    
    func main() {
    	add := Add(2)
    	i := add(3)
    	fmt.Println(i)
    }
    
    package main
    
    import "fmt"
    
    type Operation func(b int) int
    
    func Add(b int) Operation {
    	addB := func(a int) int {
    		return a + b
    	}
    
    	return addB
    }
    
    type Calculator struct {
    	v int
    }
    
    func (c *Calculator) Do(op Operation) {
    	c.v = op(c.v)
    }
    
    func main() {
    	var calc Calculator
    
    	calc.Do(Add(1))
    
    	fmt.Println(calc.v)
    }
    

    匿名函数

    package main
    
    import "fmt"
    
    type Operation func(b int) int
    
    func Add(b int) Operation {
    	return func(a int) int {
    		return a + b
    	}
    }
    
    

    闭包

    闭包是指有权访问另一个函数作用域中变量的函数,不需要传值。闭包一定是在一个函数内部的,但是闭包不等于匿名函数。

    一个函数可以是匿名函数,但可以不是闭包。

    闭包副作用

    func main() {
    	s1 := []int{1, 2, 3, 4}
    	
    	for i, v := range s1 {
    		go func() {
    			println(i, v)
    		}()
    	}
    
    	time.Sleep(time.Second)
    }
    

    输出

    3 4
    3 4
    3 4
    3 4
    

    这是为什么?

    因为没有传值,所有所有的goroutine都共享i, v,go的调度始终不会快过顺序执行的代码。

    如何修正?

    func main() {
    	s1 := []int{1, 2, 3, 4}
    
    	for i, v := range s1 {
    		go func(i, v int) {
    			println(i, v)
    		}(i, v)
    	}
    
    	time.Sleep(time.Second)
    }
    

    此时的输出顺序取决于go的调度了,但是会将s1内的元素都输出出来

    三个版本的p2p网络

    版本1:使用锁保护共享数据

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    type Peer struct {
    	ID string
    }
    
    func (p *Peer) WriteMsg(msg string) {
    	fmt.Printf("send to: %v, msg: %v
    ", p.ID, msg)
    }
    
    type Host struct {
    	peers map[string]*Peer
    	lock sync.RWMutex
    }
    
    func NewHost() *Host {
    	h := &Host{
    		peers: make(map[string]*Peer),
    	}
    	return h
    }
    
    func (h *Host) AddPeer(p *Peer) {
    	h.lock.Lock()
    	defer h.lock.Unlock()
    
    	h.peers[p.ID] = p
    }
    
    func (h *Host) GetPeer(pid string) *Peer {
    	h.lock.Lock()
    	defer h.lock.Unlock()
    
    	return h.peers[pid]
    }
    
    func (h *Host) RemovePeer(pid string) {
    	h.lock.Lock()
    	defer h.lock.Unlock()
    
    	delete(h.peers, pid)
    }
    
    func (h *Host) BroadcastMsg(msg string) {
    	h.lock.Lock()
    	defer h.lock.Unlock()
    
    	for _, p := range h.peers {
    		p.WriteMsg(msg)
    	}
    }
    
    

    版本2:使用channel传递数据

    package main
    
    import "fmt"
    
    type Peer struct {
    	ID string
    }
    
    func (p *Peer) WriteMsg(msg string) {
    	fmt.Printf("send to: %v, msg: %v
    ", p.ID, msg)
    }
    
    type Host struct {
    	add chan *Peer
    	broadcast chan string
    	remove chan string
    	stop chan struct{}
    }
    
    func NewHost() *Host {
    	h := &Host{
    		add: make(chan *Peer),
    		broadcast: make(chan string),
    		remove: make(chan string),
    		stop: make(chan struct{}),
    	}
    
    	return h
    }
    
    func (h *Host) Start() {
    	go h.loop()
    }
    
    func (h *Host) Stop() {
    	close(h.stop)
    }
    
    func (h *Host) loop() {
    	peers := make(map[string]*Peer)
    
    	for {
    		select {
    		case p := <-h.add:
    			peers[p.ID] = p
    		case pid := <-h.remove:
    			delete(peers, pid)
    		case msg := <-h.broadcast:
    			for _, p := range peers {
    				p.WriteMsg(msg)
    			}
    		case <-h.stop:
    			return
    		}
    	}
    }
    
    func (h *Host) AddPeer(p *Peer) {
    	h.add <- p
    }
    
    func (h *Host) RemovePeer(pid string) {
    	h.remove <- pid
    }
    
    func (h *Host) BroadcastMsg(msg string) {
    	h.broadcast <- msg
    }
    
    
    func main() {
    	
    }
    

    版本3:使用channel传递函数

    package main
    
    import "fmt"
    
    type Peer struct {
    	ID string
    }
    
    func (p *Peer) WriteMsg(msg string) {
    	fmt.Printf("send to: %v, msg: %v
    ", p.ID, msg)
    }
    
    type Operation func(peers map[string]*Peer)
    
    type Host struct {
    	opCh chan Operation
    	stop chan struct{}
    }
    
    func NewHost() *Host {
    	h := &Host{
    		opCh: make(chan Operation),
    		stop: make(chan struct{}),
    	}
    
    	return h
    }
    
    func (h *Host) AddPeer(p *Peer) {
    	add := func(peers map[string]*Peer) {
    		peers[p.ID] = p
    	}
    
    	h.opCh <- add
    }
    
    func (h *Host) RemovePeer(pid string) {
    	rm := func(peers map[string]*Peer) {
    		delete(peers, pid)
    	}
    
    	h.opCh <- rm
    }
    
    func (h *Host) BroadcastMsg(msg string) {
    	broadcast := func(peers map[string]*Peer) {
    		for _, p := range peers {
    			p.WriteMsg(msg)
    		}
    	}
    
    	h.opCh <- broadcast
    }
    
    func (h *Host) GetPeer(pid string) *Peer {
    	retCh := make(chan *Peer)
    	query := func(peers map[string]*Peer) {
    		retCh <- peers[pid]
    	}
    
    	go func() {
    		h.opCh <- query
    	}()
    
    	return <- retCh
    }
    
    func (h *Host) loop() {
    	peers := make(map[string]*Peer)
    
    	for {
    		select {
    		case op := <-h.opCh:
    			op(peers)
    		case <-h.stop:
    			return
    		}
    	}
    }
    

    友情提醒:这3种方式本身并无优劣之分,具体要用那种实现,要依赖自身的实际场景进行取舍。

    https://lessisbetter.site/2019/06/09/golang-first-class-function/

  • 相关阅读:
    C#中调用Outlook API 发起会议
    Log4Net配置
    web端调用Webapi获取Excel表格
    表格导出之Aspose.Cells
    验证输入框
    把新建的对象所有属性变成默认值
    省市区三级联动
    全局异常处理
    HttpHttpServletRequest / Reponse
    热部署
  • 原文地址:https://www.cnblogs.com/CherryTab/p/13213316.html
Copyright © 2020-2023  润新知