使用元组和列表的函数键入错误

时间:2018-12-13 19:52:51

标签: haskell

问题代码如下。

type Point = (Float, Float)
xs = []
arrange_in_pairs :: Point -> Point -> [Point] -> [(Point, Point)]
arrange_in_pairs (x, y) (xi, yi) points 
    | length xs == 0 = xs ++ [((x, y), (points !! 0))]
    | (length xs - 1) /= length points = xs ++ [((points !! (length xs - 1)), (points !! length xs))] arrange_in_pairs (x, y) (xi, yi) points
    | otherwise = xs ++ [((points !! length xs), (xi, yi))] xs

这个想法是一个点是一个坐标,就像在图形上一样。 (x,y)是起点,(xi,yi)是终点,“ points”是它们之间的点的列表。该函数应该将每对点作为一个元组并将它们存储在列表中。因此,第一个元组是(x,y),“点”中的第一个点。第二个元组是“点”中的第一个点,第二个点是“点”中的第二点,依此类推,直到列表中的最后一个元组是“点”和(xi,yi)中的最后一个点。任何帮助都非常感谢。

我得到的错误是:

*** Expression     : [(points !! (length xs - 1),points !! length xs)] arrange_in_pairs (x,y) (xi,yi) points
*** Term           : [(points !! (length xs - 1),points !! length xs)]
*** Type           : [((Float,Float),(Float,Float))]
*** Does not match : a -> b -> c -> d -> e

2 个答案:

答案 0 :(得分:2)

您收到的错误是由于以下原因:

[((points !! (length xs - 1)), (points !! length xs))] arrange_in_pairs (x, y) (xi, yi) points

在Haskell中的事物之间放置空格时,表示正在调用函数。在这种情况下,该行的第一部分:

[((points !! (length xs - 1)), (points !! length xs))]

被解释为一个函数,被四个参数调用:

arrange_in_pairs
(x, y)
(xi, yi)
points

这就是为什么说Does not match : a -> b -> c -> d -> e的原因。期望有一个带有四个参数并返回一个值的函数,但是它将看到类型为[((Float,Float),(Float,Float))]的值。


我认为您在这里尝试使用xs ++ [...]更新xs的值,然后返回值arrange_in_pairs (x, y) (xi, yi) points,也许吗?这有很多问题:

  • 如前所述,仅在两段代码之间放置空格是行不通的。 Haskell将其解释为函数应用程序。
  • ++运算符不更新值,它只是连接两个列表并返回结果。 xs不会更改。
  • 实际上,在Haskell中,所有变量都是不可变的,因此,如果您在全局范围内定义xs = [],则除[]之外,它将没有任何其他值。您需要使用其他方法,例如递归。
  • 您返回的值是带有未更改参数的递归调用。这等效于一个永不递增其计数器的循环:它将永远循环并永远不会完成任何工作。

@JorgeAdriano提到了一个更简洁的解决方案,但我认为它对理解原理没有太大帮助。如果要解决此问题,我的方法是尝试递归函数。为此,您需要做两件事:

  • 一个基本情况:如果points为空,我们将返回什么?
  • 递归的情况:我们可以使用points的第一个元素来决定结果的第一个元素应该是什么,我们还可以使用不同的参数再次调用该函数,以便为我们提供其余的输出。

列表上的递归函数取决于cons(:)构造函数。之所以提出这一点,是因为我注意到您没有在代码中使用它,但这很重要:

  • x:xs返回一个列表,其中第一个元素为x,其余元素为列表xs中的元素。
  • 当用作参数(在模式匹配中)时,x:xs将列表参数的第一个元素绑定到x,并将其余元素绑定到列表xs。如果列表为空,则x:xs不匹配。
  • []用作参数(在模式匹配中)时,仅在列表为空时匹配。

基本情况(points为空)是返回从起点到终点的单个线段。可以这样写:

arrange_in_pairs start end [] = [(start, end)]

递归情况更为复杂。我们知道第一个元素必须是从points的起点到第一个元素的一段,因此我们可以从以下开始:

arrange_in_pairs start end (x:xs) = (start, x) : ???

我们不是将第三个参数绑定到points,而是将它的第一个元素绑定到x,其余的元素绑定到列表xs。返回值是第一个元素(从startx的一段),返回到其余元素(我们尚未定义的列表)。

到目前为止,太好了。 ???应该是对arrange_in_pairs的递归调用,并且应该以这样的方式进行调用:返回的第一个段是我们需要的下一个段。我们需要的下一个段是从xxs的第一个元素,因此我们需要通过x代替startxs来代替points

arrange_in_pairs start end (x:xs) = (start, x) : arrange_in_pairs x end xs

完整功能如下:

arrange_in_pairs :: Point -> Point -> [Point] -> [(Point, Point)]
arrange_in_pairs start end [] = [(start, end)]
arrange_in_pairs start end (x:xs) = (start, x) : arrange_in_pairs x end xs

答案 1 :(得分:1)

提示:

  

所以第一个元组是(x,y),第一个点在“ points”中。第二个元组是“ points”中的第一个点,第二个点是“ points”中的第二个点,依此类推,直到列表中的最后一个元组是“ points”和(xi,yi)中的最后一个点。

有一个类型为{p>的函数zip

zip :: [a] -> [b] -> [(a, b)]

按位置将两个列表的元素配对。您还可以建立列表

list1 = [(x, y)] ++ points  
list2 = points ++ [(xi,yi)]

一旦有了,就真的很近了。