#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.