2016-11-29 54 views
2

對於Haskell和函數式編程,我一般都很新,所以如果問題看起來很簡單或很愚蠢,請原諒。Haskell在遞歸函數中標記項目

我有一個解析器,用於生成抽象語法樹的簡單語言。爲了扁平化AST(將while和if語句變成跳轉),我需要在樹中放置標籤。問題是,我不知道下一個標籤應該是什麼(我仍然認爲是必要的,因爲如果我有狀態,這些都不會成爲問題)。

是我到目前爲止的功能如下:

transform :: Stmt -> FStmt 
transform (Seq stmt) = FSeq (map transform stmt) 
transform (Assign var val) = FAssign var val 
transform (While cond stmt) = FWhile "label1" (Jumpf cond "label2") (transform stmt) (Jump "label1") "label2" 
transform (If cond stmt1 stmt2) = FIf (Jumpf cond "label") (transform stmt1) "label" (transform stmt2) 

在當前狀態下,該功能「變平」的AST,但並不試圖把正確的標籤(它使用相同的字符串爲每個構造)。

基本上問題是,在順序語句(並且每個程序都是順序語句)的情況下,我無法想到一種方法來傳遞應該在標籤中使用的下一個值。

預先感謝您。

回答

4

如果我理解正確的話您的問題,您可以添加到功能的額外的參數自由指數這樣的:

transform :: Stmt -> FStmt 
transform = snd . transform' 0 

transform' :: Int -> Stmt -> (Int, FStmt) 
transform' freeIdx (Seq stmt) = (freeIdx', FSeq stmt') 
    where 
    (freeIdx', stmt') = mapAccumL transform' freeIdx stmt 
transform' freeIdx (Assign var val) = (freeIdx, FAssign var val) 
transform' freeIdx (While cond stmt) = 
    (freeIdx', FWhile label1 (Jumpf cond label2) stmt' (Jump label1) label2) 
    where 
    label1 = "label" ++ show freeIdx 
    label2 = "label" ++ show (freeIdx + 1) 
    (freeIdx', stmt') = transform' (freeIdx + 2) stmt 
transform' freeIdx (If cond stmt1 stmt2) = 
    (freeIdx'', FIf (Jumpf cond label) stmt1' label stmt2') 
    where 
    label = "label" ++ show freeIdx 
    (freeIdx', stmt1') = transform' (freeIdx + 1) stmt1 
    (freeIdx'', stmt2') = transform' freeIdx' stmt2 

但是,如果你知道國家單子,你可以設計這樣的:

transform :: Stmt -> FStmt 
transform = flip evalState 0 . transform' 

transform' :: Stmt -> State Int FStmt 
transform' (Seq stmt) = FSeq <$> mapM transform stmt 
transform' (Assign var val) = return $ FAssign var val 
transform' (While cond stmt) = do 
    label1 <- freeLabel 
    label2 <- freeLabel 
    stmt' <- transform' stmt 
    return $ FWhile label1 (Jumpf cond label2) stmt' (Jump label1) label2 
transform' (If cond stmt1 stmt2) = do 
    label <- freeLabel 
    stmt1' <- transform' stmt1 
    stmt2' <- transform' stmt2 
    return $ FIf (Jumpf cond label) stmt1' label stmt2' 

freeLabel :: State Int String 
freeLabel = do 
    i <- get 
    put $ i + 1 
    return $ "label" ++ show i 
+0

但是不會(map(transform'freeIdx)stmt)將相同的「nextLabel」發送到List中的所有Stmt?這可能會導致幾個單位如果或同一個標籤? 我沒有試過國家monad,主要是因爲我不理解它,不能使它工作。我非常感謝你的解決方案,並且一定會嘗試一下,但我仍然想知道是否有可能解決這個問題而沒有monad。 – Elldorin

+0

你說得對。第一個解決方案不完全正確。我會盡力修復它。 – freestyle

+0

所以,我修正了第一個版本。 – freestyle