【问题标题】:Error with using sort on type a in Haskell在 Haskell 中对类型 a 使用排序时出错
【发布时间】:2020-11-18 11:34:44
【问题描述】:

我对 Haskell 很陌生,我正在尝试编写一个函数 neighbours :: Int -> Metric a -> Point a -> [Point a] -> [Point a] 以便 neighbours k d p xs 返回 k 个最近邻居的列表,按照距离顺序,根据度量 d 到列表 xs 中的点 p点。我的代码是

type Point a = (a, a)
type Metric a = Point a -> Point a -> Double
type Tuple a = (Double, Point a)

create:: Metric a -> Point a -> [Point a] ->  [Tuple a] -> [Tuple a]
create d p (x:xs) ys | length xs == 0 = sort(((d p x), x) : ys)
                     | otherwise      = create d p xs (((d p x), x) : ys)

takeP:: Tuple a -> Point a
takeP (_,p) = p   

pList::  [Tuple a] ->[Point a]-> [Point a]
pList (x:xs) ys | length xs == 0 = reverse (takeP x : ys)
                | otherwise      = pList xs (takeP x : ys) 

neighbours :: Int -> Metric a -> Point a -> [Point a] -> [Point a]--
neighbours k d p xs = take k (pList (create d p xs []) [])

但我在排序时遇到错误:

* No instance for (Ord a) arising from a use of `sort'
  Possible fix:
    add (Ord a) to the context of
      the type signature for:
        create :: forall a.
                  Metric a -> Point a -> [Point a] -> [Tuple a] -> [Tuple a]
* In the expression: sort (((d p x), x) : ys)
  In an equation for `create':
      create d p (x : xs) ys
        | length xs == 0 = sort (((d p x), x) : ys)
        | otherwise = create d p xs (((d p x), x) : ys)

我一开始使用type Point a = (Int, Int),它工作正常,但在规范中要求Point 是type Point a = (a, a),这导致了我的错误。另一个问题是我不能更改函数类型,所以我不能按照建议添加(Ord a)

有没有办法按第一个变量对元组列表进行排序而不会遇到错误?

【问题讨论】:

  • 既然你sort((d p x), x) : ys上,你需要给create加上类型约束,所以create:: Ord a => Metric a -> Point a -> [Point a] -> [Tuple a] -> [Tuple a]
  • 还要注意length xs在这里不是一个好主意,因为它需要线性时间,您可以改用null xs
  • 添加Ord a => 需要将其也添加到neighbours,这将更改所需的函数类型,即neighbours :: Int -> Metric a -> Point a -> [Point a] -> [Point a]
  • 感谢length xs的留言
  • 是的,当然类型约束是可传递的。如果只想对Double进行排序,可以改用sortOn fst

标签: sorting haskell


【解决方案1】:

在您的create 函数中,您使用sort :: Ord a => [a] -> [a]

… = <b>sort</b> (((d p x), x) : ys)

这意味着我们正在排序的对象类型,在本例中为Tuple a,需要是Ord 类型类的实例。一个 2 元组是 Ord 类型类的一个实例,如果项目的两个类型也是 Ord 的实例,那么在这种情况下是 DoublePoint a。因为Point a 也是一个二元组,但是是两个as,因此这意味着Tuple aOrd 的一个实例,如果aOrd 的一个实例。因此,您应该添加一个类型约束:

create :: Ord a => Metric a -> Point a -> [Point a] ->  [Tuple a] -> [Tuple a]
create d p (x:xs) ys | length xs == 0 = sort(((d p x), x) : ys)
                     | otherwise      = create d p xs (((d p x), x) : ys)

create 函数利用了一些反模式,例如使用 length,这需要线性时间。实际上,您可以将其重写为对映射进行排序:

create :: Ord a => Metric a -> Point a -> [Point a] -> [Tuple a]
create d p = sort . map f
    where f x = (d p x, x)

这删除了ys 参数,这里似乎只用作累加器。

如果您只想对 2 元组的第一项进行排序,可以使用 sortOn :: Ord b =&gt; (a -&gt; b) -&gt; [a] -&gt; [a]

create :: Metric a -> Point a -> [Point a] -> [Tuple a]
create d p = sortOn fst . map f
    where f x = (d p x, x)

【讨论】:

  • 我在create 函数上使用了您的建议,并在必要时添加了Ord a =&gt;,似乎自动化测试没有抱怨将Ord a =&gt; 添加到函数类型并且测试通过了所以我想它工作正常。感谢您的快速回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2020-11-06
相关资源
最近更新 更多