【问题标题】:Pretty print expression with as few parentheses as possible?括号尽可能少的漂亮打印表达式?
【发布时间】:2011-09-10 18:55:18
【问题描述】:

我的问题:在没有多余括号的情况下漂亮地打印表达式的最简洁方法是什么?


我有以下 lambda 表达式的表示:

Term ::= Fun(String x, Term t)
      |  App(Term t1, Term t2)
      |  Var(String x)

按照约定,App 是左关联的,即a b c 被解释为(a b) c,函数体尽可能向右延伸,即λ x. x y 被解释为λ x. (x y)

我有一个很好的解析器,但现在我想要一台漂亮的打印机。这是我目前拥有的(伪scala):

term match {
    case Fun(v, t) => "(λ %s.%s)".format(v, prettyPrint(t))
    case App(s, t) => "(%s %s)".format(prettyPrint(s), prettyPrint(t))
    case Var(v)    => v
}

上面的打印机总是把()放在表达式周围(除了原子变量)。因此对于Fun(x, App(Fun(y, x), y)),它会产生

(λ x.((λ y.x) y))

我想要

λ x.(λ y.x) y

【问题讨论】:

  • 我知道的唯一参考文献是 Norman Ramsey 的“Unparsing Expressions with Prefix and Post Operators”。如果您了解一点标准机器学习,您应该能够修改第 4 节中的代码。 cs.tufts.edu/~nr/pubs/unparse-abstract.html
  • 这种语言是不可知论的,还是您正在寻找 Scala 的答案?无论哪种方式,您都可能希望标记问题以吸引更广泛的受众。

标签: pretty-print redundancy


【解决方案1】:

在这里,我将使用一个简单的语法来处理中缀表达式,其关联性和优先级由以下语法定义,其运算符按优先级升序排列

E -> E + T | E - T | T     left associative
T -> T * F | T / F | F     left associative
F -> G ^ F | G             right associative
G -> - G | ( E ) | NUM

给定一个 抽象语法树 (AST),我们将 AST 转换为仅具有必要括号的字符串,如下面的伪代码所述。当我们递归地下降树以确定何时需要括号时,我们检查相对优先级和关联性。请注意,所有将括号括在表达式周围的决定必须在父节点中做出。

toParenString(AST) {
    if (AST.type == NUM)   // simple atomic type (no operator)
        return toString(AST)
    else if (AST.TYPE == UNARY_MINUS)  // prefix unary operator
        if (AST.arg.type != NUM AND 
           precedence(AST.op) > precedence(AST.arg.op))
              return "-(" + toParenString(AST.arg) + ")"
        else 
              return "-" + toParenString(AST.arg)
    else {  // binary operation
        var useLeftParen = 
             AST.leftarg.type != NUM AND
             (precedence(AST.op) > precedence(AST.leftarg.op) OR
              (precedence(AST.op) == precedence(AST.leftarg.op) AND
               isRightAssociative(AST.op)))

        var useRightParen = 
             AST.rightarg.type != NUM AND
             (precedence(AST.op) > precedence(AST.rightarg.op) OR
              (precedence(AST.op) == precedence(AST.rightarg.op) AND
               isLeftAssociative(AST.op)))

        var leftString;
        if (useLeftParen) {
           leftString = "(" + toParenString(AST.leftarg) + ")"
        else
           leftString = toParenString(AST.leftarg)

        var rightString;
        if (useRightParen) {
           rightString = "(" + toParenString(AST.rightarg) + ")"
        else
           rightString = toParenString(AST.rightarg)

        return leftString + AST.op + rightString;
    }
  }

【讨论】:

    【解决方案2】:

    不就是要检查App的参数类型吗?

    我不知道如何在 scala 中写这个..

    term match {
        case Fun(v: String, t: Term) => "λ %s.%s".format(v, prettyPrint(t))
        case App(s: Fun,    t: App)  => "(%s) (%s)".format(prettyPrint(s), prettyPrint(t))
        case App(s: Term,   t: App)  => "%s (%s)".format(prettyPrint(s), prettyPrint(t))
        case App(s: Fun,    t: Term) => "(%s) %s".format(prettyPrint(s), prettyPrint(t))
        case App(s: Term,   t: Term) => "%s %s".format(prettyPrint(s), prettyPrint(t))
        case Var(v: String)          => v
    }
    

    【讨论】:

    • 这种方法不能很好地概括,因为它可能需要考虑二次案例。使用优先级需要更少的代码。
    猜你喜欢
    • 2012-11-22
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 2020-07-24
    相关资源
    最近更新 更多