初学者 Haskell 问题(没有实例......引起)
我最近开始学习 Haskell,我正在尝试用 Haskell 重写我在 python 面试中所做的一些事情。我正在尝试将字符串从驼峰式大小写转换为下划线分隔(“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)] -> [一个]?
I recently started learning Haskell and I'm trying to rewrite something I did for an interview in python in Haskell. I'm trying to convert a string from camel case to underscore separated ("myVariableName" -> "my_variable_name"), and also throw an error if the first character is upper case.
Here's what I have:
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..]
I realize It's pretty likely I'm going about this in a weird way, and I'd love advice about better ways to implement this, but I'd like to get this to compile as well. Here's the error I'm getting now:
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.
Any explanation of what's going on here would be great. I really don't understand where "(Num (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t))" is coming from. I'd think the type declaration for translate_helper would be something like [(a,b)] -> [a]?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您必须将
and
替换为&&
。第一个是一个函数(前缀),它接收布尔值列表并计算它们的和。第二个是真正的逻辑与。但错误消息有点令人困惑。每当我收到如此奇怪的错误消息时,我通常会开始用类型签名来注释我的代码。然后编译器能够为您提供更详细的错误描述。You have to replace
and
by&&
. The first one is a function (prefix) that receives a list of boolean values and calculates an and of them all. The second one is a true logical and. The error message is a little bit confusing though. Whenever I get such a strange error message, I usually start to annotate my code with type signatures. Then the compiler is able to give you a more detailed description of what went wrong.其他人提到您应该使用
(&&)
而不是and
,所以我会回答您的其他问题:不,我不认为您是以一种奇怪的方式处理这件事。但是...我确实认为它可以更加优雅!
这里发生了一些有趣的事情:
concatMap
函数在很多情况下都非常方便。它只是一个map
后跟一个concat
。如果我自己写这个,我可能会使用xs >>= translate
来代替。['_' | isUpper x]
是一个列表理解;这是一个可爱的习惯用法,用于创建一个包含 0 或 1 个元素的列表,具体取决于谓词是否成立。除此之外,代码应该是相当不言自明的。
Others have mentioned that you should use
(&&)
instead ofand
, so I'll answer your other question: no, I don't think you're going about this in a weird way.But... I do think it can be even more elegant!
There's a few interesting things going on here:
concatMap
function is really handy in a lot of cases. It's just amap
followed by aconcat
. If I were writing this myself, I'd probably usexs >>= translate
instead.['_' | isUpper x]
is a list comprehension; this is a cute idiom for making a list with either 0 or 1 elements in it, depending on whether a predicate holds.Other than that, the code should be fairly self-explanatory.
问题是这样的:
and
不是中缀运算符;相反,它是一个函数:因此它将
1 和 Char.isUpper a
解释为将三个参数应用于“函数”1
。使用&&
代替。错误消息来自数字的解释方式。一个数字,比如
1
,实际上是多态的;它获得的具体类型取决于所需的类型。这就是为什么你可以说x+1
,并且无论x
是整数还是双精度数或其他什么,它都会起作用。因此编译器推断1
的类型需要是一个三参数函数,然后尝试找到一个匹配的数字类型,以便将1
转换为该类型(当然,失败了)。The problem is this:
and
is not an infix operator; rather it is a function:So it is interpreting
1 and Char.isUpper a
as applying three arguments to the "function"1
. Use&&
instead.The error message comes from the way numerals are interpreted. A numeral, say,
1
is actually polymorphic; the specific type it gets depends on the type that is needed. That's why you can sayx+1
and it will work whetherx
is an integer or a double or whatever. So the compiler inferred that the type of1
needs to be a three-argument function, and then tried to find a numeric type matching that so it could convert1
into that type (and, naturally, failed).这是我的解决方案。它不像 Daniel Wagner 使用 concatMap 和列表理解给出的答案那么熟练,但对于初学者来说可能更容易理解。
函数 conv 实际上只是检查你的标准,即第一个字符不能是大写,如果不是,它会将字符串移交给函数 Change,后者会完成工作。它一一遍历所有字符,构建一个列表,如果字符是大写,它会添加一个下划线,后跟该字符的小写版本,否则如果字符已经是小写,它只会按原样添加。
Here's my solution. It's not as masterful as the answer Daniel Wagner gave using concatMap and the list comprehension, but it's perhaps easier to understand for the beginner.
The function conv really just checks your criterion that the first character must not be uppercase, and if it isn't it hands over the string to the function change, which does the work. It goes through all the characters one by one, building a list, and if the character is uppercase, it adds an underscore followed by the lowercase version of the character, otherwise if the character is already lowercase it just adds it as it is.