【问题标题】:Haskell labelling items in recursive functionHaskell在递归函数中标记项目
【发布时间】:2016-11-30 07:38:43
【问题描述】:

我对 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,但不会尝试放置正确的标签(它对每个构造都使用相同的字符串)。

基本上问题在于,对于顺序语句(并且每个程序都是顺序语句),我想不出一种方法来传递应该在标签中使用的下一个值。

提前谢谢你。

【问题讨论】:

标签: haskell abstract-syntax-tree


【解决方案1】:

如果我正确理解您的问题,您可以向函数添加一个额外的参数 free-index,如下所示:

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

但如果你知道 State monad,你可以这样设计:

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

【讨论】:

  • 但是 (map (transform' freeIdx) stmt) 不会向列表中的所有 Stmt 发送相同的“nextLabel”吗?这可能会导致几个 flat if 或 while 具有相同的标签?我没有尝试过 State monad 主要是因为我不理解它并且无法使其工作。我非常感谢您的解决方案,并且一定会尝试一下,但我仍然想知道是否可以在没有 monads 的情况下解决问题。
  • 你是对的。第一个解决方案并不完全正确。我会努力解决的。
  • 所以,我修复了第一个版本。
  • 我收到错误,例如“无法将预期类型 'FStmt' 与实际类型 't0 'FStmt' 匹配,例如在 (freeIdx', FWhile label1 (Jumpf cond label2) stmt' (Jump label1 ) label2)。那是因为我的 AST 没有派生 Traversable?再次感谢您的帮助,这正是我正在寻找的,像 mapAccumL 这样的函数。
  • 不,这是因为我认为 stmt' 的类型为 [FStmt]。所以我修好了。
猜你喜欢
  • 2011-02-14
  • 2019-02-28
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多