[英]Python fails to bind not operator to unary operand
在 Python 中,如果“非”運算符跟在按位運算符(例如“&”或“|”)之后,則結果是語法錯誤。 當然,這將是對二進制值的按位運算,但這應該沒問題。 據我所知,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 是關鍵字,而不是 function。因此表達式not(a)
在語義上等同於not a
。 前兩個示例中的括號對綁定運算符沒有任何幫助。 但是第三個例子中的(not a)
會強制先計算內部表達式。
Python 語法確實清楚地表明了發生了什么:(我編輯出了一長串不同的比較運算符,除了非終端名稱和運算符本身之外,它們都是相同的)
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 0 | 1
not 0 | 1
表示not (0 | 1)
,因為0 | 1
0 | 1
可以是not
的操作數,而not 0
不能是|
的操作數 . 和0 | not 1
0 | not 1
是語法錯誤,因為not 1
不能是|
的操作數並且沒有其他方法可以解析表達式。
請注意,這與C不同。在 C 中,所有一元前綴運算符的綁定比任何二元運算符都更緊密,因此!0|1
表示(!0) | 1
(!0) | 1
,即 1。這與 Python 表達式相反not 0 | 1
not 0 | 1
,這是False
的。
當然,這並不能解釋為什么Python 語法是這樣寫的,我也不會在 position 中給出完整的推理歷史記錄。 顯然,人們認為可取的是
not a < b
意思not (a < b)
,而不是(not a) < b
。 很少需要后一種解釋,因此它具有一定的意義。 此外,這與其他 boolean 運算符的工作方式一致; a < b and b < c
實際上意味着天真的讀者可能會期望什么。 在 C 中也是如此: a < b && b < c
不需要加括號來提供預期的解析。 (但請注意,在 C 中, &
和|
在優先級列表中的位置與 Python 的同名運算符不同。)
所以這在某種程度上是可以理解的,但問題是為什么要編寫語法以禁止像1 | not a
這樣的明確表達式。 1 | not a
,無論優先級如何,它只能以一種方式解析。 在這里,我只能猜測。
當然,可以編寫一個允許像那樣明確表達的語法。 但這並不容易,如果您將自己限制在簡單的 BNF(或者甚至是現在在 Python 語法中使用的擴展 BNF 變體)。 問題是級聯優先樣式不允許循環; 如果優先級沒有形成一致的偏序,解析器會報告歧義。 另一方面,如果您使用類似 Yacc/Bison 的解析器生成器,或者您將通過搜索該短語找到的許多運算符優先級解析技術中的任何一種,那么它一點也不難。 因此,使用沒有基於優先級的消歧的解析器生成器的決定可能與實現有關。
您遇到的低優先級一元運算符的歧義如下,人們在嘗試為包含let
表達式的語言編寫語法時通常會遇到這種情況: "let" <var> "=" <expr> "in" <expr>
。 在該構造中,第二個<expr>
是貪婪的:它會盡可能地擴展。 但是沒有明顯的理由說明let
表達式本身在運算符的右側不應該是合法的:
z = 3 * let x = 6 in x - 1/6
let
表達式的計算結果為 29/6 (6 - (1 / 6))
,因此有充分的理由相信z
將為 14.5,而不是解析器報告語法錯誤。 但是,使用天真編寫的語法,您要么得到語法錯誤,要么得到一些奇怪的歧義報告。 當語法以與 Python 實現not
相同的方式實現let
時,您會得到語法錯誤,並且出於相同的原因: let
表達式不能是*
的操作數,在任何一方。
如果您嘗試修改級聯優先語法以允許let
在*
的右側,您通常會遇到新的歧義; 當解析器到達-
時,它可以選擇終止乘法( 3 * let x = 6 in x) - 1/6
或讓let
吸收表達式的 rest, 3 * (let x = 6 in x - 1/6)
。 我不認為大多數人類讀者會期待第一次解析,雖然你永遠不知道,但解析器不會根據人類的直覺進行操作。 (這通常是一件好事。)
這對於運算符優先級解析器來說是微不足道的,因為您需要做的就是定義左邊最高優先級和右邊最低優先級的let
。 let
運算符本身一直停留在解析器堆棧中,直到解析器被迫將其彈出,因為它到達表達式的末尾或右括號,這有效地“隱藏”了*
運算符的優先級。 因此,一切都按預期工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.