回答:回调迭代应该以 true 或 false 终止?

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

Should true or false terminate callback iteration?

问题

在某些语言中,通过提供一个回调函数来进行迭代是必要的或更清晰的,该回调函数接收项目并返回一个布尔值,指示是否继续或停止迭代。

在这种情况下,想要停止或继续迭代,通常使用的是布尔值。通常,返回true表示继续迭代,返回false表示停止迭代。这种约定在许多编程语言中都有先例。

在Go语言中的示例中,回调函数cb接收一个整数参数i,并返回一个布尔值more。如果cb(i)返回false,则表示停止迭代,函数IntSliceEach将返回false。如果所有的迭代都执行完毕,函数将返回true

请注意,以上是对代码的翻译和解释,不包含其他内容。

英文:

In some languages it's necessary or cleaner to do iteration by providing a callback function that receives items and returns a boolean that indicates whether to continue or stop the iteration.

Which is the preferred value to indicate desire to stop/continue? Why? What precedents exist?

Example in Go:

func IntSliceEach(sl []int, cb func(i int) (more bool)) (all bool) {
    for _, i := range sl {
        if !cb(i) {
            return false
        }
    }
    return true
}

答案1

得分: 2

在这种情况下,"true"(真)用于表示应该继续进行。根据示例1和示例2,当条件为真时,表示可以继续执行某些操作。因此,在你的例子中,我认为使用"true"是合适的选择。

英文:

> Which is the preferred value to indicate desire to stop/continue?

true for continue

> Why?

Example 1:

func example(i interface{}) {
    if w, ok := i.(io.Writer); ok {
        // do something with your writer, ok indicates that you can continue
    }
}

Example 2:

var sum int = 0
it := NewIntStatefulIterator(int_data)
for it.Next() {
    sum += it.Value()
}

In both cases true (ok) indicates that you should continue. So I assume that it would be way to go in your example.

答案2

得分: 1

前言: 以下答案适用于基于当前项目决定循环是否提前终止的回调函数 - 这是你所问的。

这与一个进展并报告是否有更多元素要处理的函数不同,其中通常接受true返回值来表示还有更多元素(一个很好的例子是Scanner.Scan()),其典型用法是:

scanner := bufio.NewScanner(input)
for scanner.Scan() {
    // 处理当前项目(行):
    line := scanner.Text()
    fmt.Println(line) // 对行进行操作
}

保持bool返回类型

通常,返回true以表示终止会导致更易读的代码。这是由于for的性质:如果什么都不做,for会继续执行,因此如果要提前终止,必须显式地使用break,因此具有清晰的终止条件更为常见。

但这是个人口味问题。你可以选择任何你喜欢的方式,但重要的是给你的回调函数取一个有意义的名称,清楚地说明其返回值的含义,这样查看代码(使用它的条件)将更容易理解。

例如,以下名称是好的,返回值是明确的:

// 返回值为true表示终止
func isLast(item Type) bool
func terminateAfter(item Type) bool
func abort(item Type) bool

// 返回值为true表示继续(不终止)
func keepGoing(item Type) bool
func carryOn(item Type) bool
func processMore(item Type) bool

使用这些名称可以得到易于理解的代码:

for i, v := range vals {
    doSomeWork()
    if terminateAfter(v) {
        break // 或者返回
    }
}

for i, v := range vals {
    doSomeWork()
    if !keepGoing(v) {
        break // 或者返回
    }
}

// 或者对最后一个示例的另一种写法(主观上哪个更易读):

for i, v := range vals {
    doSomeWork()
    if keepGoing(v) {
        continue
    }
    break
}

作为负面示例,以下回调函数名称不好,因为你无法猜到它们的返回值的含义:

// 不好:仅凭名称无法确定返回值为true的含义:
func test(item Type) bool
func check(item Type) bool

使用error返回类型

回调函数通常不仅测试,还会对传递的项目进行一些处理。在这些情况下,返回error而不是bool是有意义的。显然,nil返回值表示成功(并继续),非nil值表示错误并且处理应该停止。

func process(item Type) error

for i, v := range vals {
    if err := process(v); err != nil {
        // 处理错误并终止
        break
    }
}

使用类似枚举的返回值

如果多个返回值具有意义,你可以选择为返回值定义常量,并给它们取一个有意义的名称。

type Action int
const (
    ActionContinue Action = iota
    ActionTerminate
    ActionSkip
)

func actionToTake(item Type) Action

for i, v := range vals {
    switch actionToTake(v) {
    case ActionSkip:
        continue
    case ActionTerminate:
        return
    }
    doSomeWork()
}
英文:

Foreword: The following answer applies to a callback function which decides based on the current item(s) whether the loop should terminate early - this is what you asked.

This is not to be confused with a function that progresses and reports if there are more elements to process, where a true return value is generally accepted to signal that there are more elements (for which a good example is Scanner.Scan()), and whose typical use is:

scanner := bufio.NewScanner(input)
for scanner.Scan() {
    // Process current item (line):
    line := scanner.Text()
    fmt.Println(line) // Do something with line
}

Sticking to bool return type

Usually returning true to indicate termination results in code that is easier to read. This is due to the nature of for: if you do nothing, for continues, so you have to explicitly break if you want to terminate early, so having a clean termination condition is more common.

But it's a matter of taste. You may go whichever you like, but what's important is to name your callback function in a meaningful way that will clearly state what its return value means, and thus looking at the code (the condition in which it is used) will be easily understandable.

For example the following names are good and the return value is unambiguous:

// A return value of true means to terminate
func isLast(item Type) bool
func terminateAfter(item Type) bool
func abort(item Type) bool

// A return value of true means to continue (not to terminate)
func keepGoing(item Type) bool
func carryOn(item Type) bool
func processMore(item Type) bool

Using these results in easily understandable code:

for i, v := range vals {
    doSomeWork()
    if terminateAfter(v) {
        break // or return
    }
}

for i, v := range vals {
    doSomeWork()
    if !keepGoing(v) {
        break // or return
    }
}

// Or an alternative to the last one (subjective which is easier to read):

for i, v := range vals {
    doSomeWork()
    if keepGoing(v) {
        continue
    }
    break
}

As negative examples, the following callback function names are bad as you can't guess what their return value mean:

// Bad: you can't tell what return value of true means just by its name:
func test(item Type) bool
func check(item Type) bool

Having error return type

It's also common for the callback to not just test but also do some work with the passed item. In these cases it is meaningful to return an error instead of a bool. Doing so, obviously the nil return value indicates success (and to continue), and a non-nil value indicates error and that processing should stop.

func process(item Type) error

for i, v := range vals {
    if err := process(v); err != nil {
        // Handle error and terminate
        break
    }
}

Having enum-like return value

Also if multiple return values have meaning, you may choose to define constants for return values, which you can name meaningfully.

type Action int
const (
    ActionContinue Action = iota
    ActionTerminate
    ActionSkip
)

func actionToTake(item Type) Action

for i, v := range vals {
    switch actionToTake(v) {
    case ActionSkip:
        continue
    case ActionTerminate:
        return
    }
    doSomeWork()
}

huangapple
  • 本文由 发表于 2016年4月7日 16:57:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/36471296.html
匿名

发表评论

匿名网友

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

确定