String Haskell语法,傻瓜解析错误

String Haskell语法,傻瓜解析错误,string,parsing,haskell,syntax,String,Parsing,Haskell,Syntax,今天我读了很多关于Haskell的书,但这种形成方式让我发疯。我想尽快理解我的基本错误,这样我就可以开始正常编码了。此处的函数应返回以下一个“数学符号”示例字符串开头的字符串(2sdwds+asd)+3应返回+asd)+3。 这是密码 getToNextSign :: String -> String getToNextSign str = do let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] let a = head str i

今天我读了很多关于Haskell的书,但这种形成方式让我发疯。我想尽快理解我的基本错误,这样我就可以开始正常编码了。此处的函数应返回以下一个“数学符号”示例字符串开头的字符串
(2sdwds+asd)+3
应返回
+asd)+3
。 这是密码

getToNextSign :: String -> String 
getToNextSign str = do

let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']
let a = head str
if a `elem` mathSigns 
 then str
 else if tail str /= [] 
       then getToNextSign $ tail str
       else []

main = do
putStrLn $ getToNextSign "(2sdwds+asd)+3"

它给我“输入时解析错误=”。我也不确定如何准确地在main中调用它,我是否真的需要putStrLn函数。我不认为我需要它,但我尝试了2874种不同的方法来编写它,现在我放弃了,需要帮助。

Haskell对空格敏感,必须将函数体缩进到顶级之外。直接修复原始代码的方法是:

getToNextSign :: String -> String
getToNextSign str = do
  let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']
  let a = head str
  if a `elem` mathSigns
   then str
   else if tail str /= []
         then getToNextSign $ tail str
         else []

main = do
  putStrLn $ getToNextSign "(2sdwds+asd)+3"
正如评论中指出的,这里不需要do符号,因为您没有使用monad。let语句可以写成where语句

getToNextSign :: String -> String
getToNextSign str =
  if a `elem` mathSigns
   then str
   else if tail str /= []
         then getToNextSign $ tail str
         else []
  where
    a = head str
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']

除了Stephen Diehl提供的格式改进之外,您还可以进行其他改进。正如Carl指出的,您可以用防护装置替换if-else,如下所示:

getToNextSign :: String -> String
getToNextSign str
  | a `elem` mathSigns = str
  | tail str /= []     = getToNextSign $ tail str
  | otherwise          = []
  where
    a = head str
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']
当您使用时,还可以使用模式匹配替换头部/尾部,如中所示

getToNextSign :: String -> String
getToNextSign (c:cs)
  | c `elem` mathSigns = c:cs
  | not (null cs)      = getToNextSign cs
  | otherwise          = []
  where
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']
如果你要做模式匹配,你也可以一直做下去

getToNextSign :: String -> String
getToNextSign str = case str of
     c:_ | c `elem` mathSigns -> str
     c:[] -> []
     _:cs -> getToNextSign cs
  where mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']

当你这样做的时候,你会发现你还没有真正处理过当
getToNextSign
获取一个空列表作为参数时的情况,这可能是你想要做的事情。

这里有一个简单的替代方法,使用Prelude list函数,
dropWhile
elem
,它的类型是sig
(a->Bool)->[a]->[a]
。它使用一个函数,只要函数提供的条件为true,该函数就会从列表中删除元素

因此,您的函数可以重写如下

let getNextSign x = dropWhile  ( not . `elem` "+-*/^)" ) x

尽可能避免显式递归,并充分利用高阶函数。《序曲》中有大量的列表操作函数,这些函数随时都会派上用场。

您在这里发布的代码是否与源文件中缩进的代码完全相同?似乎是缩进中的语法错误。2件与您的错误无关的小事:如果您不使用Monad,则不应使用do符号,我更希望使用stdlib中的函数来完成此任务(break/span/etc)。是的,代码是1对1。我知道可能是身份证上的,但我找不到。还有@missingno我想我明白你的意思了,但正如我说的,我尝试了许多不同的方法,但不明白错误在哪里,我想我在第一次默示时没有使用“do”。真的谢谢你。我想我必须检查每个空间。但是一个完整的空白行是一个问题,因为这似乎是唯一的区别。@user3129475不,这甚至不是唯一的区别。重要的区别在于函数体必须缩进。您没有缩进if,但必须缩进。@StephenDiehl我强烈建议使用第三个版本,使用防护而不是嵌套的if。这正是他们使用这种语言的原因。@Carl你能和警卫一起看一下吗?我知道怎么做,但实际上不行。@user3129475评论太长了,所以我把它记下来了。非常感谢。当它得到一个空列表时,我没有处理这个案例,因为我在调用它之前检查列表是否为空。我将很快发布另一个问题,因为我的下一个功能是使用防护,我希望我能正确地使用它们,但我想我也需要帮助。这很好。我们可以使用
getToNextSign[]=[]
进一步重构,然后
getToNextSign str@(c:cs)
| c`elem`mathSigns=str
;否则->getToNextSign cs
@enoreptomment我考虑过这个问题,但决定不包括它,因为它会引入另一个(可能的)元素外来概念–模式中的
@
别名。