Вычисляем строку операций, но код не работает

#haskell

#haskell

Вопрос:

Мне нужно написать код, который вычисляет строку операций и выводит результирующее целое число из этой строки. Я кое-что написал, но это не работает, и мне нужна помощь. Мне нужно использовать fold, поскольку это проще, но я уверен, что что-то не так. Это на Haskell и с использованием Emacs.

 evalExpr :: String -> Int
evalExpr xs = foldl 0 xs where
  f v x | x == " " = ( ) v
        | x == "-" = (-) v
        | x == " " = 0
        | otherwise = read v :: Int
  

Например:

evalExpr «2 4 5-8 «

результат должен быть: 3

evalExpr «»

результат должен быть: 0

Это потому, что он должен читать строку слева направо.

Комментарии:

1. Где ваш f в вашем evalExpr ? И f не проверяется тип. Обратите внимание на сигнатуру типа foldl : (b -> a -> b) -> b -> [a] -> b . Возможно, вам следует использовать пакет parsec для решения вашей проблемы.

2. @Z-Y.L что вы имеете в виду, говоря, что f не проверен на тип?

3. f всегда должен возвращать результат того же типа и соответствовать сигнатуре типа foldl . В вашем случае f сигнатура типа должна быть Int -> Char -> Int , но в вашем случае это не так.

4. @Z-Y.L есть предложения, как это исправить?

5. Я предлагаю вам сначала написать его, используя рекурсию. Использовать сгиб здесь было бы сложнее, поскольку вам нужно обрабатывать более одного символа за раз. Например, когда вам нужно прочитать, " 3..." вам нужно прочитать оба и 3 решить увеличить накопитель на 3. Чтения только недостаточно. Также неясно, нужно ли вам обрабатывать числа больше 9, например, 23 543-765 : в таком случае вам нужна более разумная рекурсия.

Ответ №1:

Вы можете поступить так, как предложил @5ndG. Однако для оценки строки операций использование parsec является лучшим способом. Вот пример для вашего случая:

 module EvalExpr where

-- You need parsec to do parsing work, and the following are just example 
-- modes for your simple case.
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.String

-- A data structure for your simple arithmetic expresssion
data Expr = Lit Int
          | Plus Expr Expr
          | Minus Expr Expr
          deriving Show

-- Evaluate an Expr to an integer number
eval :: Expr -> Int
eval (Lit n) = n
eval (Plus e1 e2) = eval e1   eval e2
eval (Minus e1 e2) = eval e1 - eval e2

-- The following do the parsing work
-- Parser for an integer number
int :: Parser Expr
int = Lit . read <$> (many1 digit <* spaces)      -- A number may be followed by spaces

-- Parser for operators "Plus" and "Minus"
plus, minus :: Parser (Expr -> Expr -> Expr)
plus = Plus <$ char ' ' <* spaces
minus = Minus <$ char '-' <* spaces

-- Parser for Expr
expr :: Parser Expr
expr = chainl int (plus <|> minus) (Lit 0)

-- Evalute string to an integer
evalExpr :: String -> Int
evalExpr s = case parse expr "" s of
               Left err -> error $ show err
               Right e  -> eval e
  

Выше приведен лишь простой пример использования parsec . Если ваш реальный случай более сложный, вам потребуется больше работы. Поэтому необходимо научиться использовать parsec . intro_to_parsing — хорошее начало. Также в описании пакета есть некоторые учебные ресурсы.

Кстати, Text.Parsec.Expr в parsec можно более удобно анализировать выражение, но, прежде всего, вам нужно знать основы parsec .

Приятного обучения!

Ответ №2:

Вы недалеки от того, что работает в ваших примерах. Попробуйте это:

 evalExpr :: String -> Int
evalExpr xs = foldl f (0  ) xs 0

f :: (Int -> Int) -> Char -> (Int -> Int)
f v ch | ch == ' ' = (v 0  )
       | ch == '-' = (v 0 -)
       | ch == ' ' = v
       | otherwise = (v (read [ch] :: Int)  )
  

Итак, основное отличие от вашего заключается в том, что накопитель в fold — это функция, которая принимает одно значение Int и выдает одно значение Int, вместо того, чтобы быть просто Int.