为什么我收到此语法错误 - 可能是由于布局错误?

时间:2014-10-04 16:40:28

标签: haskell

我刚刚开始尝试学习haskell和函数式编程。我试图编写这个函数,将二进制字符串转换为十进制等效字符串。请有人指出我为什么经常收到错误:

" BinToDecimal.hs":19 - 表达式中的语法错误(意外的`}',可能是由于布局错误)

module BinToDecimal where

 total :: [Integer]
 total = []

 binToDecimal :: String -> Integer
 binToDecimal a = if (null a) then (sum total) 
                  else if (head a == "0") then binToDecimal (tail a) 
                       else if (head a == "1") then total ++ (2^((length a)-1))
                                                    binToDecimal (tail a)

3 个答案:

答案 0 :(得分:2)

因此,total可能没有按照您的想法行事。 total不是您正在更改的可变变量,它始终是空列表[]。我认为你的功能应该包含你正在建立的列表的另一个参数。我将通过让binToDecimal使用空列表的起始情况调用辅助函数来实现这一点,如下所示:

binToDecimal :: String -> Integer
binToDecimal s = binToDecimal' s []

binToDecimal' :: String -> [Integer] -> Integer
-- implement binToDecimal' here

除了@Sibi所说的,我强烈建议使用模式匹配而不是嵌套的if-else。例如,我实现了binToDecimal'的基本情况,如下所示:

binToDecimal' :: String -> [Integer] -> Integer
binToDecimal' "" total = sum total -- when the first argument is the empty string, just sum total. Equivalent to `if (null a) then (sum total)`
-- Include other pattern matching statements here to handle your other if/else cases

如果您认为它有用,我可以提供此功能的完整实现,而不是提供提示。

答案 1 :(得分:1)

好的,让我给你一些提示让你入门:

  • 由于head a == "0""0",您无法String。由于a的类型为[Char],因此head a的类型为Char,您必须将其与Char进行比较。您可以使用head a == '0'解决此问题。请注意,"0"'0'不同。
  • 同样,请在head a == "1"
  • 中纠正您的类型错误
  • 这不会进行类型检查:total ++ (2^((length a)-1))因为total的类型为[Integer](2^((length a)-1))的类型为Integer。对于函数++要进行类型检查,传递给它的两个参数都应该是相同类型的列表。
  • 你可能最后错过了一个else块。 (在代码binToDecimal (tail a)之前)

话虽如此,不要使用嵌套的if else表达式,而是尝试使用guards,因为它们会大大提高可读性。

答案 2 :(得分:1)

我们可以在这里改进很多东西(但不用担心,这在开始时是完全正常的,当我们启动Haskell时需要学习很多东西!!!)。

首先,字符串绝对不是表示二进制文件的合适方式,因为没有什么能阻止我们用“éaldkgjasdg”来代替正确的二进制文件。所以,首先要定义我们的二进制类型:

data Binary = Zero | One deriving (Show)

我们只是说它可以是零或一。派生(显示)将允许我们在GHCI中运行时显示结果。

在Haskell解决问题时,我们倾向于从一个更普遍的案例开始,然后在我们的特定情况下潜水。这里我们需要的是一个带有附加参数的函数,它保存总数。注意使用模式匹配而不是ifs使函数更容易阅读。

binToDecimalAcc :: [Binary] -> Integer -> Integer
binToDecimalAcc [] acc        = acc
binToDecimalAcc (Zero:xs) acc = binToDecimalAcc xs acc
binToDecimalAcc (One:xs) acc  = binToDecimalAcc xs $ acc + 2^(length xs)

最后,因为我们只需要传递一个参数,我们定义或具体函数,其中acc值为0:

binToDecimal :: [Binary] -> Integer
binToDecimal binaries = binToDecimalAcc binaries 0

我们可以在GHCI中进行测试:

test1 = binToDecimal [One, Zero, One, Zero, One, Zero]
> 42

好的,一切都很好,但是如果你真的需要将字符串转换为小数呢?然后,我们需要一个能够将此字符串转换为二进制的函数。上面的问题是并非所有字符串都是正确的二进制文件。为了解决这个问题,我们需要报告某种错误。我将在这里使用的解决方案在Haskell中很常见:它是使用“Maybe”。如果字符串是正确的,它将返回“Just result”,否则它将返回“Nothing”。让我们在实践中看到它!

我们要编写的第一个函数是将char转换为二进制。如上所述,Nothing代表错误。

charToBinary :: Char -> Maybe Binary
charToBinary '0' = Just Zero
charToBinary '1' = Just One
charToBinary _   = Nothing

然后,我们可以为整个字符串(这是Char的列表)编写一个函数。所以[Char]等同于String。我在这里使用它来更清楚地知道我们正在处理一个列表。

stringToBinary :: [Char] -> Maybe [Binary]
stringToBinary []    = Just []
stringToBinary chars = mapM charToBinary chars

函数mapM是一种作用于monad的map的变体(Maybe实际上是monad)。要了解monad,我建议您阅读“了解大家好的Haskell”! http://learnyouahaskell.com/a-fistful-of-monads

我们可以再次注意到,如果有任何错误,将不会返回任何内容。

现在可以编写一个专用函数来转换包含二进制文件的字符串。

binStringToDecimal :: [Char] -> Maybe Integer
binStringToDecimal = fmap binToDecimal . stringToBinary

使用“。”函数允许我们将此函数定义为与另一个函数相等,因此我们不需要提及参数(无点符号)。

fmap函数允许我们在stringToBinary(类型为“Maybe [Binary]”)的返回时运行binToDecimal(期望[Binary]作为参数)。再一次,了解一个Haskell ...是一个非常好的参考,以了解有关fmap的更多信息: http://learnyouahaskell.com/functors-applicative-functors-and-monoids

现在,我们可以进行第二次测试:

test2 = binStringToDecimal "101010"
> Just 42

最后,我们可以在字符串中错误地测试我们的错误处理系统:

test3 = binStringToDecimal "102010"
> Nothing