【问题标题】:Recursion issues in HaskellHaskell 中的递归问题
【发布时间】:2014-04-13 06:03:26
【问题描述】:

我有这个compile 函数,它应该接受一个字符串,然后产生一个表达式。

Expression 是这样定义的:

data Expression = Name Char | Lambda Char Expression | Apply Expression Expression 
        deriving Show

假设“编译”函数采用的字符串是:

"\\x.\\y.yx"

所以,最后compile 函数应该产生这个作为最终表达式:

Lambda 'x' (Lambda 'y' (Name 'y') (Name 'x'))

这是compile 函数:

compile :: [Char] -> Expression
compile (x:xs) = if [x] == "\\" then Lambda (head xs) (compile (tail xs))
                    else if [x] == "." then compile xs
                    else if null xs then Name x
                    else Name x

目前,此函数消除了表达式的最后一部分 ((Name 'x'))。我的问题是:

如果一旦我产生一个Name,我就不能继续用表达式的其余部分调用相同的函数,那么这个函数如何继续使用更多的Names?由于Expression 是这样定义的,如果ExpressionName 它只有一个Name 而没有更多,那么它上面就没有表达式了。

我的意思是,我如何获取Expression 中的每一个Name,“告诉”Haskell 我想继续寻找Names,而不是在找到一个Name 时停止。

我认为也许创建另一个函数是个好主意,但我不知道如何多次调用该函数。事实是我真的不习惯 Haskell 中的递归,我真的需要一些帮助。

我该怎么做?

【问题讨论】:

    标签: haskell recursion lambda


    【解决方案1】:

    直接在String 上使用递归进行解析虽然可行,但很棘手。例如,在您的 lambda 演算语言中,处理(嵌套)括号需要一些小心。

    如果您想尝试这样做,至少您应该了解LL parsers 的工作原理:如何编写 LL 语法、如何处理前瞻符号以及一般形式语言理论的基础知识。

    如果您更愿意“通过实践”学习这些内容,请尝试使用解析器库,例如 Parsec

    作为一个小建议:虽然大多数关于 lambda 演算的论文只使用单字母变量并编写例如“xy”表示应用程序,在实现中,您确实需要更长的变量名称,即使它的代价是在应用的术语之间需要空格(例如“x y”)。这使得解析器稍微难以编写,但值得付出努力。

    【讨论】:

    • 其实写一个直接递归的LL解析器一点都不难。这很乏味(好吧,有些极端情况很棘手,但在解析 lambda 演算表达式时不会出现任何问题)
    • @Cubic 如果您了解什么是 LL 语法以及一些基本理论,那么编程确实很容易(即使是命令式语言!)。然而,我不希望普通程序员在没有接触过正式语言的情况下自行重新发现它。您需要了解(对于手头的语言)您可以编写语法,以便一个前瞻标记足以解决每个非终结符的产生式中的非确定性。
    • 嗯,您可以经常通过一个标记前瞻来侥幸成功。毕竟 LL(1) 并不涵盖所有上下文无关文法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-17
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    相关资源
    最近更新 更多