简体   繁体   English

Haskell 中后缀形式的中缀

[英]Infix to Postfix form in Haskell

I'm a beginner in Haskell and i'm kind of lost on what to use to make this program work.我是 Haskell 的初学者,我对使用什么来使这个程序起作用有点迷茫。 What i have to do is get a String like this: "a+(b/c)" <with letters, not numbers> and turn it into its postfix form, which would be like this: "abc/+".我要做的是得到一个这样的字符串:“a+(b/c)”<带字母,而不是数字>,然后把它变成后缀形式,就像这样:“abc/+”。

The question also says that i can't use the following words: "words, putStr, putStrLn, readLn, print"问题还说我不能使用以下词:“words, putStr, putStrLn, readLn, print”

First thing i managed to separate the letters from the symbols, and get them together afterwards:首先,我设法将字母与符号分开,然后将它们组合在一起:

isLetter :: String -> String
isLetter [] = []
isLetter (a:as) | a `elem` "abcdefghijklmnopqrstuvwxyz" = a : isLetter as
                | otherwise = isLetter as

isOperator :: String -> String
isOperator [] = []
isOperator (a:as) | a `elem` "+-*/^" = a : isOperator as
                  | otherwise = isOperator as

onp :: String -> String
onp [] = []
onp str = isLetter str ++ isOperator str

The problem is that it just puts the operators after the letters, not minding the order that it should actually follow.问题在于它只是将操作符放在字母之后,而不关心它实际应该遵循的顺序。

So i did a little research on how to transform it and i thought that i should first check which is an operator and which is a letter and, based on the rules of transforming infix to postfix, i would be putting them together in another string.所以我对如何转换它做了一些研究,我认为我应该首先检查哪个是运算符,哪个是字母,并且根据将中缀转换为后缀的规则,我会将它们放在另一个字符串中。 So i created two functions that would tell whether it's a letter or an operator.所以我创建了两个函数来判断它是字母还是运算符。

It's a mess, but it is like this:一团糟,但它是这样的:

isLetHelp :: Char -> Bool
isLetHelp ch | ch `elem` "abcdefghijklmnopqrstuvwxyz" = True
             | otherwise = False

isOpHelp :: Char -> Bool
isOpHelp a | a `elem` "()+-*/^" = True
           | otherwise = False

isOperator :: String -> String
isOperator [] = []
isOperator (a:as) | a `elem` "+-*/^" = a : isOperator as
               | otherwise = isOperator as

getSymbol :: String -> String
getSymbol [] = []
getSymbol (a:as) | isOpHelp == True = isOperator
                 | isLetHelp == True = a : getSymbol as

This last function 'getSymbol' would be responsible to get the symbols and organize them the right way, but i have no clue how to do it.最后一个函数“getSymbol”将负责获取符号并以正确的方式组织它们,但我不知道如何去做。

It's not clear from your example a+(b/c) , but I assume you need to account for operator precedence, so that a+b/c parses as a+(b/c) (not (a+b)/c ), and thus also evaluates to abc/+ (not ab+c/ ).从您的示例中不清楚a+(b/c) ,但我假设您需要考虑运算符优先级,以便a+b/c解析为a+(b/c) (不是(a+b)/c ),因此也计算为abc/+ (不是ab+c/ )。

There are perhaps simpler or more idiomatic ways of going about this, but as an educational task this is a good way to learn about working with just basic recursive functions.也许有更简单或更惯用的方法来解决这个问题,但作为一项教育任务,这是学习使用基本递归函数的好方法。 There are two main ways to solve this task this way:有两种主要方法可以通过这种方式解决此任务:

The former is more flexible and ultimately the basis of what an idiomatic Haskell solution would look like (using parser combinators), but the shunting yard algorithm has the distinct advantage here of being a simpler algorithm specifically for this task of infix/postfix conversion.前者更灵活,最终是惯用的 Haskell 解决方案的基础(使用解析器组合器),但调车码算法在这里具有明显的优势,它是一种更简单的算法,专门用于中缀/后缀转换任务。

What I'll do is sketch out and describe the structure of the implementation, to help you get unstuck on the general method, and give you the task of filling in the details.我要做的是勾勒和描述实现的结构,以帮助您摆脱一般方法的束缚,并给您填写细节的任务。

The algorithm maintains two pieces of state, a stack of operators, and a queue of output.该算法维护两个状态,一个运算符堆栈和一个输出队列 We can represent both as a list of characters:我们可以将两者表示为字符列表:

type ShuntingYardState = ([Char], [Char])

To push an element to the stack or enqueue an element in the output, you'll cons : it onto the front of the list;要将一个元素推入堆栈或将一个元素排入输出中,您将有缺点:它位于列表的前面; to pop an element from the stack, you can use pattern matching.要从堆栈中弹出一个元素,您可以使用模式匹配。 The output queue is strictly an accumulator for results;输出队列严格来说是结果的累加器; we never dequeue from it.我们从不从它出列。

To convert an infix expression string to postfix, you start this algorithm with the initial state of an empty operator stack and empty output queue:要将中缀表达式字符串转换为后缀,请使用空运算符堆栈和空输出队列的初始状态启动此算法:

expression :: String -> String
expression input = shuntingYard ([], []) input

The algorithm itself has five main cases and one error case for you to handle:算法本身有五种主要情况和一种错误情况供您处理:

shuntingYard
  :: ShuntingYardState
  -> String
  -> String

shuntingYard
  state@(operatorStack, outputQueue)
  (current : rest)

-- 1. A variable term: push it to the output and proceed.

  | isVariable current
  = shuntingYard (variable current state) rest

-- 2. An operator: process operator precedence and proceed.

  | isOperator current
  = shuntingYard (operator current state) rest

-- 3. A left parenthesis: push it onto the operator stack.

  | current == '('
  = shuntingYard (leftParenthesis state) rest

-- 4. A right parenthesis: process grouping and proceed.

  | current == ')'
  = shuntingYard (rightParenthesis state) rest

-- 5. An unrecognized token: raise an error.

  | otherwise
  = error $ "unrecognized input: " ++ show rest

-- 6. No more input: finalize the result.
shuntingYard state []
  = endOfInput state

You need to fill in the definitions of the functions implementing each case above, marked in bold.您需要填写实现上述每种情况的函数的定义,以粗体标记。 Here are their signatures and a description of their function.以下是它们的签名和对其功能的描述。

  • Identifies a variable token, like your isLetHelp :标识变量标记,例如您的isLetHelp

     isVariable :: Char -> Bool
  • Identifies an operator token (not a parenthesis), like your isOpHelp :标识操作符标记(不是括号),例如您的isOpHelp

     isOperator :: Char -> Bool
  • Pushes a variable to the output queue:将变量推送到输出队列:

     variable :: Char -> ShuntingYardState -> ShuntingYardState
  • Processes an operator.处理运算符。 This function has two cases, sketched below.这个函数有两种情况,如下所示。 In the first case, it moves operators from the operator stack to the output queue as long as they have either greater precedence than the current token (for right-associative operators like ^ ), or greater or equal precedence (for left-associative operators like * and - ).在第一种情况下,它将运算符从运算符堆栈移动到输出队列,只要它们具有比当前标记更高的优先级(对于像^这样的右结合运算符),或者更大或相等的优先级(对于像这样的左结合运算符) *- )。 In the second case, it simply pushes the current operator token to the operator stack.在第二种情况下,它只是将当前运算符标记推送到运算符堆栈。

     operator :: Char -> ShuntingYardState -> ShuntingYardState operator current (op : operatorStack, outputQueue) | op /= '(' , … -- Compare precedence & associativity. = operator … -- Repeat. operator current (operatorStack, outputQueue) = … -- Push the operator and return.
  • Processes a left parenthesis by pushing it to the operator stack.通过将左括号推送到运算符堆栈来处理左括号。

     leftParenthesis :: ShuntingYardState -> ShuntingYardState
  • Processing a right parenthesis likewise has two cases: as long as there are operators remaining, move them to the output;处理右括号同样有两种情况:只要还有运算符,就将它们移到输出; if there are none, then expect a matching left parenthesis, or else raise an error.如果没有,则期望匹配的左括号,否则引发错误。

     rightParenthesis :: ShuntingYardState -> ShuntingYardState rightParenthesis (op : operatorStack, outputQueue) | op /= '(' = rightParenthesis … -- Move operator to output. | otherwise = … -- Reached matching left parenthesis; return. rightParenthesis ([], outputQueue) = … -- Mismatched right parenthesis; error.
  • Finally, when reaching the end of input, there are three cases.最后,当到达输入结束时,有三种情况。 If the operator stack is empty, you can convert the queue to the final output string.如果运算符堆栈为空,则可以将队列转换为最终输出字符串。 Otherwise, if there are operators remaining, move them one by one to the output;否则,如果还有运算符剩余,则将它们一一移动到输出; if any parentheses remain, they're missing matching right parentheses, so this is an error.如果任何括号仍然存在,则它们缺少匹配的右括号,因此这是一个错误。

     endOfInput :: ShuntingYardState -> String endOfInput ([], outputQueue) = … -- Success! Return the final result. endOfInput (op : operatorStack, outputQueue) | op == '(' = … -- Mismatched left parenthesis; error. | otherwise = … -- Operator remaining; move to output and repeat.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM