Parser就是將字符串轉化成AST(Abstract Syntax Tree)的程序python
首先咱們從樹類的簡單解析開始,假設有一個樹節點(它是一個二叉樹節點):bash
class Tr: def __init__(self, val, left=None, right=None): self.val = val self.left = left self.right = right
咱們用這個樹節點構造一棵樹:函數
t_root = Tr(5, Tr(3, Tr(4), Tr(6)), Tr(1, Tr(2), Tr(7)))
它的結構如圖所示,咱們想打印從根節點到每一個葉子節點的路徑怎麼辦呢?遍歷樹通常都能想到用遞歸,可是遞歸到底遞歸什麼東西呢?遞歸函數的參數如何選擇?返回什麼?code
思考遞歸的時候,咱們能夠從問題點出發,先尋找中止遞歸的條件。咱們須要打印從根節點到每一個葉子節點的路徑,因此中止條件就是到達葉子節點了。能夠先用代碼寫出來:blog
def print_leaf(tr_in): # type: (Tr) -> None if tr_in.left is None and tr_in.right is None: # todo: stop here and do something
咱們達到中止條件須要作什麼呢?須要打印。打印什麼?打印從根節點到葉子節點的路徑。好了,咱們如今達到了葉子節點,函數擁有該節點tr_in的val,可是以前路徑的值咱們尚未。咱們須要有,那麼先假設咱們有一個,這個怎麼來的呢,根據思考,以前路徑的值固然是沿途遍歷節點傳進來的,那麼天然須要當成函數輸入傳進來:遞歸
def print_leaf(tr_in, str_in): # type: (Tr, str) -> None if tr_in.left is None and tr_in.right is None: print(str_in + str(tr_in.val))
接着咱們思考,咱們沒有到達葉子節點的時候,須要作什麼。到達葉子節點是中止的標誌,那麼沒有到達葉子節點就是遞歸的標誌。咱們構造左節點或右節點還有葉子的狀況:字符串
def print_leaf(tr_in, str_in=""): # type: (Tr, str) -> None if tr_in.left is not None: print_leaf(tr_in.left, str_in + str(tr_in.val)) if tr_in.right is not None: print_leaf(tr_in.right, str_in + str(tr_in.val)) if tr_in.left is None and tr_in.right is None: print(str_in + str(tr_in.val))
這樣整個代碼就寫好了,咱們實現了一個遞歸函數,它遇到有子節點的狀況就遞歸調用本身,並一路把沿途的值存到str_in裏邊,到達最深層葉子的時候就中止,並把路徑打印出來,完整代碼以下:it
class Tr: def __init__(self, val, left=None, right=None): self.val = val self.left = left self.right = right def print_leaf(tr_in, str_in=""): # type: (Tr, str) -> None if tr_in.left is not None: print_leaf(tr_in.left, str_in + str(tr_in.val)) if tr_in.right is not None: print_leaf(tr_in.right, str_in + str(tr_in.val)) if tr_in.left is None and tr_in.right is None: print(str_in + str(tr_in.val)) if __name__ == "__main__": t_root = Tr(5, Tr(3, Tr(4), Tr(6)), Tr(1, Tr(2), Tr(7))) print_leaf(t_root)
打印效果以下:class
534 536 512 517