许多语言确实选择使赋值成为语句而不是表达式的途径,包括Python:
foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg
和Golang:
var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies
其他语言没有赋值,而是有范围的绑定,例如OCaml:
let foo = 42 in
if foo = 42 then
print_string "hi"
但是,let
是表达本身。
允许赋值的好处是我们可以直接在条件中检查函数的返回值,例如在以下Perl代码段中:
if (my $result = some_computation()) {
say "We succeeded, and the result is $result";
}
else {
warn "Failed with $result";
}
Perl另外将声明的范围限定为该条件,这使其非常有用。如果在没有声明新变量的情况下在条件内部赋值,if ($foo = $bar)
也会发出警告- 会警告,if (my $foo = $bar)
不会。
通常在另一条语句中进行赋值就足够了,但是会带来范围界定问题:
my $result = some_computation()
if ($result) {
say "We succeeded, and the result is $result";
}
else {
warn "Failed with $result";
}
# $result is still visible here - eek!
Golang在很大程度上依赖于返回值进行错误检查。因此,它允许条件语句执行初始化语句:
if result, err := some_computation(); err != nil {
fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)
其他语言使用类型系统禁止条件内的非布尔表达式:
int foo;
if (foo = bar()) // Java does not like this
当然,使用返回布尔值的函数会失败。
现在,我们已经看到了防止意外分配的不同机制:
- 禁止将赋值作为表达式
- 使用静态类型检查
- 分配不存在,我们只有
let
绑定
- 允许初始化语句,否则不允许分配
- 禁止在没有声明的条件内分配
我已经按照偏好的升序对它们进行了排序-表达式内的赋值可能是有用的(并且通过使用显式声明语法和不同的命名参数语法来规避Python的问题很简单)。但是也可以禁止它们,因为还有许多其他选择可以达到相同的效果。
无错误的代码比简洁的代码更重要。