使用C风格迭代器与Go的惯用方式是什么?

huangapple go评论132阅读模式
英文:

idiomatic way to use c style iterator with Go

问题

我非常新于Go编程(3-4天),我正在尝试使用现有的第三方C库(使用cgo)编写一些读取二进制文件的代码。这个C库的做法似乎相当标准(对于C来说)。稍微简化一下,代码如下:

func main() {
    file := Open(filename)
    record := NewRecord()
    iter := file.Query(region)
    for {
        n, err := file.Next(iter, record)
        if err != nil {
            log.Fatal(err)
        }
        if n <= 0 {
            // No more records to read.
            break
        }
    }
}

这段代码可以工作,也就是说它可以让我访问特定查询区域的记录。

我的问题是,这种方法在Go中是否是惯用的方式,或者是否有更好的替代方案?我看过一些网站,比如http://ewencp.org/blog/golang-iterators,但似乎无法将这些示例与C库一起使用(我认为可能是因为C库在每次迭代中重用record_t变量而不是创建一个新变量,但也可能是因为我对Go的经验不足)。

英文:

I'm very new to Go programming (3-4 days), and I'm trying to write some code that reads a binary file using an existing third-party C library using cgo. The C library's way of doing this seems fairly standard (for C). Slightly simplified it looks like:

int main(int argc, char *argv[]) {
    file_t *file = file_open(filename);
    index_t *index = index_load(file, filename);
    iterator_t *iter = query(idx, header, region);
    record_t *record = record_init();

    while (iterator_next(file, iter, record) &gt;= 0) {
        /* Do stuff with record */
    }

    iterator_destroy(iter);
    record_destroy(record);
    file_close(file);

    return 0;
}

I have written the following Go code:

func main() {
    file := Open(filename)
    record := NewRecord()
    iter := file.Query(region)
    for {
	    n, err := file.Next(iter, record)
	    if err != nil {
		    log.Fatal(err)
	    }
	    if n &lt;= 0 {
		    // No more records to read.
		    break
	    }
    }
}

This works, in the sense that it will allow me to access the records in a particular query region.

My question is whether this is an idiomatic way to approach this task in Go, or are there better alternatives? I've seen sites such as http://ewencp.org/blog/golang-iterators, but seem unable to get these examples to work with the C library (I was thinking it may be because the C library is reusing the record_t variable on each iteration rather than creating a new variable, but maybe it's just my lack of experience with Go).

答案1

得分: 0

你正在做的事情与使用io.Reader在文件中移动并没有太大的区别:

err, n := error(nil), 0
for err == nil {
    err, n = f.Read(in)
    // ...使用in[:n]做一些操作...
}

或者使用(*bufio.Scanner).Scan()(参见文档):

for scanner.Scan() {
    // ...对scanner.Text()做一些操作...
}
if err := scanner.Err(); err != nil {
    log.Fatalln(err)
}

我认为你很少需要博文中提到的更奇特的迭代选项,比如使用闭包或通道。特别是通道会调用很多用于协调真实线程工作负载的机制,在惯例上,根据迭代的内容,Go语言中的循环看起来可能会有所不同。(在这方面,它类似于C语言,但与Python、C++或Java不同。)

英文:

What you're doing isn't that different from moving through a file with an io.Reader:

err, n := error(nil), 0
for err == nil {
	err, n = f.Read(in)
	// ...do stuff with in[:n]...
}

Or using (*bufio.Scanner).Scan() (see the docs):

for scanner.Scan() {
    // ...do something with scanner.Text()...
}
if err := scanner.Err(); err != nil {
    log.Fatalln(err)
}

I think you rarely want the more exotic iterator options in the blog post you linked to, the ones with closures or channels. Channels in particular invoke a lot of machinery meant for coordinating real threaded workloads, and in terms of convention, it's just typical in Go for loops to look a little different depending on what they're iterating. (In that respect, it's like iteration in C, but different from (say) Python, C++, or Java.)

huangapple
  • 本文由 发表于 2015年9月25日 08:02:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/32772798.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定