go中如何更好的迭代
三种迭代方式
有如下三种迭代的写法:
- 回调函数方式迭代
- 通过Next()方法迭代。参照python 迭代器的概念,自定义Next()方法来迭代
- 通过channel实现迭代。
假设实现迭代从[2, max],打印出偶数。
func printEvenNumbers(max int) {
if max < 0 {
log.Fatalf("'max' is %d, should be >= 0", max)
}
for i := 2; i <= max; i += 2 {
fmt.Printf("%d\n", i)
}
}
回调函数的做法
// 将迭代的数值传递到回调函数
func printEvenNumbers(max int) {
err := iterateEvenNumbers(max, func(n int) error {
fmt.Printf("%d\n", n)
return nil
})
if err != nil {
log.Fatalf("error: %s\n", err)
}
}
// 实际的迭代的结果,接受一个回调函数,由回调函数处理
func iterateEvenNumbers(max int, cb func(n int) error) error {
if max < 0 {
return fmt.Errorf("'max' is %d, must be >= 0", max)
}
for i := 2; i <= max; i += 2 {
err := cb(i)
if err != nil {
return err
}
}
return nil
}
Next()方法的迭代
// Next()方法放在for循环体之后,通过返回布尔值来控制是否迭代完毕
func (i *EvenNumberIterator) Next() bool
// Value()方法返回当次迭代的值
func (i *EvenNumberIterator) Value() int
例子
package main
import (
"fmt"
"log"
)
// To run:
// go run next.go
// EvenNumberIterator generates even number
type EvenNumberIterator struct {
max int
currValue int
err error
}
// NewEvenNumberIterator creates new number iterator
func NewEvenNumberIterator(max int) *EvenNumberIterator {
var err error
if max < 0 {
err = fmt.Errorf("'max' is %d, should be >= 0", max)
}
return &EvenNumberIterator{
max: max,
currValue: 0,
err: err,
}
}
// Next advances to next even number. Returns false on end of iteration.
func (i *EvenNumberIterator) Next() bool {
if i.err != nil {
return false
}
i.currValue += 2
return i.currValue <= i.max
}
// Value returns current even number
func (i *EvenNumberIterator) Value() int {
if i.err != nil || i.currValue > i.max {
panic("Value is not valid after iterator finished")
}
return i.currValue
}
// Err returns iteration error.
func (i *EvenNumberIterator) Err() error {
return i.err
}
func printEvenNumbers(max int) {
iter := NewEvenNumberIterator(max)
for iter.Next() {
fmt.Printf("n: %d\n", iter.Value())
}
if iter.Err() != nil {
log.Fatalf("error: %s\n", iter.Err())
}
}
func main() {
fmt.Printf("Even numbers up to 8:\n")
printEvenNumbers(8)
fmt.Printf("Even numbers up to 9:\n")
printEvenNumbers(9)
fmt.Printf("Error: even numbers up to -1:\n")
printEvenNumbers(-1)
}
chan方式迭代
// 定义一个返回channel的函数
func generateEvenNumbers(max int) chan IntWithError
// IntWithError struct
type IntWithError struct {
Int int
Err error
}
// 调用方法,range方法可以接chan遍历的特性
func printEvenNumbers(max int) {
for val := range generateEvenNumbers(max) {
if val.Err != nil {
log.Fatalf("Error: %s\n", val.Err)
}
fmt.Printf("%d\n", val.Int)
}
}
// 完整generateEvenNumbers
func generateEvenNumbers(max int) chan IntWithError {
ch := make(chan IntWithError)
go func() {
defer close(ch)
if max < 0 {
ch <- IntWithError{
Err: fmt.Errorf("'max' is %d and should be >= 0", max),
}
return
}
for i := 2; i <= max; i += 2 {
ch <- IntWithError{
Int: i,
}
}
}()
return ch
}
例子:
package main
import (
"fmt"
"log"
)
// To run:
// go run channel.go
// IntWithError combines an integer value and an error
type IntWithError struct {
Int int
Err error
}
func generateEvenNumbers(max int) chan IntWithError {
ch := make(chan IntWithError)
go func() {
defer close(ch)
if max < 0 {
ch <- IntWithError{
Err: fmt.Errorf("'max' is %d and should be >= 0", max),
}
return
}
for i := 2; i <= max; i += 2 {
ch <- IntWithError{
Int: i,
}
}
}()
return ch
}
func printEvenNumbers(max int) {
for val := range generateEvenNumbers(max) {
if val.Err != nil {
log.Fatalf("Error: %s\n", val.Err)
}
fmt.Printf("%d\n", val.Int)
}
}
func main() {
fmt.Printf("Even numbers up to 8:\n")
printEvenNumbers(8)
fmt.Printf("Even numbers up to 9:\n")
printEvenNumbers(9)
fmt.Printf("Error: even numbers up to -1:\n")
printEvenNumbers(-1)
}
通过context实现cancel停止迭代功能
package main
import (
"context"
"fmt"
"log"
)
// To run:
// go run channel-cancellable.go
// IntWithError combines an integer value and an error
type IntWithError struct {
Int int
Err error
}
func generateEvenNumbers(ctx context.Context, max int) chan IntWithError {
ch := make(chan IntWithError)
go func() {
defer close(ch)
if max < 0 {
ch <- IntWithError{
Err: fmt.Errorf("'max' is %d and should be >= 0", max),
}
return
}
for i := 2; i <= max; i += 2 {
if ctx != nil {
// if context was cancelled, we stop early
select {
case <-ctx.Done():
return
default:
}
}
ch <- IntWithError{
Int: i,
}
}
}()
return ch
}
func printEvenNumbersCancellable(max int, stopAt int) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch := generateEvenNumbers(ctx, max)
for val := range ch {
if val.Err != nil {
log.Fatalf("Error: %s\n", val.Err)
}
if val.Int > stopAt {
cancel()
// notice we keep going in order to drain the channel
continue
}
// process the value
fmt.Printf("%d\n", val.Int)
}
}
func main() {
fmt.Printf("Even numbers up to 20, cancel at 8:\n")
printEvenNumbersCancellable(20, 8)
}
总结:
- 回调方式实现起来最简单但是语法很别扭
- Next()方法实现最困难,但是对调用方很友好,标准库里运用了这种复杂写法
- channel的实现很好,对系统资源的消耗最昂贵,channel应该与goroutine搭配使用,否则尽量不用
go中如何更好的迭代的更多相关文章
- 编写高质量代码改善C#程序的157个建议——建议31:在LINQ查询中避免不必要的迭代
建议31:在LINQ查询中避免不必要的迭代 无论是SQL查询还是LINQ查询,搜索到结果立刻返回总比搜索完所有的结果再将结果返回的效率要高. 示例代码: class MyList : IEnumera ...
- iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式
iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...
- 如何在MySQL中获得更好的全文搜索结果
如何在MySQL中获得更好的全文搜索结果 很多互联网应用程序都提供了全文搜索功能,用户可以使用一个词或者词语片断作为查询项目来定位匹配的记录.在后台,这些程序使用在一个SELECT 查询中的LIKE语 ...
- caffe中在某一层获得迭代次数的方法以及caffe编译时报错 error: 'to_string' is not a member of 'std'解决方法
https://stackoverflow.com/questions/38369565/how-to-get-learning-rate-or-iteration-times-when-define ...
- ios-Objective-C中的各种遍历(迭代)方式(转载)
iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...
- 在FL Studio中如何更好地为人声加上混响(进阶教程)
为人声加上混响是我们在处理人声过程中必不可少的一步.然而,除了直接在人声混音轨道加上混响插件进行调节以外,这里还有更为细节的做法可以达到更好的效果. 步骤一:使用均衡器 在为人声加上混响之前,我们应该 ...
- 【论文阅读】Beyond OCR + VQA: 将OCR融入TextVQA的执行流程中形成更鲁棒更准确的模型
论文题目:Beyond OCR + VQA: Involving OCR into the Flow for Robust and Accurate TextVQA 论文链接:https://dl.a ...
- JAVA中的for-each循环与迭代
在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 ...
- 让Redis在你的系统中发挥更大作用的几点建议
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/105.html?1455868313 Redis在很多方面与其他数据库解决 ...
随机推荐
- windows下如何制作和应用数字签名证书 全流程
目前我们在发布应用程序时,有时用户下载后会被360杀毒当做木马直接隔离.为应用程序可执行文件打上数字签名可以让360杀毒放宽检测规则.下文是讲述如何制作数字签名证书的过程. 需要准备的工具:makec ...
- [无效]网络流之Edmond-Karp算法
// 此博文为迁移而来,写于2015年2月2日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vr12.html UP ...
- Linux 中【./】和【/】和【.】之间有什么区别?
/是指根目录,就和Windows的我的电脑那个位置差不多../是指用户所在的当前目录.如下所示:[root~]# cd /etc[root etc]# pwd/etc[rootetc]# cd /[r ...
- 配置keepalived支持nginx高可用
实验环境 序号 主机名 IP地址 1 nginx1 192.168.204.11 2 nginx2 192.168.204.12 安装nginx 安装nginx yum install -y epel ...
- Oracle_12541错误和ora-12514错误
ORA-12541: TNS无监听程序错误 在oracle安装目录(E:\app\Administrator\product\instantclient_12_1\NETWORK\ADMIN)下查找l ...
- Java一个对象占用多少字节
虚拟机:Java HotSpot(TM) 64-Bit Server VM (25.221-b11, mixed mode) 对象的内存以字节为单位,且必须是8的倍数,它的构成由3部分组成:对象头+实 ...
- Ansible13:Playbook循环语句
目录 循环语句 简介 loop关键字说明 在循环语句中注册变量 旧循环语句 1. with_items 2. with_nested 3. with_dict 4. with_fileglob 5. ...
- antdpro 打包部署后访问路由刷新后404
antdpro build 后访问路由刷新后 404? 解决方法有三种: 1. 改用 hashHistory,在 .umirc.js或者是config.js 里配 history: 'hash' 2. ...
- cmdb知识总结
cmdb面试 1.paramiko模块的作用与原理 2.cmdb是什么 3.为什么要开发CMDB? 4.你们公司有多少台服务器?物理机?虚拟机? 5.你的CMDB是如何实现的? 6.CMDB都用到了哪 ...
- 重学C语言之结构体
概念 结构体是一种构造类型,由若干个成员组成,成员可以是基本数据类型,或是另一个结构体 声明结构体 struct 结构体名 { 成员列表 }; 结构体名表示结构的类型名. 声明一个结构体表示创建一种新 ...