首页 > 其他分享 >Go语言精进之路读书笔记第21条——让自己习惯于函数是"一等公民"

Go语言精进之路读书笔记第21条——让自己习惯于函数是"一等公民"

时间:2024-02-13 11:00:31浏览次数:40  
标签:return 函数 读书笔记 GOROOT go func Go http 21

21.1 什么是"一等公民"

(1) 正常创建

// $GOROOT/src/fmt/print.go
func newPrinter() *pp {
    p := ppFree.Get().(*pp)
    p.panicking = false
    p.erroring = false
    p.wrapErrs = false
    p.fmt.init(&p.buf)
    return p
}

(2) 在函数内创建,定义匿名函数赋值给变量

// $GOROOT/src/runtime/print.go
func hexdumpWords(p, end uintptr, mark func(uintptr) byte) {
    p1 := func(x uintptr) {
        var buf [2 * sys.PtrSize]byte
        for i := len(buf) - 1; i >= 0; i-- {
            if x&0xF < 10 {
                buf[i] = byte(x&0xF) + '0'
            } else {
                buf[i] = byte(x&0xF) - 10 + 'a'
            }
            x >>= 4
        }
        gwrite(buf[:])
    }
    ...
}

(3) 作为类型

// $GOROOT/src/net/http/server.go
type HandlerFunc func(ResponseWrite, *Request)

(4) 存储到变量中,可以将定义好的函数存储到一个变量中

// $GOROOT/src/runtime/vdso_linux.go
func vdsoParseSymbols(info *vdsoInfo, version int32) {
    ...
    apply := func(symIndex uint32, k vdsoSymbolKey) bool {
        sym := &info.symtab[symIndex]
        typ := _ELF_ST_TYPE(sym.st_info)
        bind := _ELF_ST_BIND(sym.st_info)
        ...
        *k.ptr = info.loadOffset + uintptr(sym.st_value)
        return true
    }
    ...
}

(5) 作为参数传入函数

// $GOROOT/src/time/sleep.go
func AfterFunc(d Duration, f func()) *Timer {
    t := &Timer{
        r: runtimeTimer{
            when: when(d),
            f:    goFunc,
            arg:  f,
        },
    }
    startTimer(&t.r)
    return t
}

(6) 作为返回值从函数返回

// $GOROOT/src/strings/strings.go
func makeCutsetFunc(cutset string) func(rune) bool {
    if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
        return func(r rune) bool {
            return r == rune(cutset[0])
        }
    }
    if as, isASCII := makeASCIISet(cutset); isASCII {
        return func(r rune) bool {
            return r < utf8.RuneSelf && as.contains(byte(r))
        }
    }
    return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
}

除了上面那些例子,函数还可以被放入数组、切片或map等结构中,可以被赋值给interface{},可以建立元素为函数的channel

21.2 函数作为"一等公民"的特殊运用

1.像对整型变量那样对函数进行显式类型转换

func greeting(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Welcome, Gopher!\n")
}

func main() {
    http.ListenAndServe(":8080", http.HandlerFunc(greeting))
    //http.ListenAndServe(":8080", greeting)
}

// $GOROOT/src/net/http/server.go
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

...

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

...

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP调用f(w, r)
func (f HandlerFunc) ServeHttp(w ResponseWriter, r *Request) {
    f(w, r)
}

之所以http.HandlerFunc(greeting)可以通过编译器检查,是因为HandlerFunc的底层类型是func(ResponseWriter, *Request),与greeting的原型是一致的

2.函数式编程

(1) 柯里化函数

函数柯里化(currying)是把接受多个参数的函数变换成接受一个单一参数(原函数的第一个参数)的函数,并返回接受余下的参数和返回结果的新函数的技术。

闭包是在函数内部定义的匿名函数,并且允许该匿名函数访问定义它的外部函数的作用域

(2) 函子(functor)

函子需要满足两个条件:

  • 函子本身是一个容器类型,以Go语言为例,这个容器可以是切片、map甚至channel;
  • 该容器类型需要实现一个方法,该方法接受一个函数类型参数,并在容器的每个元素上运用那个函数,得到一个新函子,原函子容器内部的元素值不受影响

函子非常适合用来对容器集合元素进行批量同构处理,而且代码也比每次都对容器中的元素进行循环处理要优雅、简洁需要多。
还需要Go对泛型提供支持,否则就要为每一种容器类型都实现一套对应的Functor机制。

(3) 延续传递式(Continuation-passing Style,CPS)

在CPS风格中,函数是不允许有返回值的。一个函数A应该将其想返回的值显式传给一个continuation函数(一般接受一个参数),而这个continuation函数自身是函数A的一个参数。

CPS需要语言支持尾递归优化,避免代码执行效率不高的问题

标签:return,函数,读书笔记,GOROOT,go,func,Go,http,21
From: https://www.cnblogs.com/brynchen/p/18014395

相关文章

  • Go语言精进之路读书笔记第20条——在init函数中检查包级变量的初始状态
    20.1认识init函数init函数的特点:运行时调用,Go程序中不能显式调用顺序执行,等待一个init函数执行完毕并返回后再执行下一个init函数在整个Go程序生命周期内仅会被执行一次先被传递给Go编译器的源文件中的init函数先被执行,同一个源文件中的多个init函数按声明顺序依次执行。但......
  • P2178 [NOI2015] 品酒大会
    题意简述定义后缀\(p,q\)是\(r\)相似的当且仅当\(\forall1\lei\ler,s_{p+i-1}=s_{q+i-1}\)。对于每一个\(0\ler<n\),求出:有多少对\(r\)相似的后缀。每个后缀有权值\(a_i\),求在所有\(r\)相似的后缀对\((p,q)\)中\(a_p\cdota_q\)的最大值。若不存在则答案为......
  • D. Good Trip
    原题链接题解1.把分数中的除法用乘以逆元表示,在求模运算里的除法都可以用乘以逆元代替(如果除法的结果为整数),但是这里规定了可以用其表示,那就用其表示2.读题code#include<bits/stdc++.h>intmod=1e9+7;//确保mod是一个整数usingnamespacestd;//快速幂函数,计算base......
  • python3.9 + django4.1 + vue3 ,报错,无法访问配置的路由地址,Using the URLconf defined
    python3.9+django4.1+vue3,报错,无法访问配置的路由地址,UsingtheURLconfdefinedinStudentMgrBE.urls,DjangotriedtheseURLpatterns,inthisorder:-------------------------------------------------------------------------------无法访问 地址,报错如下: Us......
  • 二十三、Django之Form组件
    Django的Form:1、对用户请求的验证2、生成HTML代码a、创建一个类b、类中创建字段(包含正则表达式)c、Geta)Obj=Fr()obj.user=>自动生成HTMLd、POSTa)Obj=Fr(request,POST)i.Ifobj.is_valid():Obj.cleaned_dataElse:......
  • 二十四、Django Serializes
    SerializersDjango中,自定义类型的对象无法通过json序列化,可以使用serializers。defget_data(request):#由于UserInfo是自定义对象,不能通过json序列化#因此使用以下方式fromdjango.coreimportserializersret={'status':True,'data':None}try:......
  • 十八、Django之Http
    1、Django请求的生命周期请求响应Http1、发送Http请求请求头(包含Cookie)\r\n\r\n请求体2、服务器请求,根据请求头中的url在路由关系表中进行匹配(从上到下)3、匹配成功后,执行指定的views函数a.Url->函数==>FBVb.Url->类==>CBV4、响应内......
  • 8小时速成golang(四)语法新奇
      1、从一个main函数初见golang语法packagemainimport"fmt"funcmain(){/*简单的程序万能的helloworld*/fmt.Println("HelloGo")}终端运行$goruntest1_hello.goHelloGo$gorun表示直接编译go语言并执行应用......
  • 十六、Django的ORM(二)
    1、DecimalFieldDecimalField保存浮点型数据比FloatField精确,因为它是以字符串来保存,而FloatField,数据越长,保存得越不精确。2、索引(命中索引)正确使用SQL语言,使查找数据时,用到索引username=models.CharField(...#db_index=True数据库中字段是否可以建立......
  • 8小时速成golang(三)Golang语言特性
    golang的优势一、极其简单的部署方式1、可直接编译成机器码2、不需要依赖其他库3、直接运行即可部署 二、静态语言类型可在编译的时候检查出大多隐藏问题 三、语言层面的并发1、天生的基因支持2、充分利用多核packagemainimport("fmt""time")fu......