使用Go语言简单模拟Python的生成器

def demo_input_and_output():
  input = yield 'what is the input?'
  yield 'input is: %s' % input

gen = demo_input_and_output()
print(gen.next())
print(gen.send(42))

这段代码演示了 python generator 的功能。可以看到 yield 同时做了两个操作,一个是往外发数据 "waht is the input",同时做的操作是往里收数据 input。而且这个接收数据的操作是一个阻塞的操作,如果外部没有调用 next() (也就是往里传递None),或者调用send(42)(也就是往里传递42这个值),那么这个阻塞的操作就会一直等待下去。

也就是说 python 的 generator 自带了一个对外通信的 channel,用于收发消息。用 go 模拟 python 的 generator 的话写起来就是这样的

代码如下:

package main

import "fmt"

func demoInputAndOutput(channel chan string) {
    channel <- "what is my input?"
    input := <- channel
    channel <- fmt.Sprintf("input is: %s", input)
}

func main() {
    channel := make(chan string)
    go demoInputAndOutput(channel)
    fmt.Println(<- channel)
    channel <- "42"
    fmt.Println(<- channel)
}

这段代码和 python 版本基本上等价。隐含的 channel 在 go 版本里变成显式的了。yield 变成了 channel <- 操作,同时立马做了一个 <- channel 的阻塞读操作。这也就是 yield 的本质吧。

go 的 channel 也可以当成 iterator 被 for 循环使用:

代码如下:

package main

import "fmt"

func someGenerator() <-chan string {
    channel := make(chan string)
    go func() {
        channel <- "a"
        fmt.Println("after a")
        channel <- "c"
        fmt.Println("after c")
        channel <- "b"
        fmt.Println("after b")
        close(channel)
    }()
    return channel
}

func main() {
    channel := someGenerator()
    for val := range channel {
        fmt.Println(val)
    }
}

和 python 的 yield 不同,这里的 channel <- 不等价于 yield,它会往下执行直到阻塞。效果是

代码如下:

after a
a
c
after c
after b
b

这和预期的顺序不一样。这里没有把 after a after c after b 都打印出来是因为 channel 默认只有一个元素的buffer,所以写入了一个就阻塞了。如果增大 buffer,那么就有效果了

代码如下:

make(chan string, 10)

输出变成了:

after a
after c
after b
a
c
b

可见 goroutine 就好象一个独立的线程一样自己和自己玩去了,不用等待被执行。如果要模拟 yield 就要加上显示的同步操作(从 channel 里阻塞读取信号):

代码如下:

package main

import "fmt"

func someGenerator() chan string {
    channel := make(chan string)
    go func() {
        channel <- "a"
        <- channel
        fmt.Println("after a")
        channel <- "c"
        <- channel
        fmt.Println("after c")
        channel <- "b"
        <- channel
        fmt.Println("after b")
        close(channel)
    }()
    return channel
}

func main() {
    channel := someGenerator()
    for val := range channel {
        fmt.Println(val)
        channel <- ""
    }
}

输出的结果就是

a
after a
c
after c
b
after b

到这里我们可以看到,python 的 generator 就好象是 golang 的 goroutine 带了一个无buffer的channel。这样导致每次yield一个值,都会产生一次协程上下文切换。虽然协程上下文切换很廉价,但是也不是没有成本。像 goroutine 的 buffered channel 这样的设计,可以让一个 goroutine 一次性多产生一些输出再阻塞等待,而不是产生一个输出就阻塞等待一下,再产生另外一个输出。golang rocks!

时间: 2015-08-11

Go语言生成素数的方法

本文实例讲述了Go语言生成素数的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main // 生成2, 3, 4, ... 到 channel 'ch'中. func Generate(ch chan<- int) {     for i := 2; ; i++ {         ch <- i  // Send 'i' to channel 'ch'.     } } // 从管道复制值 'in' 到 channel 'out', // 移除可整除的

go语言返回1-99之间随机数的方法

本文实例讲述了go语言返回1-99之间随机数的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main import (     "fmt"     "math/rand" ) func main() {     max := big.NewInt(100)     i, err := rand.Int(rand.Reader, max) } 希望本文所述对大家的Go语言程序设计有所帮助.

golang模板template自定义函数用法示例

本文实例讲述了golang模板template自定义函数用法.分享给大家供大家参考,具体如下: golang的模板十分强大,其中的unix管道风格函数调用很是喜欢. 模板中有很多内置可以参看pkg文档, 另外还可以实现自定义函数. 例子如下: 复制代码 代码如下: package main import (     "text/template"     "time"     "os" ) type User struct {     Usern

理解Golang中的数组(array)、切片(slice)和map

我比较喜欢先给出代码,然后得出结论 数组 复制代码 代码如下: package main import (     "fmt" ) func main() {     arr := [...]int{1, 2, 3}     //打印初始的指针     fmt.Printf("the pointer is : %p \n", &arr)     printPointer(arr) } func printPointer(any interface{}) {

深入解析golang编程中函数的用法

函数是一组一起执行任务的语句.每Go程序具有至少一个函数,它一般是main(),以及所有的最琐碎程序可以定义附加函数. 你可以将代码放到独立的功能.如何划分代码之间的不同功能,但逻辑上的划分通常是让每个函数执行特定的任务. 函数声明告诉编译器有关的函数的名称,返回类型和参数.一个函数定义提供了函数的实际主体. Go语言标准库提供了大量的内置函数,在程序可以调用.例如,函数len()需要不同类型的参数和返回值的类型的长度.例如,如果一个字符串传递给它,它会返回字符串的长度以字节为单位,如果一个数组

Go语言图片处理和生成缩略图的方法

本文实例讲述了Go语言图片处理和生成缩略图的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main import (     "fmt"     "os"     "image"     "image/color"     "image/draw"     "image/jpeg" ) func main() {     f1, err := os

Golang编程实现生成n个从a到b不重复随机数的方法

本文实例讲述了Golang编程实现生成n个从a到b不重复随机数的方法.分享给大家供大家参考,具体如下: 代码很简单: 复制代码 代码如下: package test import (     "fmt"     "math/rand"     "time" ) //生成若干个不重复的随机数 func RandomTestBase() {     //测试5次     for i := 0; i < 5; i++ {         nums

Golang记录、计算函数执行耗时、运行时间的一个简单方法

先写一个公共函数, 比如在 common 包下有这么一个方法: // 写超时警告日志 通用方法 func TimeoutWarning(tag, detailed string, start time.Time, timeLimit float64) { dis := time.Now().Sub(start).Seconds() if dis > timeLimit { log.Warning(log.CENTER_COMMON_WARNING, tag, " detailed:&quo

Go语言生成随机数的方法

本文实例讲述了Go语言生成随机数的方法.分享给大家供大家参考.具体实现方法如下: golang生成随机数可以使用math/rand包 复制代码 代码如下: package main        import (     "fmt"     "math/rand" )        func main() {     for i:=0; i<10; i++ {         fmt.Println(rand.Intn(100))     } } 发现这种情况

c语言生成随机数的方法(获得一组不同的随机数)

复制代码 代码如下: //以空间换时间/*arr :数组名n  :给定的数组长度转移数组中的数据*/ void diffarr(int arr[],int n){    int sn=n,index,*ptr=arr;    int *pfrom=new int[sn];    for(int i=0;i<sn;i++)        *(pfrom+i) =i+1 ;    srand(time(0));    for(int k=0;k<sn;)    {        index=ran

js计算任意值之间随机数的方法

本文实例讲述了js计算任意值之间随机数的方法.分享给大家供大家参考.具体实现方法如下: 首先:Math.random()这个方法就是计算随机数的返回大于等于0小于1的随机数, Math.random()*10岂不是返回大于等于0返回小于10吗,但是他只能返回小于10的数,不能返回10,怎么办呢,我们在原来的函数上再加1就变成了Math.random()*10+1;此时就可以返回1到10的随机数了,但是我们返回的很多是小数,不符合要求,下面就用到了Math.floor()这个函数了,这个函数执行向

Angular+Node生成随机数的方法

本文实例讲述了Angular+Node生成随机数的方法.分享给大家供大家参考,具体如下: 以前写过一个PHP生成随机数,然后jquey ajax获取,再jQuery改变文本的随机数的程序 现在用Angular 和 Node来重写一下 Angular的好处是双向绑定,这样直接设置变量,不用再重新设置了 Node的好处我目前还不是很理解,可以通过count这个例子来说明一下Node和PHP的不同之处 当然了,最大的好处就是,前后端都可以用JavaScript来写了,这样的话Javascript的水平

Java编程实现生成给定范围内不重复随机数的方法小结

本文实例总结了Java编程实现生成给定范围内不重复随机数的方法.分享给大家供大家参考,具体如下: 在Java中的Math类中存在一个random()方法,该方法默认生成0.0到1.0之间的double型随机数:经过稍微处理,就可以产生我们需要的随机数以及随机字符. 除了Math类中的random()方法之外,java.util.Random类也可以用来生成随机数:下面分别举例(用不同的方法生成双色球号码)说明: 1. Math.random()方法 生成随机数: package test; im

php简单生成随机数的方法

本文实例讲述了php简单生成随机数的方法.分享给大家供大家参考.具体如下: <?php /** *生成随机数,可用户验证码 *@param */ function randStr($m = 5) { $new_str = ''; $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwsyz0123456789'; $max=strlen($str)-1; for ($i = 1; $i <= $m; ++$i) { $new_str

js生成随机数的方法实例

js生成随机数主要用到了内置的Math对象的random()方法.用法如:Math.random().它返回的是一个 0 ~ 1 之间的随机数.有了这么一个方法,那生成任意随机数就好理解了.比如实际中我们可能会有如下的需要: (1)生成一个 0 - 100 之间的随机整数,那么则可以: parseInt(100*Math.random()); 注意:因为Math.random()的返回值是包括0和1的,所以这里是有生成0和100的可能性的. (2)生成一个从 m - n 之间的随机整数,例如要生

python 随机数使用方法,推导以及字符串,双色球小程序实例

如下所示: #随机数的使用 import random #导入random random.randint(0,9)#制定随机数0到9 i=random.sample(range(1,34),6)#输出6个随机数,范围是1到34 i.sort()#排序方法,排序时更改原数组,无返回值 sorted(i)#排序函数,排序时不影响原数组,产生新的排序后数据 print('----------------用上述的随机数做一个双色球---------------------') sj=random.sam