在 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 或其他 Google 上找到任何類似的問題。謝謝!
uj5u.com熱心網友回復:
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
因此, for 的運算元not必須是 a comparison,或者是從comparison. 并且 for 的運算元|必須是bitwise_or(bitwise_xor在右側)或那些位于優先級鏈上的東西。由于bitwise_or比 更下游not,bitwise_or運算式可以是 的運算元,not但not運算式不能是 的任何一個運算元|。
所以not 0 | 1意味著not (0 | 1),因為0 | 1可以是notwhile的運算元,not 0不能是 的運算元|。And0 | not 1是一個語法錯誤,因為not 1它不能作為運算元,|并且沒有其他方法可以決議運算式。
請注意,這與C 不同。在 C 中,所有一元前綴運算子都比任何二元運算子系結得更緊密,因此!0|1意味著(!0) | 11。這與 Python 運算式相反not 0 | 1,即False。
當然,這并不能解釋為什么Python 語法是這樣寫的,我也無法對這個推理給出完整的歷史說明。顯然,人們認為可取的是
not a < b
意思是not (a < b),而不是(not a) < b。后一種解釋很少需要,所以它有一定的意義。此外,這與其他布爾運算子的作業方式是一致的。a < b and b < c實際上確實意味著天真的讀者可能會期望什么。在 C 語言中也是如此:a < b && b < c不需要用括號括起來以提供預期的決議。(但請注意,在 C 中,&并且|與 Python 的同名運算子在優先級串列中的位置不同。)
所以這一切都可以理解,但問題是為什么要撰寫語法以禁止明確的運算式,例如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,而不是決議器報告語法錯誤。但是,使用簡單撰寫的語法,您要么得到語法錯誤,要么得到一些奇怪的歧義報告。let當語法以與 Python 實作相同的方式實作時,您會得到語法錯誤not,并且出于相同的原因:let運算式不能是*, 在任何一方的運算元。
如果您嘗試修改級聯優先語法以允許let在 的右側*,您通常會得到一個新的歧義;當決議器到達 時-,它可以選擇終止乘法 ( 3 * let x = 6 in x) - 1/6或讓let吸收運算式的其余部分3 * (let x = 6 in x - 1/6)。我不認為大多數人類讀者會期望第一次決議,雖然你永遠不知道,但是決議器不以人類的直覺操作。(這通常是一件好事。)
這對于運算子優先級決議器來說是微不足道的,因為您需要做的就是定義let左側的最高優先級和右側的最低優先級。運算子本身保留在決議器堆疊上,let直到決議器被迫將其彈出,因為它到達運算式的末尾或右括號,這有效地“隱藏”了*運算子的優先級。因此,一切都按預期作業。
uj5u.com熱心網友回復:
請記住,not 是關鍵字,而不是函式。所以運算式not(a)在語意上等價于not a。前兩個示例中的括號對系結運算子沒有任何幫助。但是(not a)第三個例子中的 將強制內部運算式的評估首先發生。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/461374.html
