首页 > 解决方案 > 使用元组和列表的函数类型错误

问题描述

问题代码如下。

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)和“点”中的第一个点。第二个元组是 "points" 中的第一个点和 "points" 中的第二个点,依此类推,直到列表中的最后一个元组是 "points" 和 (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

标签: haskell

解决方案


你得到的错误是因为这一行:

[((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。它期待一个接受四个参数并返回一个值的函数,但它看到的是一个 type 的值[((Float,Float),(Float,Float))]


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

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

@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到 list ,其余元素绑定到 list xs。返回值是第一个元素(从startto的一个段x),consed 到其余元素(我们尚未定义的列表)。

到目前为止,一切都很好。???应该是对 的递归调用,arrange_in_pairs并且应该以这样的方式调用它,即它返回的第一个段是我们需要的下一个段。我们需要的下一段是 fromx到 的第一个元素xs,所以我们需要通过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

推荐阅读