Haskell参数模式匹配

时间:2014-11-10 22:42:21

标签: haskell pattern-matching

我正在尝试使用Haskell创建一个编译器,作为我大学课程的一部分。

我想创建一个匹配任何字符串的方法:

int a = 5
int foo = 3

所以这就是我创建的方法:

readInstruction :: String -> String
readInstruction ( 'i' : 'n' : 't' : ' ' : varName : ' ' : '=' : ' ' : val : []) = 
    "Declare Int " ++ [varName] ++ " = " ++ [val]

但是这仅适用于1个字母的变量名称。我该怎么做?

另外,作为附注,我还注意到以下内容无法编译:

   readInstruction ( "int " ++ varName ++ " = " ++ val ) = 
        "Declare Int " ++ varName ++ " = " ++ val

为什么?

请注意,我是Haskell的新手,只知道基础知识。我不知道任何其他库函数,并且不想使用它们(因为我不鼓励将它们用于我的课程)。

2 个答案:

答案 0 :(得分:4)

有很多方法可以解决这个问题....这将是我的偏好顺序。

  1. 使用解析器库,如Parsec。

  2. 使用正则表达式。

  3. 使用诸如splitAt之类的前奏函数。

  4. 但是既然你不能使用任何libs,那么你将不得不使用凌乱的解决方案4。

    1. 使用模式匹配手动编写所有内容。
    2. 您已经向我们展示了如何匹配int部分,因此您只需要剩余的内容。由于这是作业,我不会给你答案,但给你一个可能的大纲。

      您可以做的是将问题分解为多个部分,并编写多个类型

      的匹配器函数
      showPart::String->String
      

      其中的部分类似于showVarNameshowEq等。每个部分都需要消耗部分文本,然后调用下一部分(所以最终你只需要调用第一部分,其余的woulc按顺序消耗)。上面提到的唯一大修改就是需要在showVarName等可变长度部分进行递归。

      showVarName (c:rest) | isAlphanum c = c ++ showVarName
      showVarName x = .... --call the next part here
      

      (是的,我添加了一个新函数isAlphaNum ....你需要类似的东西,尽管可以根据需要使用模式匹配来编写)

      这将解决问题,但请注意解决方案将非常脆弱....很难对零件的排序,类型进行任何更改(如果RHS可以是变量,那该怎么办? ,或者一个完整的表达式),允许的格式(即,如果varname可以是[alpha] [alphaNum] *),或者输出(如果你想输出一个完全解析的表达式树,那么在那里使用它)多种方式,包括将其插入show函数)。

      在实践中,没有人会真正用这种方式解析,我假设这可能是你教授的课程之一。可能正试图为你说明。

答案 1 :(得分:3)

当您进行模式匹配时,您只能在构造函数上进行模式匹配。对于列表,您的两个构造函数是:[],而++是列表中的函数。编译器不能从函数应用程序向后工作,但它可以来自构造函数应用程序(一种非常特殊的函数,甚至可以存在于Haskell中自己的命名空间中)。

更好的替代方法是将输入标记化,这样可以防止错误模式不足,并且从长远来看更容易处理。特别是因为您想要编写编译器,所以应该使用tokenizer,因为这几乎是编写解析器的可接受方式。你可以改为

-- A very simple tokenizer, only splits on whitespace
-- so `int x=1` won't be tokenized correctly
tokenize :: String -> [String]
tokenize = words

readInstructions :: [String] -> (String, [String])
readInstructions ("int" : varName : "=" : val : rest) = ("Declare Int" ++ varName ++ " = " ++ val, rest)
readInstructions otherPatterns = undefined

我返回(String, [String])的原因是您可以迭代地应用readInstructions并让它只消耗每个命令所需的令牌数。所以你可以做到

main = do
    program <- readFile "myProgram.prog"
    let tokens = tokenize program
        (firstInstr,   tokens') = readInstructions tokens
        (secondInstr, tokens'') = readInstructions tokens'
    putStrLn firstInstr
    putStrLn secondInstr

如果您认为这看起来很费力,那么您是对的。这是因为在Haskell中有更好的方法来处理这种事情,而且非常优雅。完成作业后,我会鼓励您查看Parsec库和State monad。 Parsec库专门为你编写了一个标记器并将这些标记转换成有意义的东西,并且状态monad就是库真正建立在它之上的。对状态monad有一个很好的理解将帮助你成为一名Haskell程序员,因为它在很多不同的问题上被大量使用。