初学者 Haskell 问题(没有实例......引起)

发布于 2024-12-10 09:20:48 字数 2228 浏览 0 评论 0原文

我最近开始学习 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

久随 2024-12-17 09:20:48

您必须将 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.

淑女气质 2024-12-17 09:20:48

其他人提到您应该使用 (&&) 而不是 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 个元素的列表,具体取决于谓词是否成立。

除此之外,代码应该是相当不言自明的。

Others have mentioned that you should use (&&) instead of and, 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!

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]

There's a few interesting things going on here:

  1. The special case is checked straight away. Don't wait until you're recursing to do this!
  2. The concatMap function is really handy in a lot of cases. It's just a map followed by a concat. If I were writing this myself, I'd probably use xs >>= translate instead.
  3. That ['_' | 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.

眼眸里的快感 2024-12-17 09:20:48

问题是这样的:

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

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

and :: [Bool] -> Bool

因此它将 1 和 Char.isUpper a 解释为将三个参数应用于“函数”1。使用 && 代替。

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

The problem is this:

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

and is not an infix operator; rather it is a function:

and :: [Bool] -> Bool

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 say x+1 and it will work whether x is an integer or a double or whatever. So the compiler inferred that the type of 1 needs to be a three-argument function, and then tried to find a numeric type matching that so it could convert 1 into that type (and, naturally, failed).

憧憬巴黎街头的黎明 2024-12-17 09:20:48

这是我的解决方案。它不像 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 实际上只是检查你的标准,即第一个字符不能是大写,如果不是,它会将字符串移交给函数 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.

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文