如何在 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。现在你的解析器仍然只能读取整数,但在内部它会将它们表示为 Doubles.

步骤 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.

的规则

其次,注意一定要用tryparseDouble。如果不这样做,整数将无法解析,因为 parseDouble 解析器将使用直到数字末尾的输入,看不到点,将失败,但 将不会回滚到数字 的开头,这样 parseInt 将看不到任何数字并且也会失败。 try 组合器是在解析器失败时回滚到开始的原因。