【问题标题】:Not in scope: type constructor or class ‘-’ (and other errors while reversing a list)不在范围内:类型构造函数或类“-”(以及反转列表时的其他错误)
【发布时间】:2019-03-18 18:40:02
【问题描述】:

我正在研究“七周内的七种语言”一书,Haskell 第一天的一个问题是“编写一个函数,该函数接受一个列表并反向返回相同的列表”。

我的第一个实现效果很好:

reverseList :: [a] -> [a]
reverseList [] = []
reverseList [x] = [x]
reverseList (h:t) = reverseList(t) ++ [h]

然后我想到了另一种方法,我尝试将其实现为:

reverseList1 :: [a] -> [a]
reverseList1 [] = []
reverseList1 [x] = [x]
reverseList1 l = last(l) :: reverseList1(take(length(l)-1)(l))

(即 - 取最后一个元素,然后将反向的 first-all-but-one 元素附加到它上面)。但是,当我尝试加载文件时,这给了我一个错误(实际上是三个):

[1 of 1] Compiling Day1             ( day1.hs, interpreted )

day1.hs:35:49: error: Not in scope: type constructor or class ‘-’
   |
35 |   reverseList1 l = last(l) :: reverseList1(take(length(l)-1)(l))
   |                                                 ^^^^^^^^^^^

day1.hs:35:49: error:
    Illegal operator ‘-’ in type ‘length (l) - 1’
      Use TypeOperators to allow operators in types
   |
35 |   reverseList1 l = last(l) :: reverseList1(take(length(l)-1)(l))
   |                                                 ^^^^^^^^^^^

day1.hs:35:59: error:
    Illegal type: ‘1’ Perhaps you intended to use DataKinds
   |
35 |   reverseList1 l = last(l) :: reverseList1(take(length(l)-1)(l))
   |                                                           ^
Failed, no modules loaded.

谷歌搜索“不在范围内”错误表明我正在尝试使用尚未导入的东西 - 我不认为我需要显式导入“减法”,基于其他实验。

我不明白第二个错误 (Use TypeOperators to allow operators in types) - 我从 here 看到 TypeOperators 是我可以在 ghci 上设置的标志,但我没有尝试 在类型中使用运算符 - length(l)-1 是(据我所知)take 的参数,而不是类型。

谷歌搜索“也许你打算使用 DataTypes”让我找到 here - 我希望我的代码不会像那个例子那样被撕裂,但我有可能同样一无所知!


基于上一个例子here,我也试过了:

reverseList1 l = last(l) :: reverseList1(take(takeLength)(l))
  where takeLength = length(l)-1

(它给出了一个(非常冗长!)以“无法匹配类型'a'”开头的错误)和

reverseList1 l =
  let takeLength = length(l)-1 in
  in last(l) :: reverseList1(take(takeLength)(l))

给了

day1.hs:40:5: error: parse error on input ‘in’
   |
40 |     in last(l) :: reverseList1(take(takeLength)(l))
   |     ^^
Failed, no modules loaded.

【问题讨论】:

  • 列表构造函数是:,而不是::。在第二次尝试中,您还写了两次in
  • 旁注:Haskell 函数的调用方式类似于 func arg1 arg2 arg3 ...。像func(arg1) 这样称呼他们是不好的风格,以后可能会引起问题。
  • 写两次in 是故意的(虽然,我承认,不直观!)因为here 底部的示例中有两个“ins”。我会尝试一个 - 谢谢!是的 - 这肯定是我错过的一个简单的错字(:/::),谢谢!我当然注意到了这种风格,并且我通常会尝试匹配它,但我在括号中加上了以确保我清楚什么叫什么。感谢您的提示!

标签: haskell


【解决方案1】:

列表是用: 构造的。 :: 表示类型,因此 Haskell 尝试将其作为类型读取,因此出现错误。

更正后的代码是:

reverseList1 :: [a] -> [a]
reverseList1 [] = []
reverseList1 [x] = [x]
reverseList1 l = last l : reverseList1 (take (length l - 1) l)

注意使代码更具可读性的括号!

在您的第二次尝试中,您写了两次in,解析器会感到困惑。

更正后的代码是:

reverseList1 l =
  let takeLength = length l - 1
  in last l : reverseList1 (take takeLength) l

现在,这个错误实际上很有趣。 TypeOperators 扩展是一种允许(不出所料)在类型声明中使用中缀运算符的扩展。

在这里,编译器注意到- 看起来像一个中缀运算符,并且鉴于它在类型签名中,推断您可能打算使用TypeOperators 扩展。当然,这里不是这样的!

至于DataKinds,这是一个相当高级的扩展,允许您将数据(如1"Hello" 等)放入类型中。我不会在这里详细说明它的用途,但编译器提到它的原因是您在类型签名中使用了1,它只能与DataKinds 扩展一起使用。

【讨论】:

  • 非常感谢!令人惊讶的是,额外的冒号可以对编译器解释命令的方式产生多大的影响:)
  • @scubbo 很像一个杂散的分号,有人可能会说 ;)
猜你喜欢
  • 2021-03-31
  • 1970-01-01
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多