Haskell 为什么我会收到这个语法错误-可能是因为布局不好?

Haskell 为什么我会收到这个语法错误-可能是因为布局不好?,haskell,Haskell,我刚刚开始尝试学习haskell和函数式编程。我正在尝试编写这个函数,它将二进制字符串转换为十进制等效字符串。请有人指出为什么我经常出错: “BinToDecimal.hs”:19-表达式中的语法错误(意外的“}”,可能是由于布局不正确) 好的,让我给你一些提示让你开始: 您不能执行头a==“0”,因为“0”是字符串。由于a的类型是[Char],因此头a的类型是Char,您必须将其与Char进行比较。您可以使用头a==“0”来解决它。请注意,“0”和“0”是不同的 同样,纠正标题a==“1”

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

“BinToDecimal.hs”:19-表达式中的语法错误(意外的“}”,可能是由于布局不正确)


好的,让我给你一些提示让你开始:

  • 您不能执行
    头a==“0”
    ,因为
    “0”
    字符串。由于
    a
    的类型是
    [Char]
    ,因此
    头a
    的类型是
    Char
    ,您必须将其与
    Char
    进行比较。您可以使用
    头a==“0”
    来解决它。请注意,
    “0”和
    “0”是不同的
  • 同样,纠正
    标题a==“1”
  • 这不会进行类型检查:
    total++(2^((长度a)-1))
    ,因为
    total
    的类型是
    [Integer]
    ,而
    (2^((长度a)-1))
    的类型是
    Integer
    。对于要进行类型检查的函数
    ++
    ,传递给它的两个参数应该是相同类型的列表
  • 您可能最终错过了一个else块。(代码前
    binToDecimal(尾部a)

也就是说,不要使用嵌套的if-else表达式,尝试使用防护,因为它们将大大提高可读性。

好的,让我给您一些提示,让您开始:

  • 您不能执行
    头a==“0”
    ,因为
    “0”
    字符串。由于
    a
    的类型是
    [Char]
    ,因此
    头a
    的类型是
    Char
    ,您必须将其与
    Char
    进行比较。您可以使用
    头a==“0”
    来解决它。请注意,
    “0”和
    “0”是不同的
  • 同样,纠正
    标题a==“1”
  • 这不会进行类型检查:
    total++(2^((长度a)-1))
    ,因为
    total
    的类型是
    [Integer]
    ,而
    (2^((长度a)-1))
    的类型是
    Integer
    。对于要进行类型检查的函数
    ++
    ,传递给它的两个参数应该是相同类型的列表
  • 您可能最终错过了一个else块。(代码前
    binToDecimal(尾部a)

也就是说,不要使用嵌套的if-else表达式,而是尝试使用防护,因为它们将大大提高可读性。

因此,
total
可能没有达到您认为的效果
total
不是您正在更改的可变变量,它将始终是空列表
[]
。我认为你的函数应该包括你正在建立的列表的另一个参数。我将通过让
binToDecimal
调用一个以空列表开头的helper函数来实现这一点,如下所示:

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

如果您认为这会有所帮助,我可以提供此功能的完整实现,而不是提供提示。

因此,
total
可能没有做您认为应该做的事情
total
不是您正在更改的可变变量,它将始终是空列表
[]
。我认为你的函数应该包括你正在建立的列表的另一个参数。我将通过让
binToDecimal
调用一个以空列表开头的helper函数来实现这一点,如下所示:

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

如果您认为这会有所帮助,我可以提供此功能的完整实现,而不是提供提示。

这里有很多地方我们可以改进(但不用担心,这在一开始是完全正常的,当我们开始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”。让我们在实践中看到这一点

我们将编写的第一个函数是将字符转换为二进制。如上所述,没有任何东西表示错误

charToBinary :: Char -> Maybe Binary
charToBinary '0' = Just Zero
charToBinary '1' = Just One
charToBinary _   = Nothing
然后,我们可以为整个字符串(即字符列表)编写一个函数。所以[Char]等价于String。我在这里使用它是为了更清楚地表明,我们正在处理一份清单

stringToBinary :: [Char] -> Maybe [Binary]
stringToBinary []    = Just []
stringToBinary chars = mapM charToBinary chars
函数mapM是map的一种变体,作用于monad(可能实际上是monad)。要了解monads,我建议阅读《向你学习Haskell》一书,这是非常有益的!

我们可以再次注意到,如果有任何错误,将不会出现任何错误
test2 = binStringToDecimal "101010"
> Just 42
test3 = binStringToDecimal "102010"
> Nothing