在 Haskell 中使用异常处理将字符串解析为整数

string to Integer parsing with Exception Handling in Haskell

正在尝试创建 nat 类型 return [ (int, string) ]; handle zero as string

并输入 int 可以处理 negative integer also.

import Data.Char ( isDigit )    

type Parser tok a = [tok] -> [(a, [tok])] 
    
return :: a -> Parser tok a 
return v = \ts -> [(v, ts)]

failure :: Parser tok a 
failure = \_ -> [] 

item :: Parser tok tok 
item []     = []
item (t:ts) = [(t, ts)] 

sat :: (tok -> Bool) -> Parser tok tok 
sat test = item >>= \t -> 
           if test t then return t 
                     else failure 

(<|>) :: Parser tok a -> Parser tok a -> Parser tok a 
p1 <|> p2 = \ts -> case p1 ts of 
                     [] -> p2 ts
                    rs1 -> rs1

many1 :: Parser tok a -> Parser tok [a] 
many  p = many1 p <|> return []
many1 p = p      >>= \v  -> 
          many p >>= \vs -> 
          return (v:vs)

digit = sat isDigit 

首先我创建 nat 并使用 if ~ then ~ else make exception of 0.

nat :: Parser Char Int 
nat = many1 digit >>= \s -> 
      if (s == "0") then failure 
                    else return (read s) 

它能够处理一个零。

nat "0"
> [] 

nat "0def" 
> [] 

但输入0开头的连续数字时无法处理

nat "012def" 
> [(12, "def")] -- probably my code only handle 1 Zero
-- expect return [] 

nat "000def" 
> [(0, "def")]  -- but i really don't know why this output is coming 
-- expect return [] 

我试着把nat的问题搁置一旁,先做一个int。

首先我尝试使用nat来定义int。

int :: Parser Char Int 
int = nat >>= \s -> 
      if (s < 0) then return (-s) 
                 else return s 

而且我意识到我无法让编译器识别 s 是负数。

所以我试着让它和 nat 一样。我想添加 char '-'?

int :: Parser Char Int 
int = many1 digit >>= \s -> 
      if (s == "-") then return (read s) ++ "-"  
                    else return (read s) 

我有两个问题

  1. 为什么nat只处理1个零?我还以为many1是一步步递归解析字符串呢

  2. 如何在int中添加“-”?跟合成函数有关系吗?

我不擅长使用Haskell。有什么我想念的吗?

不要惊慌。没那么难。

  1. Why nat only handle 1 zero? I thought many1 is recursive parsing string step-by-step.

你是对的 many1 digit 将在尽可能长的链上应用解析器数字。这意味着当您解析 012def 时,解析的部分是“012”,其余部分是“def”。但是你错过了错误所在。它在下面

\s -> if (s == "0") then failure 
                    else return (read s)

一次,many1 digit 完成后,它会通过绑定 (>>=) 向您的函数提供“012”,这是一个过程。但是“012”不是“0”。所以应用 else 部分:return (read "012")。上下文 "def" 未修改。所以这导致答案 [12,"def"]。如您所见,问题不是绑定的第一部分,而是第二部分。既然你知道你只会得到数字(因为 many1 digit 只能解析数字),我建议你改变你的活页夹

let n = read s 
in if n == 0 then failure 
             else n

更优雅的(单子)解决方案是可能的,但这个解决方案对于新的 haskeller 来说可能是非常可读和明确的。

  1. How can I add "-" in int? does it related with thins like synthetic function?

你不能。你想读一个不能翻译成数字的 Char。所以你不能把它读成数字。这是一个简单的解析问题。您必须单独处理 '-' 。您可以在两个解析器之间进行选择。第一个解析 Char '-' 后跟自然。第二个只解析自然。然后将结果翻译成 Int。注意不要忘记 0。它可以在第一个、第二个或两者中处理。这意味着您不能在两者中都使用 nat...或者您将不得不制作第三个仅解析 0 的解析器。所以记住顺序很重要。

考虑到以前的工作,我很确定可以写这个。您已经有了数字和选择组合器。 char 解析器非常明显。你只需要考虑一下。

希望这会有所帮助。