haskell: калькулятор RPN - PullRequest
       11

haskell: калькулятор RPN

1 голос
/ 24 октября 2011

Я хочу разработать систему управления стеками.Список начинается с того, что он пустой [], и пользователь может вводить числа, и они будут добавлены в список, а также двоичные операции, которые возьмут два первых числа из списка и выполнят операцию, а затем вернут ее в список.,Например:

[] : 3
[3] : 4
[4,3] : +
[7] : c
[] : 123
[123] : 3
[3,123] : *
[369] :

Я не могу понять, как обрабатывать ввод с консоли.У меня есть этот сломанный код:

import System.Environment   
import System.Directory  
import System.IO  
import Data.List  

stack = []

add1 :: [Int] -> [Int]
add1 [] = []
add1 [x] = [x]
add1 [x,y] = [(x+y)]
add1 x:(y:xs) = (x+y) : (xs : []) 

--sub :: [Int] -> [Int]
--sub [] = []
--sub x:(y:xs) = (x-y) : xs 

--mul :: [Int] -> [Int]
--mul [] = []
--mul x:(y:xs) = (x*y) : xs 

--div :: [Int] -> [Int]
--div [] = []
--div x:(y:xs) = (x/y) : xs 

c :: [Int] -> [Int]
c = []

push :: [Int] -> a -> [Int]
push [] a = [a]
push (x:xs) a = a : (x:xs)

dispatch :: [(String, Int -> IO ())]
dispatch =  [ ("+", add)
       --     , ("-", sub)
       --     , ("*", mul)
       --     , ("/", div)
            ]

xcl = do
    print stack
    answer <- readLine

Но я даже не знаю, двигаюсь ли я в правильном направлении.Любая помощь будет отличной.Спасибо.

Ответы [ 3 ]

4 голосов
/ 24 октября 2011

Вы находитесь на правильном пути; давайте посмотрим на некоторые изменения в вашем коде, которые я бы предложил.

Ваша add1 функция довольно близка, но у вас есть две небольшие проблемы - первая - это соответствие шаблону, которое вы предоставляете: чтобы быть синтаксически правильным, вам нужно, чтобы все совпадение было в скобках. Второй - второй минус (двоеточие). тип cons имеет тип a -> [a] -> [a], поэтому он отлично работает с (x+y) в качестве первого параметра; однако, поскольку xs уже имеет тип [Int], вам не нужно указывать : [].

add1 (x:y:xs) = (x+y) : xs

Далее, поскольку вы имеете дело с Int s и списками Int s, использование некоторого типа a не имеет смысла в этом контексте.

push :: [Int] -> Int -> [Int]

Наконец, ваша рабочая лошадка. Из-за отсутствия конструкций циклов в Haskell способ сделать цикл пользовательского ввода (также известный как REPL ) - через рекурсию. Из-за этого имеет смысл принять параметр. Давайте сделаем это вашим [Int] стеком. Функция для чтения одной строки из stdin в виде строки - getLine, которая имеет тип IO String. Наконец, вы действительно должны обработать этот ввод. Для простоты я только что включил эту логику в оператор case в xcl, но это также можно было бы сделать с помощью dispatch или аналогичной функции (на самом деле, если ваш RPN-калькулятор становится более сложным, это будет иметь свои достоинства). Действие для каждого случая должно повторяться в вашем цикле xcl с измененным стеком. Когда вы выходите, он должен просто выйти - что является хорошим использованием return.

xcl :: [Int] -> IO ()
xcl st = do
    print st
    answer <- getLine
    case answer of
      "q" -> return ()
      "c" -> xcl ([] ::[Int])
      "+" -> xcl $ add1 st
      x -> xcl $ push st $ read x

Фактически, вы могли бы сделать еще один шаг и защитить себя от исключений - что произойдет, когда пользователь передаст какую-то не функциональную, не числовую строку? Приведенный выше код завершится ошибкой, за исключением «без разбора». Лучший способ обойти это - использовать reads вместо read, как обсуждается в этого ответа . reads возвращает либо список с одной записью - кортеж, содержащий проанализированный номер и оставшуюся строку, либо пустой список (указывающий на неудачное чтение). Например:

      x -> case (reads x) of
        [(num,_)] -> xcl $ push st num
        _ -> xcl st
2 голосов
/ 24 октября 2011
xcl stack = do
    print stack
    answer <- getLine
    case lookup answer dispatch of
        Just function -> -- we have a function, apply it to the top of the stack
        Nothing -> -- if we have a number, parse it and push it onto the stack
                   -- if not, issue an error
  1. stack не может быть константой верхнего уровня, потому что мы хотим манипулировать ею.Поэтому вместо этого мы задаем для параметра xcl.
  2. Используйте getLine для чтения строки текста, потому что мы не знаем, хотим ли мы интерпретировать это как имя оператора или как число.
0 голосов
/ 24 октября 2011

Читать это http://learnyouahaskell.com/functionally-solving-problems#reverse-polish-notation-calculator.Это может быть полезно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...