我刚刚开始尝试学习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)
答案 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
。对于函数++
要进行类型检查,传递给它的两个参数都应该是相同类型的列表。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