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