【GO】GO语言学习笔记五

函数

1、函数的基本组成

  关键字func,函数名,参数列表,返回值,函数体,返回语句。

2、函数的定义

  举个栗子  

package mymath
import "errors"

func Add(a int, b int) (ret int, err error) {
	if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法
	    err= errors.New("Should be non-negative numbers!")
		return
	}
	return a + b, nil // 支持多重返回值
}

  如果参数列表中若干个相邻的参数类型的相同,比如上面例子中的a和b,则可以在参数列表 中省略前面变量的类型声明,如下所示:

func Add(a, b int)(ret int, err error) {
	// ...
}

  如果返回值列表中若干个相邻返回值类型相同,也可以如上面的方式合并

  如果只有一个返回值,还可以这样写

  

func Add(a,b int) int{
    //...
}

 3、函数的调用

函数的调用非常方便,只要事先引入了函数所在的包,就可以直接用下面的方式调用函数

import "mymath";

c := mymath.Add(a,b);

 注意:小写字母开头的函数,只在本包内可用,大写字母开头的函数才能被其他包使用,类型和变量也一样。 

 4、不定参数

a、不定参数类型

不定参数是指传入参数的个数为不定数量,要做到这一点,首先要把函数定义成接受不定参数的类型:

func myfunc (args ...int) int {
  for _,arg := range args {    fmt.Println(arg)  }}

 调用方式

myfunc(1,2,3)
myfunc(1,2,3,4,5)

  形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。它是一 个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。通 常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的机会。

从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为 什么上面的参数args可以用for循环来获得每个传入的参数。

b、不定参数的传递

func myfunc(args ...int) { // 按原样传递
    myfunc3(args...)
	// 传递片段,实际上任意的int slice都可以传进去
    myfunc3(args[1:]...)
}

c、任意类型不定参数

如果希望传递任意类型,可以把参数类型定义为interface{},看看fmt.Println的原型

func Println(format string, ...interface{}){
//...
}

 5、多返回值

如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单 地用一个下划线“_”来跳过这个返回值,比如下面的代码表示调用者在读文件的时候不想关心函数返回的错误码:

_,c := read();

6、匿名函数与闭包

匿名函数指不定义函数名的一种函数实现方式。

a、匿名函数

在GO里面,函数可以像普通变量一样传递,跟C的回调函数比较相似,不同的是,GO语言支持在代码里定义匿名函数

func (a,b int ,z float){
  return a*b < int(z)
}

 匿名函数可以直接赋值给一个变量或者直接执行:

f := func (a,b int){
     return a+b
}

func (ch chan char){
    return ch <- ACK
}(reply_chan)//函数后面直接跟参数列表表示函数调用

b、闭包

匿名函数就是一个闭包。

闭包基本概念:

闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者 任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含 在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环 境(作用域)。

闭包的价值:

闭包的价值在于可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要表示数据还要表示代码。支持闭包的多数语言都将函数作为第一级对象,就是说这些函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。

GO语言中的闭包:

Go语言中的闭包同样也会引用到函数外的变量。闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在,如下面代码所示:

package main

import (
    "fmt"
)

func main(){
      var j int = 5
      a := func() (func()){
            var i int = 10
            return func(){
                    fmt.Printf("i , j: %d , %d\n" , i , j)
            }
      }()
      a()
      j *= 2
     a()
}

  上面的执行结果是:

i , j: 10 , 5
     i , j: 10 , 10

在上面的例子中,变量a指向的闭包函数引用了局部变量i和j,i的值被隔离,在闭包外不 能被修改,改变j的值以后,再次调用a,发现结果是修改过的值。

在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量i,而无法通过其他途径访问到,因此保证了i的安全性。

 

 

  

 

  

时间: 2024-10-20 22:04:21

【GO】GO语言学习笔记五的相关文章

C语言学习笔记(五) 预处理符号

关于预处理的"常识" 预处理并不是编译,也不是"预编译". 预处理并不是每个语言都有的. C/C++预处理仅仅是把源程序划分和整理成一个个的段(phase),并不进行编译. 预处理器在UNIX传统中通常缩写为PP,在自动构建脚本中C预处理器被缩写为CPP的宏指代.为了不造成歧义,C++(c-plus-plus) 经常并不是缩写为CPP,而改成CXX. C语言预处理符号 ANSI C标准要求支持的预处理符号包括: #define.#undef(宏定义), #inclu

C语言学习笔记(五) 数组

数组 数组的出现就是为了解决大量同类型数据的存储和使用的问题: 数组的分类:一维数组.二维数组. 一维数组:为多个变量连续分配存储控件:所有的变量的数据类型必须相同:所有变量所占的字节大小必须相等: 例如:int a[4]; 一维数组名不代表数组中的所有元素而是代表数组中第一个元素的地址: 数组的初始化: 完全初始化: int a[4] = {1,2,3,4}; 不完全初始化: int a[4] = {1};  未被初始化的元素的值默认为0; 数组清零 int a[4] = {0}; 不初始化:

R语言学习笔记

參考:W.N. Venables, D.M. Smith and the R DCT: Introduction to R -- Notes on R: A Programming Environment for Data Analysis and Graphics,2003. http://bayes.math.montana.edu/Rweb/Rnotes/R.html 前言:关于R 在R的官方教程里是这么给R下注解的:一个数据分析和图形显示的程序设计环境(A system for data

Linux System Programming 学习笔记(五) 进程管理

1. 进程是unix系统中两个最重要的基础抽象之一(另一个是文件) A process is a running program A thread is the unit of activity inside of a process the virtualization of memory is associated with the process, the threads all share the same memory address space 2. pid The idle pro

小猪的数据结构学习笔记(五)

小猪的数据结构学习笔记(五) 线性表之--循环链表                           --转载请注明出处:coder-pig 循环链表知识点归纳: 相关代码实现: ①判断是否为空表: ②单循环链表的存储结构 其实和单链表的结构是一样的! /*定义循环链表的存储结构*/ typedef struct Cir_List { int data; struct Cir_List *next; }Lnode; ③初始化循环单链表 代码如下: //1.循环链表的初始化 //表示一个元素,如

Swift学习笔记五:循环和条件语句

一.循环语句 1. for循环 1) for -in 循环,对于数据范围,序列,集合等中的每一个元素,都执行一次 for a in 0...5{}    //循环迭代,从a=0到a=5,执行{}里的代码 注意:a只循环中存在,也就是出了{}a就不存在了 或者遍历数组 let a = [1,2,3] for b in a{} //循环迭代,从b=1到b=3 如果你不需要序列中的每一个值,可以使用_来忽略它,仅仅只是使用循环体本身: for _ in 0...5{}    //循环执行{}里的代码,

深度学习笔记(五):LSTM

深度学习笔记(一):logistic分类 深度学习笔记(二):简单神经网络,后向传播算法及实现 深度学习笔记(三):激活函数和损失函数 深度学习笔记(四):循环神经网络的概念,结构和代码注释 深度学习笔记(五):LSTM 看到一篇讲LSTM非常清晰的文章,原文来自Understanding LSTM Networks , 译文来自理解LSTM网络 Recurrent Neural Networks 人类并不是每时每刻都从一片空白的大脑开始他们的思考.在你阅读这篇文章时候,你都是基于自己已经拥有的

Lua学习笔记(五):函数

函数有两种用途: 1).完成指定的任务,这种情况下函数作为调用语句使用: 2).计算并返回值,这种情况下函数作为赋值语句表达式使用. 1 function func_name (arguments-list) 2 statements-list; 3 end; 4 5 --调用函数的时候,如果参数列表为空,必须使用()表明是函数调用 6 print(8*9, 9/8) 7 a = math.sin(3) + math.cos(10) 8 print(os.date()) 9 10 --上述规则有

Perl语言学习笔记 9 正则表达式处理文本

1.替换 s/PATTERN/REPLACE/; #返回是否替换成功的布尔值 可以使用捕获变量,如:s/(\w)/$1/ 匹配失败则不做任何处理 2.定界符 对于没有左右之分的定界符,重复三次即可,如:s///.s### 对于有左右之分的定界符,需使用两对,一对包含模式,一对包含替换字符串,这两对可以不一样,如:s{}{}.s[]{}.s<>[] 3.可选修饰符 /g可进行全局替换,替换所有匹配到的字符串,如:s/ / /g /s:使得 . 匹配所有字符 /i:大小写无关 4.绑定操作符 $f