Go中的短路求值

22

我理解的短路评估是在if语句中只有在需要时才调用表达式。Go语言是否遵循这种做法?

例如,我是否可以从以下方式中获得更好的平均性能:

if !isValidQueryParams(&queries) || r == nil || len(queries) == 0 {
    return "", fmt.Errorf("invalid querystring")
}
把这个转换成这样:
if r == nil || len(queries) == 0 || !isValidQueryParams(&queries) {
    return "", fmt.Errorf("invalid querystring")
}

...因为isValidQueryParams是一个比r == nil或测试映射长度开销更大的函数?

即解释器会先评估r == nil,看到它是true,不会费心去评估其他条件吗?

编辑:错误地将短路求值称为惰性求值


1
我认为惰性求值通常指的是一个变量在被访问之前不会被计算或获取(例如从数据库或API中获取)。 - Jonathan Hall
2
有点相关:在Go代码中存在短路评估,但Go的模板引擎不使用短路评估。详情请参见:Golang模板和测试有效字段 - icza
@icza,谢谢,已包含。 - kostix
3个回答

31

感谢Kostix和mkrieger的回答 - 他们是正确的,我指的是短路求值而不是惰性求值。

Go语言实现了正常的短路求值,可以通过以下代码得出:

package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
        if testFunc(1) || testFunc(2) {
            // do nothing
        }
    }
}

func testFunc(i int) bool {
    fmt.Printf("function %d called\n", i)
    return true
}

...这将始终给出:

$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called

13

这被称为短路评估。根据这个教程,布尔运算符使用它:

虽然在Go语言规范中并没有明确说明Go使用短路评估,但是它提到:

逻辑运算符应用于布尔值,并产生与操作数类型相同的结果。右操作数有条件地进行评估。

下面是一个快速示例,以证明Go使用短路评估

[...]


7
你所提到的是“短路求值”,也就是说,只有当完整结果可用且根据相关二元运算符规则计算其余表达式不会改变它时,子表达式才会按照常规结合律进行求值。
Go确实实现了逻辑表达式的短路求值(参见另一个答案)。
(@icza评论:有点相关的是,Go代码中存在短路求值,但Go的模板引擎不使用短路求值。详情请见Golang模板和测试有效字段。)
“惰性求值”是完全不同的事情——通常在所谓的“函数式”编程语言中实现,并且在Go中没有直接实现。

话虽如此,我要指出的是,虽然Go语言在语法和运行时方面没有直接支持惰性求值(lazy evaluation),但可以根据需要使用它。

例如,你可能拥有一个协程从通道中读取潜在的无限数量的项目,并以某种方式处理它们,而另一个或多个协程则生成这些值并通过通道发送它们。这样,这些值只会在接收端实际准备好处理时才会"具体化"。


我是Go的新手,你所解释的通道是实现惰性求值的一种方式(但并不完全相同),这个回答对我很有帮助。我正在尝试弄清楚--> Go是否有类似于Python的range表达式(如range(1000))的等效物,它不会将所有1000个元素存储在内存中,而只返回一个值? - Dhiwakar Ravikumar
@DhiwakarRavikumar,这些被称为“生成器”,但是Go语言没有它们。但请注意,生成器只是一些对象的语法糖,这些对象在单个方法的相邻调用之间维护一些状态。 - kostix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接