如何在 Haskell 代码中实现浮点解析器功能以进行整数处理?
How to implement a floating point parser feature in Haskell code for integer processing?
参考我的作业描述(我是学生,只有 Haskell 的基本经验),我必须使用 Text.Parsec 制作一个简单的计算器解析器。到目前为止,该程序可以读取一些字符串输入以仅通过整数值执行解析,例如:
parseTest addition "5 + 8 / 4"
我其实有完整的程序代码:
import Text.Parsec hiding(digit)
import Data.Functor
type CalcParser a = Parsec String () a
digit :: CalcParser Char
digit = oneOf ['0'..'9']
number :: CalcParser Integer
number = read <$> many1 digit
fp_char :: CalcParser String
fp_char = many1 digit
applyMany :: a -> [a -> a] -> a
applyMany x [] = x
applyMany x (h:t) = applyMany (h x) t
div_ :: CalcParser (Integer -> Integer -> Integer)
div_= do
char '/'
return div
star :: CalcParser (Integer -> Integer -> Integer)
star = do
char '*'
return (*)
plus :: CalcParser (Integer -> Integer -> Integer)
plus = do
char '+'
return (+)
minus :: CalcParser (Integer -> Integer -> Integer)
minus = do
char '-'
return (-)
multiply :: CalcParser Integer
multiply = do
spaces
lhv <- enclosed
spaces
t <- many tail
return $ applyMany lhv t
where tail =
do
f <- star <|> div_
spaces
rhv <- enclosed
spaces
return (`f` rhv)
add :: CalcParser Integer
add = do
spaces
lhv <- multiply <|> fact' <|> negation'
spaces
t <- many tail
return $ applyMany lhv t
where tail =
do
f <- plus <|> minus
spaces
rhv <- multiply <|> fact' <|> negation'
spaces
return (`f` rhv)
enclosed :: CalcParser Integer
enclosed = number <|> do
char '('
res <- add
char ')'
return res
-- factorial
fact' :: CalcParser Integer
fact' = do
spaces
char '!'
rhv <- number
return $ factorial rhv
factorial :: Integer -> Integer
factorial n
| n < 0 = error "No factorial exists for negative inputs"
| n == 0 || n == 1 = 1
| otherwise = acc n 1
where
acc 0 a = a
acc b a = acc (b-1) (b * a)
-- negation
negation' :: CalcParser Integer
negation' = do
spaces
char '~'
rhv <- enclosed
spaces
return $ negate rhv
上面的清单包括使用否定和阶乘计算选项扩展的主要运算的函数定义。我需要的只是让这个程序对浮点值以及任何字符串输入中的整数敏感。我如何通过调用一个唯一的函数(适用于小数和整数)来实现它来启动解析器,如下所示(仅作为示例):
parseTest totalCalc "~(8.44 * 12.85 / 3.2) * !4"
我输入'!'在阶乘表示法的数字部分之前的字符,因为解析器似乎无法识别正常的“4!”或类似的字符序列作为阶乘指标。
第 1 步: 搜索+将所有出现的 Integer
替换为 Double
。现在你的解析器仍然只能读取整数,但在内部它会将它们表示为 Double
s.
步骤 2: 使 number
解析器解析整数或浮点数。您已经记下的整数:它只是一个数字序列。让我们重命名它以更好地反映它在做什么:
parseInt :: CalcParser Double
parseInt = read <$> many1 digit
浮点数并没有多难:它是一个数字序列,后跟一个点(句点),然后是另一个数字序列:
parseDouble :: CalcParser Double
parseDouble = do
whole <- many1 digit
char '.'
fract <- many1 digit
pure $ read $ whole <> "." <> fract
然后 任何 数字将只是 "either double or int":
number :: CalcParser Double
number = try parseDouble <|> parseInt
另外两个注意事项:
先,注意一定要先试双。如果不这样做,字符串 "8.32"
将被解析为 int,因为前缀 "8"
匹配 parseInt
.
的规则
其次,注意一定要用try
和parseDouble
。如果不这样做,整数将无法解析,因为 parseDouble
解析器将使用直到数字末尾的输入,看不到点,将失败,但 将不会回滚到数字 的开头,这样 parseInt
将看不到任何数字并且也会失败。 try
组合器是在解析器失败时回滚到开始的原因。
参考我的作业描述(我是学生,只有 Haskell 的基本经验),我必须使用 Text.Parsec 制作一个简单的计算器解析器。到目前为止,该程序可以读取一些字符串输入以仅通过整数值执行解析,例如:
parseTest addition "5 + 8 / 4"
我其实有完整的程序代码:
import Text.Parsec hiding(digit)
import Data.Functor
type CalcParser a = Parsec String () a
digit :: CalcParser Char
digit = oneOf ['0'..'9']
number :: CalcParser Integer
number = read <$> many1 digit
fp_char :: CalcParser String
fp_char = many1 digit
applyMany :: a -> [a -> a] -> a
applyMany x [] = x
applyMany x (h:t) = applyMany (h x) t
div_ :: CalcParser (Integer -> Integer -> Integer)
div_= do
char '/'
return div
star :: CalcParser (Integer -> Integer -> Integer)
star = do
char '*'
return (*)
plus :: CalcParser (Integer -> Integer -> Integer)
plus = do
char '+'
return (+)
minus :: CalcParser (Integer -> Integer -> Integer)
minus = do
char '-'
return (-)
multiply :: CalcParser Integer
multiply = do
spaces
lhv <- enclosed
spaces
t <- many tail
return $ applyMany lhv t
where tail =
do
f <- star <|> div_
spaces
rhv <- enclosed
spaces
return (`f` rhv)
add :: CalcParser Integer
add = do
spaces
lhv <- multiply <|> fact' <|> negation'
spaces
t <- many tail
return $ applyMany lhv t
where tail =
do
f <- plus <|> minus
spaces
rhv <- multiply <|> fact' <|> negation'
spaces
return (`f` rhv)
enclosed :: CalcParser Integer
enclosed = number <|> do
char '('
res <- add
char ')'
return res
-- factorial
fact' :: CalcParser Integer
fact' = do
spaces
char '!'
rhv <- number
return $ factorial rhv
factorial :: Integer -> Integer
factorial n
| n < 0 = error "No factorial exists for negative inputs"
| n == 0 || n == 1 = 1
| otherwise = acc n 1
where
acc 0 a = a
acc b a = acc (b-1) (b * a)
-- negation
negation' :: CalcParser Integer
negation' = do
spaces
char '~'
rhv <- enclosed
spaces
return $ negate rhv
上面的清单包括使用否定和阶乘计算选项扩展的主要运算的函数定义。我需要的只是让这个程序对浮点值以及任何字符串输入中的整数敏感。我如何通过调用一个唯一的函数(适用于小数和整数)来实现它来启动解析器,如下所示(仅作为示例):
parseTest totalCalc "~(8.44 * 12.85 / 3.2) * !4"
我输入'!'在阶乘表示法的数字部分之前的字符,因为解析器似乎无法识别正常的“4!”或类似的字符序列作为阶乘指标。
第 1 步: 搜索+将所有出现的 Integer
替换为 Double
。现在你的解析器仍然只能读取整数,但在内部它会将它们表示为 Double
s.
步骤 2: 使 number
解析器解析整数或浮点数。您已经记下的整数:它只是一个数字序列。让我们重命名它以更好地反映它在做什么:
parseInt :: CalcParser Double
parseInt = read <$> many1 digit
浮点数并没有多难:它是一个数字序列,后跟一个点(句点),然后是另一个数字序列:
parseDouble :: CalcParser Double
parseDouble = do
whole <- many1 digit
char '.'
fract <- many1 digit
pure $ read $ whole <> "." <> fract
然后 任何 数字将只是 "either double or int":
number :: CalcParser Double
number = try parseDouble <|> parseInt
另外两个注意事项:
先,注意一定要先试双。如果不这样做,字符串 "8.32"
将被解析为 int,因为前缀 "8"
匹配 parseInt
.
其次,注意一定要用try
和parseDouble
。如果不这样做,整数将无法解析,因为 parseDouble
解析器将使用直到数字末尾的输入,看不到点,将失败,但 将不会回滚到数字 的开头,这样 parseInt
将看不到任何数字并且也会失败。 try
组合器是在解析器失败时回滚到开始的原因。