三种迭代方式

3 ways to iterate in 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)
}

总结:

  1. 回调方式实现起来最简单但是语法很别扭
  2. Next()方法实现最困难,但是对调用方很友好,标准库里运用了这种复杂写法
  3. channel的实现很好,对系统资源的消耗最昂贵,channel应该与goroutine搭配使用,否则尽量不用

go中如何更好的迭代的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议——建议31:在LINQ查询中避免不必要的迭代

    建议31:在LINQ查询中避免不必要的迭代 无论是SQL查询还是LINQ查询,搜索到结果立刻返回总比搜索完所有的结果再将结果返回的效率要高. 示例代码: class MyList : IEnumera ...

  2. iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式

    iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...

  3. 如何在MySQL中获得更好的全文搜索结果

    如何在MySQL中获得更好的全文搜索结果 很多互联网应用程序都提供了全文搜索功能,用户可以使用一个词或者词语片断作为查询项目来定位匹配的记录.在后台,这些程序使用在一个SELECT 查询中的LIKE语 ...

  4. 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 ...

  5. ios-Objective-C中的各种遍历(迭代)方式(转载)

    iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...

  6. 在FL Studio中如何更好地为人声加上混响(进阶教程)

    为人声加上混响是我们在处理人声过程中必不可少的一步.然而,除了直接在人声混音轨道加上混响插件进行调节以外,这里还有更为细节的做法可以达到更好的效果. 步骤一:使用均衡器 在为人声加上混响之前,我们应该 ...

  7. 【论文阅读】Beyond OCR + VQA: 将OCR融入TextVQA的执行流程中形成更鲁棒更准确的模型

    论文题目:Beyond OCR + VQA: Involving OCR into the Flow for Robust and Accurate TextVQA 论文链接:https://dl.a ...

  8. JAVA中的for-each循环与迭代

    在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 ...

  9. 让Redis在你的系统中发挥更大作用的几点建议

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/105.html?1455868313 Redis在很多方面与其他数据库解决 ...

随机推荐

  1. 基于zookeeper-3.5.5安装hadoop-3.1.2

    目录 目录 1 1. 前言 3 2. 缩略语 3 3. 安装步骤 4 4. 下载安装包 4 5. 机器规划 4 6. 设置批量操作参数 5 7. 环境准备 5 7.1. 修改最大可打开文件数 5 7. ...

  2. 图的遍历 | 1034 map处理输入数据,连通块判断

    这题写得比较痛苦.首先有点不在状态,其次题目比较难读懂. “Gang”成立的两个条件:①成员数大于两个  ②边权总和大于阈值K 首先,在录数据的时候通过map或者字符串哈希建立string到int的映 ...

  3. CSS 分割线

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. Pod Preset玩转K8S容器时区自动配置

    缘由默认的情况,在K8S里启动一个容器,该容器的设置的时区是UTC0,但是对于很多客户而言,其主机环境并不在UTC0.例如中国客户在UTC8.如果不把容器的时区和主机主机设置为一致,则在查找日志等时候 ...

  5. PHP strlen 对中文 的定义

    $zhStr = ‘您好,中国!’; // gbk编码下每个中文字符所占字节为2echo strlen($zhStr); // 输出:12 //utf-8编码下每个中文字符所占字节为3 echo st ...

  6. AQS3---出队(凌乱,供参考,先看AQS2)

    出队时候,如果队列处于稳定状态,那么就是一个挨着一个出队,难点在于出队的时候,队列正处于调整阶段,那么此时队列中的关系是混乱无章可寻的.  出队:unlock释放锁,不在队列线程去抢锁,队列第一个正常 ...

  7. [原创]K8Cscan插件之Host2IP(批量域名解析/主机名转IP)

    0x001 前言 在内网渗透中,有时候通过域或日志导出一些机器名,或者管理员备分文件等方式获取 只有主机名或者域名时,就需要先解析出IP获取相关IP或IP段,才好进行下一步扫描. 0x002 用法 首 ...

  8. Vue ----------- 了解, 展示json 数据

    Vue.js  是一套构建用户界面的渐进式框架. 优点: 与大型框架不同的是采用自底向上的增量开发的设计, 只聚焦于视图层,不仅易于上手,还便于与第三方库或既有项目整合 当与现代化工具链以及各种类库结 ...

  9. [转帖]分布式锁-redLock And Redisson

    分布式锁-redLock And Redisson 2019-03-01 16:51:48 淹不死的水 阅读数 372更多 分类专栏: 分布式锁   版权声明:本文为博主原创文章,遵循CC 4.0 B ...

  10. Springboot Actuator之十二:actuator aop

    前言spring 中aop是一个核心概念,spring boot 是如何实现自动化配置的?现在我们就来分析一下 解析spring boot 中自动化配置是读取/META-INF/spring.fact ...