三种迭代方式

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. [东西]EquationCalcular

    名称:EquationCalcular 版本:V1.0.0 更新日期:2015/9/27   简要介绍:本工具用于计算范围比较有限的方程及方程组,仅仅局限于n元一次方程组,欢迎需要的小学生和初中生来玩 ...

  2. springcloud的Hystrix turbine断路器聚合监控实现(基于springboot2.02版本)

    本文基于方志朋先生的博客实现:https://blog.csdn.net/forezp/article/details/70233227 一.准本工作 1.工具:Idea,JDK1.8,Maven3. ...

  3. 第08组 Beta冲刺(4/5)

    队名:955 组长博客:点这里! 作业博客:点这里! 组员情况 组员1(组长):庄锡荣 过去两天完成了哪些任务 文字/口头描述 ? 测试新功能中 展示GitHub当日代码/文档签入记录 接下来的计划 ...

  4. SQL数据同步到ELK(四)- 利用SQL SERVER Track Data相关功能同步数据(上)

    一.相关文档 老规矩,为了避免我的解释误导大家,请大家务必通过官网了解一波SQL SERVER的相关功能. 文档地址: 整体介绍文档:https://docs.microsoft.com/en-us/ ...

  5. 对称加密与非对称加密,及Hash算法

    一 , 概述 在现代密码学诞生以前,就已经有很多的加密方法了.例如,最古老的斯巴达加密棒,广泛应用于公元前7世纪的古希腊.16世纪意大利数学家卡尔达诺发明的栅格密码,基于单表代换的凯撒密码.猪圈密码, ...

  6. 你真的了解java的lambda吗?- java lambda用法与源码分析

    你真的了解java的lambda吗?- java lambda用法与源码分析 转载请注明来源:cmlanche.com 用法 示例:最普遍的一个例子,执行一个线程 new Thread(() -> ...

  7. Solr7.x学习(4)-导入数据

    导入配置可参考官网:http://lucene.apache.org/solr/guide,http://lucene.apache.org/solr/guide/7_7/ 1.数据准备(MySQL8 ...

  8. sql server数据表大小初始化

    sql server表在存储大数据和处理大数据表时,经常会遇到表空间越来越大,有时候会超出应该占有空间大小很多,此时如果表数据是压缩存储的,那么重新执行一下压缩脚本,数据的大小会重新初始化,然后再使用 ...

  9. JavaScript如何实现日期的前一天后一天转变

    1.生成时间 var data =new Date(); 2.获得时间戳     什么是时间戳? 时间戳是指格林威治时间自1970年1月1日(00:00:00 GTM)至当前时间的总秒数.它也被称为U ...

  10. Keras 中的 verbose 参数

    在 fit( ) 和 evaluate( ) 中 都有 verbose 这个参数,但都是表示日志显示的参数. 具体如下:  fit( ) 中 的 verbose 参数: verbose:日志显示ver ...