初学者Haskell问题(没有实例......来自)

时间:2011-10-16 20:51:27

标签: haskell

我最近开始学习Haskell,我正在尝试重写我在Haskell中使用python进行访谈时所做的事情。我正在尝试将字符串从camel case转换为下划线(“myVariableName” - >“my_variable_name”),如果第一个字符为大写,则会抛出错误。

这就是我所拥有的:

import qualified Data.Char as Char

translate_java :: String -> String
translate_java xs = translate_helper $ enumerate xs
    where 
        translate_helper [] = []
        translate_helper ((a, num):xs)
            | num == 1 and Char.isUpper a = error "cannot start with upper"
            | Char.isUpper a              = '_' : Char.toLower a : translate_helper xs
            | otherwise                   = a : translate_helper xs


enumerate :: (Num b, Enum b) => [a] -> [(a,b)]
enumerate xs = zip xs [1..]

我意识到我很可能会以一种奇怪的方式解决这个问题,而且我喜欢有关更好的方法来实现这一点的建议,但我也希望能够编译它。这是我现在得到的错误:

Prelude> :r
[1 of 1] Compiling Main             ( translate.hs, interpreted )

translate.hs:4:20:
    No instance for (Num
                       (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t))
      arising from a use of `translate_helper' at translate.hs:4:20-35
    Possible fix:
      add an instance declaration for
      (Num (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t))
    In the first argument of `($)', namely `translate_helper'
    In the expression: translate_helper $ enumerate xs
    In the definition of `translate_java':
        translate_java xs
                         = translate_helper $ enumerate xs
                         where
                             translate_helper [] = []
                             translate_helper ((a, num) : xs)
                                                | num == 1 and Char.isUpper a
                                                = error "cannot start with upper
"
                                                | Char.isUpper a
                                                = '_' : Char.toLower a : transla
te_helper xs
                                                | otherwise = a : translate_help
er xs
Failed, modules loaded: none.

对这里发生的事情的任何解释都会很棒。我真的不明白“(Num(([Bool] - > Bool) - >(Char - > Bool) - > Char - > t))”来自。我认为translate_helper的类型声明类似于[(a,b)] - > [α]?

4 个答案:

答案 0 :(得分:5)

您必须将and替换为&&。第一个是一个函数(前缀),它接收一个布尔值列表并计算它们的全部。第二个是真正的逻辑和。但错误消息有点令人困惑。每当我收到如此奇怪的错误消息时,我通常会开始使用类型签名来注释我的代码。然后编译器能够为您提供更详细的错误描述。

答案 1 :(得分:5)

其他人提到你应该使用(&&)代替and,所以我会回答你的另一个问题:不,我认为你不会以一种奇怪的方式解决这个问题。

但是......我认为它可以更加优雅!

translate_java (x:xs) | isUpper x = error "cannot start with an upper"
translate_java xs = concatMap translate xs where
    translate x = ['_' | isUpper x] ++ [toLower x]

这里有一些有趣的事情:

  1. 直接检查特殊情况。不要等到你再次做到这一点!
  2. concatMap函数在很多情况下非常方便。它只是map后跟concat。如果我自己写这篇文章,我可能会使用xs >>= translate代替。
  3. ['_' | isUpper x]是列表理解;这是一个可爱的习惯用法,用于制作一个包含0或1个元素的列表,具体取决于谓词是否成立。
  4. 除此之外,代码应该是相当不言自明的。

答案 2 :(得分:4)

问题在于:

| num == 1 and Char.isUpper a = ...

and不是中缀运算符;相反,它是一个功能:

and :: [Bool] -> Bool

因此它将1 and Char.isUpper a解释为将三个参数应用于“function”1。请改用&&

错误消息来自解释数字的方式。数字,例如,1实际上是多态的;它获得的具体类型取决于所需的类型。这就是为什么你可以说x+1,无论x是整数还是双数或其他什么都可以。因此编译器推断出1的类型需要是一个三参数函数,然后尝试找到匹配的数字类型,以便它可以将1转换为该类型(当然,失败)。

答案 3 :(得分:3)

这是我的解决方案。它不像Daniel Wagner使用concatMap和列表理解所给出的那样精湛,但对初学者来说可能更容易理解。

conv :: String -> String
conv [] = []
conv s@(x:xs) = if Char.isUpper x 
                then error "First character cannot be uppercase"
                else change s

change :: String -> String
change [] = []
change (x:xs) = if Char.isUpper x 
                then '_' : Char.toLower x : change xs 
                else x : change xs

函数conv实际上只是检查你的标准,即第一个字符不能是大写的,如果不是,它将字符串移交给函数更改,这就完成了工作。它逐个遍历所有字符,构建一个列表,如果字符是大写的,它会添加一个下划线,后跟字符的小写版本,否则如果该字符已经是小写,则只需按原样添加它。 / p>