Go语言核心36讲笔记09--通道的高级玩法


1.单向通道

1.1什么是单向通道?

单向通道是只能发不能收或者只能收不能发的通道,一个通道是单向的还是双向的是由它的类型字面量体现的。通道的类型字面量中的<-表示的是通道的方向:

// 只能发不能收的通道
var uselessChan = make(chan<- int, 1)
// 只能收不能发的通道
var anotherUselessChan = make(<-chan int, 1)

1.2单向通道有什么应用价值

单向通道最主要的用途就是约束其他代码的行为,例如:Notifier接口中的SendInt方法只能接受一个发送通道作为参数,在该接口的所有实现类型中的SendInt方法都会受到限制。

type Notifier interface {
	SendInt(ch chan<- int)
}

func SendInt(ch chan<- int) {
	ch <- rand.Intn(1000)
}

2.select

(1)select基础知识

  • select语句只能与通道联用,每次执行时一般只有一个分支中的代码会被执行;
  • select语句的分支分为两种,分别是候选分支和默认分支,候选分支以关键字case开头,默认分支是defaul case;
  • select语句的每个case表达式中都只能包含操作通道的表达式;

(2)select使用注意点

  • 当select语句中有默认分支时,那么无论是否涉及通道操作的表达式是否有阻塞,select语句都不会被阻塞,如果没有满足求值的条件,那么默认分支就会被选中执行;
  • 如果select语句中没有默认分支,当所有的case表达式都没满足求值条件时select语句就会被阻塞,直到至少有一个case表达式满足条件为止;
  • select语句只能对每一个case表达式各求值一次,所以如果需要连续或者定时操作其中的通道,那么可以通过在for语句中嵌入select语句的方式实现,但此时需要注意简单地在select语句的分支中使用break语句只能结束当前select语句的执行,不会对外层的for语句产生作用;
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	example1()
	example2()
}

func example1() {
	intChannels := [3]chan int{
		make(chan int, 1),
		make(chan int, 1),
		make(chan int, 1),
	}

	index := rand.Intn(3)
	fmt.Printf("The index: %d\n", index)
	intChannels[index] <- index

	select {
	case <-intChannels[0]:
		fmt.Println("The first candidate case is selected.")
	case <-intChannels[1]:
		fmt.Println("The second candidate case is selected.")
	case elem := <-intChannels[2]:
		fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
	default:
		fmt.Println("No candidate case is selected!")
	}
}

func example2() {
	intChan := make(chan int, 1)

	time.AfterFunc(time.Second, func() {
		close(intChan)
	})

	select {
	case _, ok := <-intChan:
		if !ok {
			fmt.Println("The candidate case is closed.")
			break
		}
		fmt.Println("The candidate case is selected.")
	}
}

(3)select语句的分支选择规则

  • 对于每一个case表达式,都至少会包含一个代表发送操作的表达式或者一个代表接收操作的接收表达式,同时也可能会包含其他的表达式;
  • select语句包含的候选分支中的case表达式都会在该语句执行开始时先被求值,并且求值的顺序是依从代码编写的顺序从上到下的;
  • 对于每一个case表达式,如果其中的发送表达式或者接受表达式在被求值时,相应的操作正处于阻塞状态,那么对该case表达式的求值是不成功的;
  • 仅当select语句中的所有case表达式都被求值完成后,它才会开始选择候选分支,这时它只会挑选满足选择条件的候选分支执行,如果所有的候选分支都不满足选择条件,那么默认分支就会被执行;如果没有默认分支,那么select语句就会立即进入阻塞状态,直到至少有一个候选分支满足选择条件为止;
  • 如果select语句中同时有多个候选分支满足选择条件,那么它就会用一种伪随机的算法在这些分支中选择一个并执行。注意:即使select语句是在被唤醒时发现的这种情况也会这样做;
  • 一个select语句中有且只能有一个默认分支,并且默认分支只在无候选分支可选时才会被执行,这与其编写的位置无关;
  • select语句的每次执行,包括case表达式求值和分支选择,都是独立的;
package main

import "fmt"

var channels = [3]chan int{
	nil,
	make(chan int),
	nil,
}

var numbers = []int{1, 2, 3}

func main() {
	select {
	case getChan(0) <- getNumber(0):
		fmt.Println("The first candidate case is selected.")
	case getChan(1) <- getNumber(1):
		fmt.Println("The second candidate case is selected.")
	case getChan(2) <- getNumber(2):
		fmt.Println("The third candidate case is selected")
	default:
		fmt.Println("No candidate case is selected!")
	}
}

func getNumber(i int) int {
	fmt.Printf("numbers[%d]\n", i)
	return numbers[i]
}

func getChan(i int) chan int {
	fmt.Printf("channels[%d]\n", i)
	return channels[i]
}

3.思考题

(1)如果在select语句中发现某个通道已关闭,那么应该怎样屏蔽掉它所在的分支?
答:判断该通道是否已关闭,如果关闭了则将其重新赋值为nil,此外在select语句中搭配default使用则会保证select语句不会被阻塞

select {
    case _, ok := <-ch1:
        if !ok {
            ch1 = nil
        }
    case ... :
    ...
    default:
    ...
    }
}

(2)在select语句与for语句联用时,怎样直接退出外层的for语句?
答:有两种方式,方式一:使用break搭配标签方式,break到指定的循环体;方式二:使用goto语句跳转到指定标签处;

package main

import (
	"fmt"
	"time"
)

func main() {
	example1()
	example2()
}

func example1() {
	ch1 := make(chan int, 1)
	time.AfterFunc(time.Second, func() { close(ch1) })
loop:
	for {
		select {
		case _, ok := <-ch1:
			if !ok {
				break loop
			}
			fmt.Println("in")
		}
	}
	fmt.Println("out")
}

func example2() {
	ch1 := make(chan int, 1)
	time.AfterFunc(time.Second, func() { close(ch1) })

	for {
		select {
		case _, ok := <-ch1:
			if !ok {
				goto loop
			}
			fmt.Println("in")
		}
	}
loop:
	fmt.Println("out")
}

本节示例代码地址:demo Github

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
R语言实战笔记第九章介绍了方差分析的内容。方差分析是一种用于比较两个或多个组之间差异的统计方法。在R语言中,可以使用lm函数进行方差分析的回归拟合。lm函数的基本用法是: myfit <- lm(I(Y^(a))~x I(x^2) I(log(x)) var ... [-1],data=dataframe 其中,Y代表因变量,x代表自变量,a代表指数,var代表其他可能对模型有影响的变量。lm函数可以拟合回归模型并提供相关分析结果。 在方差分析中,还需要进行数据诊断,以确保模型的可靠性。其中几个重要的诊断包括异常观测值、离群点和高杠杆值点。异常观测值对于回归分析来说非常重要,可以通过Q-Q图和outlierTest函数来检测。离群点在Q-Q图中表示落在置信区间之外的点,需要删除后重新拟合并再次进行显著性检验。高杠杆值点是指在自变量因子空间中的离群点,可以通过帽子统计量来识别。一般来说,帽子统计量高于均值的2到3倍即可标记为高杠杆值点。 此外,方差分析还需要关注正态性。可以使用car包的qqplot函数绘制Q-Q图,并通过线的位置来判断数据是否服从正态分布。落在置信区间内为优,落在置信区间之外为异常点,需要进行处理。还可以通过绘制学生化残差的直方图和密度图来评估正态性。 综上所述,R语言实战第九章介绍了方差分析及其相关的数据诊断方法,包括异常观测值、离群点、高杠杆值点和正态性检验。这些方法可以用于分析数据的可靠性和模型的适应性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [R语言实战笔记--第八章 OLS回归分析](https://blog.csdn.net/gdyflxw/article/details/53870535)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值