1 回答

TA貢獻1797條經驗 獲得超4個贊
這是一個更簡單的例子;希望你能看到這個模式。
關鍵的見解是當生產的匹配被完全解析時執行縮減操作(即解析器函數)。這意味著如果一個產生式包含非終結符,則這些非終結符的動作在整個產生式的動作之前執行。
應該清楚為什么這是真的。每個生產動作取決于所有組件的語義值,在非終結符的情況下,這些值是通過運行相應的動作產生的。
現在,考慮這兩種非常相似的解析 a list
of thing
s 的方法。在這兩種情況下,我們都假設有一個基礎產生式,它識別出一個空的list
( list :
) 并且什么都不做。
右遞歸:
list : thing list
左遞歸:
list : list thing
在這兩種情況下,操作都會打印thing
,這p[0]
在右遞歸的情況下,p[1]
在左遞歸的情況下。
右遞歸產生將導致thing
s 以相反的順序打印,因為thing
直到內部list
被解析(并且它的組件被打?。┲蟛艜蛴?s 。
但thing
出于同樣的原因,左遞歸產生式將按從左到右的順序打印s。區別在于左遞歸情況下的 tgat,內部(遞歸)list
包含初始thing
s,而在右遞歸情況下,則list
包含最終thing
s。
如果您只是構建thing
s的 Python 列表,這可能無關緊要,因為執行順序并不重要。它僅在此示例中可見,因為該操作具有副作用(打印值),這使得執行順序可見。
在極少數情況下確實有必要時,還有其他技術可以對操作進行排序。但最佳實踐是在語法上可行時始終使用左遞歸。左遞歸解析器效率更高,因為解析器不需要累積一堆不完整的產生式。并且左遞歸通常也更適合您的操作。
例如,在這里,左遞歸操作可以附加新值 ( p[0].append(p[1]); return p[0]
),而右遞歸操作需要創建一個新列表 ( return [p[0] + p[1]
)。由于重復追加是平均線性時間,而重復串聯是二次的,左遞歸解析器對于大列表更具可擴展性。
添加回答
舉報