【问题标题】:Why doesn't this function work if I use "[xs]" instead of "xs"?如果我使用“[xs]”而不是“xs”,为什么这个功能不起作用?
【发布时间】:2018-02-20 11:27:57
【问题描述】:
split :: [a] -> Int -> ([a], [a])
split [xs] n = 
    (take n [xs], drop n [xs])

如果我将变量指定为xs 而不是[xs],则相同的代码可以工作,两种情况下的签名都是相同的。使用[xs] 会给出模式不是详尽无遗的错误。我知道这说明我提供的输入没有包含在我的代码中,但不清楚幕后发生了什么。

测试输入:[1,2,3] 2

【问题讨论】:

  • 好吧,[xs] 匹配具有恰好一个元素的列表,然后该元素与xs 统一。不知何故,人们每隔一天就会问这个问题:(
  • 以后,请尽量给您的问题提供更具体的标题。 “Haskell 代码不起作用,为什么?”几乎没有说明你的问题。
  • @WillemVanOnsem 我同意,这个错误在这里太频繁了。我们可能需要写一个“参考”问题/答案,解决一般问题,以便我们可以关闭所有此类问题作为重复。我们这样做是为了限制单态性(在对 meta, IIRC 进行了一些讨论之后)。
  • 我有一个反问:为什么你认为[xs]xs 意思一样?
  • @DanielWagner 我不希望 [xs] 与 xs 具有相同的含义,但是正如下面提到的(ans 中的 cmets),在声明您使用 [] 用于列表的类型时,主要混淆出现在 Haskell 中。加上因为我不经常使用 Haskell,所以默认情况下编写了这样的代码。如问题中所述,我知道如何使代码正常工作,并且部分理解了编译器抱怨的内容,但仍然无法了解整体情况。感谢您的回答,现在我知道更多了。也许将其添加为参考问题答案是个好主意。

标签: haskell


【解决方案1】:

不知何故,很多人认为[xs] 作为模式意味着您统一xs 的列表。但这是不正确的,因为函数签名(无论是隐式派生的还是显式声明的)已经会阻止您编写使用非列表项调用函数的代码。

一个列表有两个构造函数:

  • 空列表[];和
  • “缺点”(h : t) h head(第一个元素),t tail(包含其余元素的列表)。

Haskell 也引入了一些语法糖。例如,[1](1:[]) 的缩写,[1, 4, 2](1:(4:(2:[]))) 的缩写。

这意味着如果你写[xs],在幕后你定义了一个模式(xs: []),这意味着你将所有列表与恰好一个元素匹配,并且那个单个元素(不是整个列表)然后是xs

无论如何,解决方案是使用:

split <b>xs</b> n = (take n <b>xs</b>, drop n <b>xs</b>)

由于take :: Int -&gt; [a] -&gt; [a]drop :: Int -&gt; [a] -&gt; [a] 在签名中都包含xs 应该是一个列表,Haskell 会自动推导出n 应该是一个Int,而xs 应该是一个@ 987654344@.

请注意,您也可以使用splitAt :: Int -&gt; [a] -&gt; ([a], [a])。我们可以使签名与您的目标签名相同:

split = flip splitAt

【讨论】:

  • 我实际上更喜欢您的评论,因为这非常清楚地解释了我(或我的)问题。认为这可能是一个典型的初学者问题,部分原因是函数签名中的 [a] 表示列表,而代码中的相同 ([a]) 表示包含单个值的列表!
  • @peeyushsingh 是的,“[]-the-type vs []-the-value”双关语会让初学者感到困惑。您必须记住,类型和值存在于不同的世界中。当您定义data Foo = Foo 时,左侧定义了一个名为Foo 的类型,而右侧定义了一个值,巧合的是,它也被命名为Foo(但在不同的命名空间中)。这与[][] 的情况相同。
  • @peeyushsingh:您可以将[a] 类型视为普通前缀语法[] a 的语法糖——您可以使用它来代替,就像其他前缀类型构造函数(例如Maybe a)一样。例如,写[[Int]] 比写[] ([] Int) 在视觉上更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-16
  • 2018-11-04
  • 1970-01-01
  • 2018-09-14
  • 1970-01-01
相关资源
最近更新 更多