Python 无法将非运算符绑定到一元操作数
Python fails to bind not operator to unary operand
在 Python 中,如果 'not' 运算符跟在按位运算符(例如“&”或“|”)之后,结果是语法错误。当然,这将是对二进制值的按位运算,但这应该没问题。据我所知,C 没有问题。
例如,这个有效:
a = 0
b = 1
anot = not(a)
bnot = not(b)
c = anot | bnot
但这失败了:
c = not(a) | not(b)
这些工作:
c = not(a) | (not(b))
c = not a | (not b)
谁能告诉我为什么会这样?不寻找解决方法,只是对实现的解释。与此同时,我会努力浏览源代码和 CFG,看看是否能学到更多。到目前为止,我还没有在 Stacks 或其他谷歌上发现任何类似的问题。谢谢!
请记住,not 是关键字,不是函数。所以表达式 not(a)
在语义上等同于 not a
。前两个示例中的括号对绑定运算符没有任何帮助。但是第三个例子中的 (not a)
会强制先计算内部表达式。
Python 语法确实清楚地表明了发生了什么:(我编辑了长长的不同比较运算符列表,除了 non-terminal 名称和运算符本身之外,它们都是相同的)
inversion:
| 'not' inversion
| comparison
comparison:
| bitwise_or compare_op_bitwise_or_pair+
| bitwise_or
compare_op_bitwise_or_pair:
| eq_bitwise_or
# ...
eq_bitwise_or: '==' bitwise_or
# ...
bitwise_or:
| bitwise_or '|' bitwise_xor
| bitwise_xor
bitwise_xor:
| bitwise_xor '^' bitwise_and
| bitwise_and
bitwise_and:
| bitwise_and '&' shift_expr
| shift_expr
因此,not
的操作数必须是 comparison
,或者从 comparison
开始的优先级链中的某项。 |
的操作数必须是 bitwise_or
(右侧的 bitwise_xor
)或它们的优先级链下的操作数。由于 bitwise_or
比 not
更靠下,因此 bitwise_or
表达式可以是 not
的操作数,但 not
表达式不能是 not
的操作数|
.
所以not 0 | 1
表示not (0 | 1)
,因为0 | 1
可以是not
的操作数,而not 0
不能是|
的操作数. 0 | not 1
是语法错误,因为 not 1
不能是 |
的操作数,并且没有其他解析表达式的方法。
请注意,这与 C 不同。在 C 中,所有一元前缀运算符的绑定比任何二元运算符都更紧密,因此 !0|1
表示 (!0) | 1
,即 1。这与 Python 表达式 not 0 | 1
相反,即 False
。
当然,这并不能解释为什么 Python语法是这样写的,我无法给出完整的历史记录的推理。显然,
被认为是可取的
not a < b
意思是not (a < b)
,而不是(not a) < b
。很少需要后一种解释,因此它具有一定的意义。此外,这与其他布尔运算符的工作方式一致; a < b and b < c
实际上意味着天真的 reader 可能会期望的内容。在 C 语言中也是如此:a < b && b < c
不需要加括号来提供预期的解析。 (但请注意,在 C 中,&
和 |
与 Python 的同名运算符在优先级列表中的位置不同。)
所以这一切都有些可以理解,但问题是为什么要写语法以禁止像1 | not a
这样的无歧义表达式,无论优先级如何,它只能以一种方式进行解析。在这里,我只能猜测。
当然,可以编写一个允许像那样明确表达的语法。但这并不容易,如果您将自己限制在简单的 BNF(或者甚至 Python 语法中现在使用的扩展 BNF 变体)。问题是级联优先样式不允许循环;如果优先级没有形成一致的偏序,解析器会报告歧义。另一方面,如果您使用 Yacc/Bison-like 解析器生成器,或者您可以通过搜索该短语找到的许多 operator-precedence 解析技术中的任何一种,那么它一点也不难。因此,决定使用没有 precedence-based 消歧的解析器生成器的决定可能与实现有关。
您 运行 使用较低优先级的一元运算符的歧义如下,人们通常 运行 在尝试为包含 let
的语言编写语法时遇到这种歧义表达式:"let" <var> "=" <expr> "in" <expr>
。在该构造中,第二个 <expr>
是贪婪的:它会扩展到它可以扩展的范围。但是没有明显的理由说明 let
表达式本身在运算符的 right-hand 端不应该是合法的:
z = 3 * let x = 6 in x - 1/6
let
表达式的计算结果为 29/6 (6 - (1 / 6))
,因此有充分的理由相信 z
将是 14.5,而不是解析器报告语法错误。但是,使用 naively-written 语法,您要么得到语法错误,要么得到一些奇怪的歧义报告。当语法以与 Python 实现 not
相同的方式实现 let
时出现语法错误,并且出于相同的原因:let
表达式不能是 [ 的操作数=55=],两边。
如果您尝试修改级联优先语法以允许 let
在 *
的 right-hand 一侧,您通常会遇到新的歧义;当解析器到达 -
时,它可以选择终止 乘法 ( 3 * let x = 6 in x) - 1/6
或让 let
吸收表达式的其余部分,3 * (let x = 6 in x - 1/6)
。我不认为大多数人 reader 会期待第一次解析,虽然你永远不知道,但解析器不会根据人类的直觉进行操作。 (这通常是一件好事。)
这对于 operator-precedence 解析器来说是微不足道的,因为您需要做的就是定义 let
,左边的优先级最高,右边的优先级最低。 let
运算符本身停留在解析器堆栈上,直到解析器被迫将其弹出,因为它到达表达式的末尾或右括号,这有效地“隐藏”了 *
的优先级操作员。因此,一切都按预期工作。
在 Python 中,如果 'not' 运算符跟在按位运算符(例如“&”或“|”)之后,结果是语法错误。当然,这将是对二进制值的按位运算,但这应该没问题。据我所知,C 没有问题。
例如,这个有效:
a = 0
b = 1
anot = not(a)
bnot = not(b)
c = anot | bnot
但这失败了:
c = not(a) | not(b)
这些工作:
c = not(a) | (not(b))
c = not a | (not b)
谁能告诉我为什么会这样?不寻找解决方法,只是对实现的解释。与此同时,我会努力浏览源代码和 CFG,看看是否能学到更多。到目前为止,我还没有在 Stacks 或其他谷歌上发现任何类似的问题。谢谢!
请记住,not 是关键字,不是函数。所以表达式 not(a)
在语义上等同于 not a
。前两个示例中的括号对绑定运算符没有任何帮助。但是第三个例子中的 (not a)
会强制先计算内部表达式。
Python 语法确实清楚地表明了发生了什么:(我编辑了长长的不同比较运算符列表,除了 non-terminal 名称和运算符本身之外,它们都是相同的)
inversion:
| 'not' inversion
| comparison
comparison:
| bitwise_or compare_op_bitwise_or_pair+
| bitwise_or
compare_op_bitwise_or_pair:
| eq_bitwise_or
# ...
eq_bitwise_or: '==' bitwise_or
# ...
bitwise_or:
| bitwise_or '|' bitwise_xor
| bitwise_xor
bitwise_xor:
| bitwise_xor '^' bitwise_and
| bitwise_and
bitwise_and:
| bitwise_and '&' shift_expr
| shift_expr
因此,not
的操作数必须是 comparison
,或者从 comparison
开始的优先级链中的某项。 |
的操作数必须是 bitwise_or
(右侧的 bitwise_xor
)或它们的优先级链下的操作数。由于 bitwise_or
比 not
更靠下,因此 bitwise_or
表达式可以是 not
的操作数,但 not
表达式不能是 not
的操作数|
.
所以not 0 | 1
表示not (0 | 1)
,因为0 | 1
可以是not
的操作数,而not 0
不能是|
的操作数. 0 | not 1
是语法错误,因为 not 1
不能是 |
的操作数,并且没有其他解析表达式的方法。
请注意,这与 C 不同。在 C 中,所有一元前缀运算符的绑定比任何二元运算符都更紧密,因此 !0|1
表示 (!0) | 1
,即 1。这与 Python 表达式 not 0 | 1
相反,即 False
。
当然,这并不能解释为什么 Python语法是这样写的,我无法给出完整的历史记录的推理。显然,
被认为是可取的 not a < b
意思是not (a < b)
,而不是(not a) < b
。很少需要后一种解释,因此它具有一定的意义。此外,这与其他布尔运算符的工作方式一致; a < b and b < c
实际上意味着天真的 reader 可能会期望的内容。在 C 语言中也是如此:a < b && b < c
不需要加括号来提供预期的解析。 (但请注意,在 C 中,&
和 |
与 Python 的同名运算符在优先级列表中的位置不同。)
所以这一切都有些可以理解,但问题是为什么要写语法以禁止像1 | not a
这样的无歧义表达式,无论优先级如何,它只能以一种方式进行解析。在这里,我只能猜测。
当然,可以编写一个允许像那样明确表达的语法。但这并不容易,如果您将自己限制在简单的 BNF(或者甚至 Python 语法中现在使用的扩展 BNF 变体)。问题是级联优先样式不允许循环;如果优先级没有形成一致的偏序,解析器会报告歧义。另一方面,如果您使用 Yacc/Bison-like 解析器生成器,或者您可以通过搜索该短语找到的许多 operator-precedence 解析技术中的任何一种,那么它一点也不难。因此,决定使用没有 precedence-based 消歧的解析器生成器的决定可能与实现有关。
您 运行 使用较低优先级的一元运算符的歧义如下,人们通常 运行 在尝试为包含 let
的语言编写语法时遇到这种歧义表达式:"let" <var> "=" <expr> "in" <expr>
。在该构造中,第二个 <expr>
是贪婪的:它会扩展到它可以扩展的范围。但是没有明显的理由说明 let
表达式本身在运算符的 right-hand 端不应该是合法的:
z = 3 * let x = 6 in x - 1/6
let
表达式的计算结果为 29/6 (6 - (1 / 6))
,因此有充分的理由相信 z
将是 14.5,而不是解析器报告语法错误。但是,使用 naively-written 语法,您要么得到语法错误,要么得到一些奇怪的歧义报告。当语法以与 Python 实现 not
相同的方式实现 let
时出现语法错误,并且出于相同的原因:let
表达式不能是 [ 的操作数=55=],两边。
如果您尝试修改级联优先语法以允许 let
在 *
的 right-hand 一侧,您通常会遇到新的歧义;当解析器到达 -
时,它可以选择终止 乘法 ( 3 * let x = 6 in x) - 1/6
或让 let
吸收表达式的其余部分,3 * (let x = 6 in x - 1/6)
。我不认为大多数人 reader 会期待第一次解析,虽然你永远不知道,但解析器不会根据人类的直觉进行操作。 (这通常是一件好事。)
这对于 operator-precedence 解析器来说是微不足道的,因为您需要做的就是定义 let
,左边的优先级最高,右边的优先级最低。 let
运算符本身停留在解析器堆栈上,直到解析器被迫将其弹出,因为它到达表达式的末尾或右括号,这有效地“隐藏”了 *
的优先级操作员。因此,一切都按预期工作。