我最近开始学习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)] - > [α]?
答案 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]
这里有一些有趣的事情:
concatMap
函数在很多情况下非常方便。它只是map
后跟concat
。如果我自己写这篇文章,我可能会使用xs >>= translate
代替。['_' | isUpper x]
是列表理解;这是一个可爱的习惯用法,用于制作一个包含0或1个元素的列表,具体取决于谓词是否成立。除此之外,代码应该是相当不言自明的。
答案 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>