我应该如何在Go中使用Filepath.Walk?

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

How am I meant to use Filepath.Walk in Go?

问题

filepath.Walk函数接受一个回调函数作为参数。这是一个没有上下文指针的普通函数。当然,Walk的一个主要用例是遍历目录并根据其采取某些操作,参考更广泛的上下文(例如,将每个文件输入到表中)。

如果我用C#编写这个函数,我会使用一个对象(其中的字段可以指向上下文中的对象)作为回调函数(具有给定的回调方法),以便该对象可以封装Walk被调用的上下文。

(编辑:用户“usr”建议闭包方法在C#中也存在)

如果我用C编写这个函数,我会要求一个函数和一个上下文指针作为void *,这样函数就有一个上下文指针,它可以将其传递给Walk函数,并将其传递给回调函数。

但是Go只有函数参数,没有明显的上下文指针参数。

(如果我设计这个函数,我会将一个对象作为回调而不是函数,符合接口FileWalkerCallback或其他接口,并在该接口上放置一个callback(...)方法。消费者可以在将其传递给Walk之前将任何上下文附加到对象上。)

我能想到的唯一方法是在回调函数中捕获外部函数的闭包。以下是我使用它的方式:

func ScanAllFiles(location string, myStorageThing *StorageThing) (err error) {
	numScanned = 0

	// 将其封装在此函数的闭包中,以捕获`corpus`绑定。
	var scan = func(path string, fileInfo os.FileInfo, inpErr error) (err error) {
		numScanned ++

		myStorageThing.DoSomething(path)
	}

	fmt.Println("Scan All")

	err = filepath.Walk(location, scan)

	fmt.Println("Total scanned", numScanned)

	return
}

在这个示例中,我创建了回调函数,使其闭包包含变量numScannedmyStorageThing

这种方式对我来说感觉不对。我是否正确地认为它感觉奇怪,还是我只是习惯了编写Go?filepath.Walk方法的使用方式是回调函数如何引用更广泛的上下文的参考?

英文:

The filepath.Walk function takes a function callback. This is straight function with no context pointer. Surely a major use case for Walk is to walk a directory and take some action based on it, with reference to a wider context (e.g. entering each file into a table).

If I were writing this in C# I would use an object (with fields that could point back to the objects in the context) as a callback (with a given callback method) on it so the object can encapsulate the context that Walk is called from.

(EDIT: user "<a href="https://stackoverflow.com/users/122718/usr">usr</a>" suggests that the closure method occurs in C# too)

If I were writing this in C I'd ask for a function and a context pointer as a void * so the function has a context pointer that it can pass into the Walk function and get that passed through to the callback function.

But Go only has the function argument and no obvious context pointer argument.

(If I'd designed this function I would have taken an object as a callback rather than a function, conforming to the interface FileWalkerCallback or whatever, and put a callback(...) method on that interface. The consumer could then attach whatever context to the object before passing it to Walk.)

The only way I can think of doing it is by capturing the closure of the outer function in the callback function. Here is how I am using it:

func ScanAllFiles(location string, myStorageThing *StorageThing) (err error) {
	numScanned = 0

	// Wrap this up in this function&#39;s closure to capture the `corpus` binding.
	var scan = func(path string, fileInfo os.FileInfo, inpErr error) (err error) {
		numScanned ++

		myStorageThing.DoSomething(path)
	}

	fmt.Println(&quot;Scan All&quot;)

	err = filepath.Walk(location, scan)

	fmt.Println(&quot;Total scanned&quot;, numScanned)

	return
}

In this example I create the callback function so its closure contains the variables numScanned and myStorageThing.

This feels wrong to me. Am I right to think it feels weird, or am I just getting used to writing Go? How is it intended for the filepath.Walk method to be used in such a way that the callback has a reference to a wider context?

答案1

得分: 16

你做得差不多了。有两个小变化你可以考虑一下。一个是你可以用下划线替换未使用的参数的名称。所以,在你的例子中,你只使用了路径,函数签名可以写成:

func(path string, _ os.FileInfo, _ error) error

这样可以节省一些打字,清理代码,同时也清楚地表明你没有使用该参数。此外,特别是对于小函数,通常可以跳过将函数字面量分配给变量,直接将其作为参数使用。你的代码最终会变成:

err = filepath.Walk(location, func(path string, _ os.FileInfo, _ error) error {
    numScanned ++

    myStorageThing.DoSomething(path)
})

这样可以清理一下作用域,清楚地表明你只使用了闭包一次。

英文:

You're doing it about right. There are two little variations you might consider. One is that you can replace the name of an unused parameter with an underbar. So, in your example where you only used the path, the signature could read

func(path string, _ os.FileInfo, _ error) error

It saves a little typing, cleans up the code a little, and makes it clear that you are not using the parameter. Also, for small functions especially, it's common skip assigning the function literal to a variable, and just use it directly as the argument. Your code ends up reading,

err = filepath.Walk(location, func(path string, _ os.FileInfo, _ error) error {
    numScanned ++

    myStorageThing.DoSomething(path)
})

This cleans up scoping a little, making it clear that you are using the closure just once.

答案2

得分: 0

作为一个C#程序员,我可以说这正是.NET中这样的API应该被使用的方式。你会被鼓励使用闭包,而不是创建一个显式带有字段的类,因为那只是浪费你的时间。

由于Go支持闭包,我认为这是使用该API的正确方式。我没有看到任何问题。

英文:

As a C# programmer I can say that this is exactly how such an API in .NET would be meant to be used. You would be encouraged to use closures and discouraged to create an explicit class with fields because it just wastes your time.

As Go supports closures I'd say this is the right way to use this API. I don't see anything wrong with it.

huangapple
  • 本文由 发表于 2012年7月5日 06:34:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/11336048.html
匿名

发表评论

匿名网友

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

确定