Go基础-011 函数

1. 概念

函数,function,具有特定功能的代码块,称之为函数。通常函数可以接收外部数据称 之为函数参数,函数可以将运算结果返回,称之为函数的返回值。
定义函数的作用,为了将特定功能的代码块重用。体现程序设计上的封装性。
Go 应用程序有一个特殊的函数, main.main() 函数,入口函数,应用程序的初始执行点!

2. 声明语法

使用关键字 func 来定义。
语法如下:

func 函数标识符(形参列表) (返回值类型列表) { 
  函数体
}

其中,返回值类型列表,根据是否存在返回来确定是否使用。若仅仅存在一个返回值,可以 省略小括号。
也就是说,函数由:func 关键字,标识符(函数名)、参数、返回值类型、函数体构成。 其中:函数 func 关键字,形参列表、返回值类型以及函数体,构成了函数的字面量。
之所以称之为函数字面量,在 Go 语言中,函数也作为一种类型进行管理。而且是一种引用类型。

代码演示:

func F(p1 int, p2 string) string {
    return "" 
}

注意:函数在定义时,不能嵌套。func {func {}} 是错误的。

3. 调用函数

F() 的方式进行调用。
调用函数时,需要根据函数的需要传递参数给函数,同时根据需求得到函数的返回结果。
演示如下:

//调用函数
result := F(42, "hank") 
fmt.Println(result)

4. 函数标识符和匿名函数

满足标识符的要求。
函数标识符用于引用函数执行代码的地址,通过函数名,即可调用该函数。
函数名,不完全算作函数的一个部分,是一个查找机制。

在程序设计时,可以定义没有名字的函数,称之为匿名函数
匿名函数仅仅存在函数的字面量部分,需要将其立即调用或者存储到某个特定的变量中。 演示:

//匿名函数 

// 存储起来
f := func (p1 int, p2 string) string {
  return "return value" 
}
fmt.Printf("%T, %v\n", f, f)  // func(int, string) string,   f值是 0x49c5d0 
fmt.Println(f(42, "")) // return value

// 直接调用
result := func (p1 int, p2 string) string {
  return "return value" 
}(42, "hank")
fmt.Println(result) // return value


本例中, 输出的 func(int, string) string 称之为函数签名,也可以叫函数类型字面量。
函数签名,可以用来描述函数是否相同。
例如,本例中的具名函数 F() ,和 匿名函数变量 f,就是具有相同的函数签名,可以被视为同一个功能函数,指的是在 语法中可以通用。

5.参数

1) 概述

参数用于在调用函数时,向函数体内传递外部数据。

2)形参和实参

参数在描述时分为:形式参数(形参,paramter)和实际参数(实参,argument)。
形参:函数定义时使用的参数。在函数被调用时,被当做函数内的局部变量来处理。
实参:函数在被调用时,传递的参数。 在调用函数时,是使用实参为形参赋值的过程。

代码演示:

func F(p1 int, p2 string) string { 
  fmt.Println(p1, p2)
  return "return value"
}
result := F(42, "hank") // p1 = 42, p2 = "hank"

其中:
p1, p2 就是形参
42, “hank” 就是实参
调用函数时,使用 42 和 hank 为 p1 和 p2 赋值。

3)参数传递

调用函数时,使用 42 和 hank 为 p1 和 p2 赋值。
由于形参 p1, p2 类型不同,要求传递参数也是不同的。
典型的传参定义:

  • 常规类型,非引用类型,int,string,array。修改函数内的形参,不会影响函数外 的实参。
  • 引用类型 slice,map。修改函数内的形参,会导致函数外的实参改变。
  • 指针类型,*T。T类型的指针,通过*解析地址操作,修改形参数据,会影响到外
    部的实参。
func Func1(p1 int, p2 []int, p3 *int) { 
   p1 += 10
   p2[1] += 10
  *p3 += 20 
  fmt.Println(p1, p2, *p3) //  52 [1 12 3] 62
}
a1, a2, a3 := 42, []int{1, 2, 3}, 42 
Func1(a1, a2, &a3)
fmt.Println(a1, a2, a3)  // 42 [1 12 3] 62

注意结果,内部形参改变,切片和指针导致外部实参也随之改变,而常规类型 int,外部实参没有改变。

4)简洁类型语法

当多个参数的类型一致时候,可以在最后一个该类型参数后声明类型即可。
演示:

func Func2(p1, p2 int, p3 string) int {
  return 0 
}

与 p1 int, p2 int 一致。此时函数的签名还是:func(int, int, string) int 。没有省略第一个参 数参类型的描述。

5)剩余参数,不定数量参数

函数的最后一个形参,在定义的时候,可以在类型前使用剩余运算符定义,语法:
p ...T
注意:一定是最后一个参数。 剩余参数的作用,当调用函数时,可以传递不定数量该类型的参数。
此时形参接收的数据类型为 []T,T 类型的切片。 演示:

func Func3(op string, ps ...int) int { 
  fmt.Println(ps) 
  return 0 
}
Func3("plus")  // []
Func3("plus", 1)  // [1]
Func3("plus", 1, 2)  // [1 2]
Func3("plus", 1, 2, 3) // [1 2 3]

不定数量,0 个也可以。

当参数定义为不定数量时,传递实参时,也可以利用 ... 展开运算符,将同类型的数据展开传递,演示:

Func3("plus", []int{1, 2, 3, 4, 5}...)
func Func3(op string, ps ...int) int

此时相当于使用 []int{1, 2, 3, 4, 5} 为 ps 赋值的过程。(ps = []int{1, 2, 3, 4, 5} )

注意:
直接使用切片展开进行剩余参数赋值的操作,是两个变量间的直接赋值。
要求切片的类型和形参的类型完全一致。

思考:
fmt.Println() 的签名是:
func Println(a ...interface{}) (n int, err error)
参数 a,是空接口类型的切片。

问题是: 为什么不能 fmt.Println(slice...)的形式来调用。slice = []int{1, 2, 3, 4, 5}。

fmt.Println(1,2, 3, 4, 5) 
x := []int{1, 2 ,3, 4, 5} // 错误的语法
fmt.Println(x...) // 不同于 fmt.Println(1,2, 3, 4, 5)

原因是:
展开运运算 为 剩余参数赋值时,是直接变量间赋值的过程。Println 的形参 a,直接被赋值
x 赋值的过程。
a=x
该操作,就要去 a 和 x 的类型一致。当前不一致:
x = []int
a = []interface{}
[]interface{}空接口类型的切片不是 []int 整型切片类型。

6.返回值

1) 概述

函数的处理结果,为函数的返回值。
语法上,需要声明函数的返回值类型以及在函数体内使用 return 完成数据返回。

2)返回值类型声明

语法如下,有 4 种语法: 无返回值,单值返回,多值返回,命名返回。
代码演示:

// 无返回值 
func F4() {
}
// 单返回值
func F5() int {
  return 0 
}
// 亦可
//func F5() (int) {
// return 0
//}

// 多返回值
func F6() (int, string) {
  return 0, ""
}
// 命名返回
func F7() (result1 int) {
  return
}
func F8() (result1 int, result2 string) {
  return
}

函数的返回值声明,在函数参数列表后,使用括号进行包裹声明,声明的部分可以由返回值变量及类型构成。其中返回值变量可以省略,在函数内处理即可。同时若仅存在一个返回值 类型声明,可以省略括号部分。

注意: 函数签名(函数类型字面量)部分 包含返回值类型声明部分。(不包含返回值变量名部分)

3)return 语句

函数体内的 return 语句,用于完成返回值处理。 两种语法:

  • 返回特定数据表达式
  • 独立的return,通常同于命名返回值上
    演示:
func F6() (int, string) {
  return 0, "" 
}
func F8() (result1 int, result2 string) {
  return
}

若函数声明了返回值,则需要强制使用 return 语句。而且是可检测到一定执行的 return 语句。 例如,下面的语法错误:

func F6() (int, string) {
  //return 0, ""
} // missing return at end of function

可检测到的 return,指的是 return 必须可能执行到。在 条件判断语句体内的 return,也会被认为没有 return:

func F9() (int, string) { 
  cond := true
  if cond {
    return 0, "" 
  }
} // missing return at end of function

上面语法的处理应该在最后,再加一个 return:

func F9() (int, string) { 
  cond := true
  if cond {
    return 0, "" 
  }
    return 0, "" 
} 

要求 return 后,没有其他语句了。return 为最后一条语句。
因为 return 表示函数运行结束, 后续的语句没有任何意义。反过来说,即使执行了后续的语句,函数就没有正确的返回。演示:

func F9() (int, string) { 
  cond := true
  if cond {
    return 0, "" 
  }
  return 0, ""
  fmt.Println("after return")
} // missing return at end of function
4)具名返回(命名返回)

在声明返回值时,同时指定了返回变量。
该变量,相当于已经生命好的函数内的局部变量(类似于形参),在函数结束时,直接使用简单的 return 即可完成返回。

代码演示:

func F8() (result1 int, result2 string) {
  return
}

命名返回的优势在于可以在返回值声明的位置,通过变量名确定返回值意义。 例如下面的函数定义,返回为一个长度和一个标题

func F11() (int, string) {
  return 0, "" 
}

func F12() (length int, title string) {
  return
}
5)返回引用

返回值也可以是引用类型。

func F13() *int { 
   result := 42
   return &result
}
result := F13() 
fmt.Println(result, *result) // 0xc00000a0c0 42

注意:若返回值为当前函数内变量的引用形式,意味着在函数运行结束后,该被应用的变量 值空间,还会被继续使用。此时函数已经运行结束,函数内所控制的资源变量,已经被释放。
该情况的处理,Go 采用了【栈逃逸】的机制。

栈逃逸: 当需要在函数外使用函数内的变量资源时,为函数内的相应变量分配空间时,不会
使用常规的函数调用栈空间,而是在栈外开辟空间存储,该策略称之为栈逃逸。

7.调用栈

函数,在被调用时,会在内存的函数调用栈,为函数开辟空间,将函数的相关数据进行
存储,例如函数内的局部变量(包括形参,函数内声明的参数,命名返回值变量)。

栈:先进后出的一种结构。

如图所示:


注意:当函数运行完毕,对应函数调用栈,会被释放,内部的资源会全部回收!

8.递归调用

1)概述

调用函数的一种方案,指的是,在函数内部调用函数本身,称之为递归调用。
在函数体的某条语句中,完成了对自身的调用。
是迭代(循环)执行一种方案。
用于解决,一个复杂的问题,可以拆解成规模小且方案一致的简单问题。方案一致,意味着需要使用相同的方法进行解决,使用相同的函数进行解决。当问题的规模足够小时,解决方案显而易见。
因此,将大规模问题,逐步拆解成小规模问题,逐一解决小规模问题,将解决的结果向 上集合,进而将大问题解决。

分为:分,治,合,三个步骤。拆分,治理,合并。

例如:遍历某个目录下的全部(包含子目录)内容。
语法演示(伪代码):

func DirRead(path string) {
  for file := readFile(path) {
      print(file)
      if is_dir(file) {
        DirRead(file)
      }
  }

}

编写递归时,主要考虑:

  • 是否可以使用递归编程?是否可以大规模拆解为相同算法的小规模?
  • 递归调用的条件(递归点)。
  • 递归的出口在哪里?当问题拆分到什么程度,不再需要继续拆分了?
2)递归计算阶乘

(阶乘更好的方案,应该是循环结构,此处为了演示递归) 阶乘:

5! = 5 * 4 * 3 * 2 *1
6! = 6 * 5 * 4 * 3 * 2 *1 = 6 * 5!
N! = N * (N-1) * .... * 2 * 1

总结:
N! = N * (N-1)!

可见,当需要求解阶乘问题时,出现了大规模问题可以拆解成小规模问题的现象: N 的问题,可以拆解长 N-1 的问题。
可以使用递归编程来实现。

  • 递归的条件:
    直接计算 N-1 的阶乘,递归调用即可。

  • 递归的出口:
    当 N==1 时,阶乘为 1。1! == 1, 不需要继续递归了,有出口。

  • 编程实现:

func Factorial(n int) int {
    if 1 == n {  // 递归出口,不需要继续递归了
      return 1  // 该规模的问题,可以直接解决。 
   }
    return n * Factorial(n - 1) 
}

fmt.Println(Factorial(1))  // 1
fmt.Println(Factorial(2))  // 2 
fmt.Println(Factorial(3))  // 6
fmt.Println(Factorial(4))  // 24
fmt.Println(Factorial(5))  // 120
fmt.Println(Factorial(6))  // 720

分析该函数的执行过程。如下:

Factorial(5)
// return 5 * Factorial(5-1)
// return 5 * (return 4 * Factorial(4-1))
// return 5 * (return 4 * (return 3 * Factorial(3-1)))
// return 5 * (return 4 * (return 3 * (return 2 * Factorial(2-1)))) 
// return 5 * (return 4 * (return 3 * (return 2 * 1)))
// return 5 * (return 4 * (return 3 * 2))
// return 5 * (return 4 * 6)
// return 5 * 24
// 120

可见,最外层的 return 一直在等,内部的调用结束。在函数的调用栈中,会存在很多 Factorial()的栈,如图所示:

3)递归计算斐波那契数列第 N 项的值

(递归编程的演示, 本例还是建议使用循环结构)

斐波那契,前两项已知一般是 1,1,从第三项开始,为前两项的和。
F(3) = F(3-1) + F(3-2)
1, 1, 2, 3, 5, 8, 13, 21 ....

要求定义函数,计算第 N 项斐波那契数的值。

分析:
大规模可以拆解成思路一致的小规模。
递归点在于 N-1 和 N-2 上。
出口:前两项已知,n==1, n==2 不用递归实现。

编程实现:

func Fibonacci(n int) int { 
  //出口
  if 1 == n {
    return 1 
  }
  if 2 == n {
    return 1 
  }
  // 递归计算
  return Fibonacci(n-1) + Fibonacci(n-2)
} 
fmt.Println(Fibonacci(1)) 
fmt.Println(Fibonacci(2)) 
fmt.Println(Fibonacci(3)) 
fmt.Println(Fibonacci(4)) 
fmt.Println(Fibonacci(5)) 
fmt.Println(Fibonacci(6)) 
fmt.Println(Fibonacci(7)) 
fmt.Println(Fibonacci(8)) 
fmt.Println(Fibonacci(9)) 
fmt.Println(Fibonacci(10)) 
fmt.Println(Fibonacci(11))
4)处理树状结构

树状结构,是比较典型的嵌套结构。例如,文件夹的关系,分类的关系,以及 word 的
标题层次 都是典型的树状结构。

典型的树状结构,可以处理无限层次。

树状结构数据的存储,很难是直接的树状结构,通常都是在数据库里的并列的表格结构。 业务逻辑中,将表格数据,维护成树状的结构,典型的程序。例如:遍历文件夹中的全部文件,做产品的无限极分类。都是类似的结构。

演示分类的处理:
输入数据,并列的表格数据.
如下演示: 每个分类有三个字段,标题,ID,上级分类 ID。

type Category struct {
   ID int // ID
   Title string // 标题
   PID int // 上级分类 ID
}

categories := []Category {
  Category{1, "电脑", 0}, 
  Category{2, "服装", 0}, 
  Category{3, "手机", 0}, 
  Category{4, "图书", 0}, 
  Category{5, "手机通讯", 3}, 
  Category{6, "智能设备", 3}, 
  Category{7, "手机配件", 3},
  Category{8, "手机壳", 7},
  Category{9, "数据线", 7}, 
  Category{10, "支架", 7}, 
  Category{11, "type-c", 9}, 
  Category{12, "usb", 9}, 
  Category{13, "light", 9}, 
  Category{14, "教材", 4}, 
  Category{15, "文学", 4}, 
  Category{16, "科技", 4}, 
  Category{17, "中小学", 14}, 
  Category{18, "外语词典", 14}, 
  Category{19, "男装", 2},
}

基于该数据,完成树状结构或嵌套结构的获取。
利用递归编程实现。

  • 可以用递归的原因:
    上级分类检索下级分类的大规模问题,可以拆解为下级分类再检索 下下级分类的问题。大规模拆解成小规模。
  • 实现思路:
    利用 PID 检索某个分类下的子分类,当检索到某个子分类后,递归基于该子分类继续 检索后代分类。检索的过程就是遍历全部分类的过程。

演示: 增加一个数据类型,用于记录分类及其层级,层级用来标识当前分类的缩进深度。

func Tree(dataList []Category, id, l int) (tree []CategoryTree) {
  // 遍历全部的 category
  for _, c := range dataList {
  // 基于 PID 判断是否为当所检索 Id 的子分类 
  if c.PID == id {
      // 是 子分类 
      // 1 记录下来
      tree = append(tree, CategoryTree{ 
        Category: c,
        Level: l, 
      })
      // 2 递归检索,将所到当前分类的子分类,将其追加到结果之后。 
      tree = append(tree, Tree(dataList, c.ID, l+1)...)
  } }
  return
}

此时得到的返回值就是带有层级的分类树。

需要时可以根据层级做缩进展示:

categoryTree := Tree(categories, 0, 0) 
for _, c := range categoryTree {
  fmt.Print(strings.Repeat(" ", c.Level * 2))
  fmt.Println(c.Category.Title) 
}

输出样式

电脑 
服装
    男装 
手机
   手机通讯
   智能设备
   手机配件
      手机壳 
      数据线
 图书
    教材 
    中小学
    外语词典 
    文学
  ...... 省略
5)嵌套结构

对于该层级分类结构,另一种典型的数据格式为嵌套格式,在当前分类上记录其后代分类。
结构如下:

//得到的数据模拟
var nested = []CategoryNested{
     CategoryNested {
       Category: Category{3, "手机", 0},
       Children: []CategoryNested{
            {
              Category: Category{5, "手机通讯", 3}, 
              Children: nil,
            }, 
            {
              Category: Category{7, "手机配件", 3}, 
              Children: []CategoryNested{
                }, 
            },
        }, 
    },
}

该结构的生成,也是典型的递归程序,实现如下:

func Nested(dataList []Category, id int) (nested []CategoryNested){
    for _, c := range dataList { 
        if c.PID == id {
          // 继续检索其后代分类
          nested = append(nested, CategoryNested{ 
            Category: c,
            Children: Nested(dataList, c.ID),
            }) 
        }
    }
    return
}
  categoryNested := Nested(categories, 0) 
  fmt.Println(categoryNested)

得到的结果:

[{{1 电脑 0}[]}{{2 服装 0}[{{19 男装 2}[]}]}{{3 手机 0}[{{5 手机 通讯 3}[]}{{6 智能设备 3}[]}{{7 手机配件 3}[{{8 手机壳 7}[]}{{9 数 据线 7} [{{11 type-c 9} []} {{12 usb 9} []
} {{13 light 9} []}]} {{10 支架 7} []}]}]} {{4 图书 0} [{{14 教材 4} [{{17 中 小学 14} []} {{18 外语词典 14} []}]} {{15 文学 4} []} {{16 科技 4} []}]}]

展示时,通常配合多层循环完成展示,例如:

categoryNested := Nested(categories, 0) 
//fmt.Println(categoryNested)
for _, c := range categoryNested {
      fmt.Println(c.Category.Title)
      // 判断是否存在子分类
      if len(c.Children) > 0 {
          for _, cc := range c.Children {
              fmt.Println(cc.Category.Title)
              // 继续
              if len(cc.Children) > 0 {
                  for _, ccc := range cc.Children {
                        fmt.Println(ccc.Category.Title) 
                  }
              } 
          }
      } 
}

可见,下列展示:


或者也有类似目录那种多级的 缩进结构展示

. 9 延迟调用

常规情况下,函数调用后立即执行。可以利用 defer 关键字,将函数的执行延迟当所在
在函数的最后执行。
演示:

fmt.Println("before F14") 
defer F14() 
fmt.Println("after F14")
func F14() {
  fmt.Println("F14 is running.")
}
//输出:
//before F14 
//after F14
//F14 is running.

延迟调用,与函数本身无关,仅仅是调用机制问题。

主要用在关闭一些资源上,例如打开的数据库连接,文件句柄等。

一般来说,资源用完需要关闭。为了防止当操作资源后,忘记关闭资源,通常的做法都 是打开资源后立即使用 defer 将其关闭,不会立即关闭,可以保证函数执行完毕,一定会关闭。将打开与关闭写在一起,演示:

func F15 () { 
  // 操作文件
  // 打开文件
  handle, _ := os.Open("./if.go") 
  // 延迟关闭
  defer handle.Close()
  // 文件操作
  // 操作结束 
}

defer 延迟调用,接收的参数为调用时的参数值,而不是运行时的参数值,演示:

func F14(n int) {
fmt.Println(n, " ", "F14 is running.") }
fmt.Println("before F14") 
a := 42
defer F14(a)
a = 1024
fmt.Println(a, " ", "after F14")
// 输出
// before F14
// 1024 after F14
// 42 F14 is running.

注意: F14 运行时 n 的值为 42 而不是更新后的 1024. 因为在 defer 是会获取参数的拷贝,传递给函数。

多个 defer 的执行顺序,先注册的 defer 后执行,因为函数会维护一个 defer 栈,栈,先 进后出结构。(后进先出)。

defer F14()
defer F16()
// 输出
// f16 is running. 
// F14 is running.

defer 三点:

  • 延迟到函数结束时调用
  • 接收的参数为调用时的参数拷贝
  • 先defer后调用,后defer先调用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,727评论 2 9
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,771评论 1 10
  • 函数 函数是执行特定任务的自包含代码块。给定函数一个名称作为标识,并在需要的时候通过调用其名称来执行任务。 Swi...
    BoyceC阅读 414评论 0 1
  • 第5章 函数和函数式编程 5.1 引言函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数...
    VIVAFT阅读 943评论 0 5
  • #1.函数基础1.1 局部对象1.2 函数声明1.3 分离式编译 #2.参数传递2.1 传值参数2.2 传引用参数...
    MrDecoder阅读 583评论 0 1