关于列表,方括号和圆括号有什么区别?

问题描述

正如标题所暗示的,我不确定方括号和圆括号在列表中的区别。

Haskell 的 insert 定义了两个版本,一个使用方括号,另一个使用圆括号:

insert' :: Int -> [Int] -> [Int]
insert' y [] = [y]
insert' y (x:xs) | y <= x = y:x:xs | otherwise = x : insert' y xs
----
insert' y [x:xs] | y <= x = y:x:xs | otherwise = x : insert' y xs

insert' 的第二个定义不起作用的原因是什么?

它给出的错误信息,对于任何想知道的人:

test.hs:3:12: error:
    • Couldn't match expected type ‘Int’ with actual type ‘[Int]’
    • In the pattern: x : xs
      In the pattern: [x : xs]
      In an equation for ‘insert'’:
          insert' y [x : xs]
            | y <= x = y : x : xs
            | otherwise = x : insert' y xs
  |
3 | insert' y [x:xs] | y <= x = y:x:xs | otherwise = x : insert' y xs
  |  

解决方法

与几乎所有其他语言一样,Haskell 中的括号实际上没有任何作用。它们帮助解析器知道您打算将哪些内容组合在一起,仅此而已;它们不会改变它们内部事物的含义,如果解析器可以神奇地猜测您打算在每个选择点以哪种方式对事物进行分组,我们就根本不需要它们。所以所有这些模式的行为完全相同:

x
(x)
((x))
(((((((((x)))))))))

所有这些模式也完全相同:

x:xs
(x:xs)
((x:xs))
(((((((((x:xs)))))))))

有时我们必须使用后面的一个——比如(x:xs)——而不是第一个——x:xs——来通知解析器我们打算将这些东西组合在一起。但是括号不会改变模式的含义。

另一方面,方括号用于构造列表。它们具有实际的运行时含义:它们在内存中分配一个新的数据结构并对其进行初始化。例如,3 是一个数字,但 [3] 是一个具有单个元素的列表,[3,4] 是一个具有两个元素的列表,[[3]] 是一个列表列表,以及很快。相同的行为适用于模式:

x -- match anything (even a list!) and bind x to its value
[x] -- match a list with a single element,and bind x to that element's value
[x,y] -- match a list with two elements,and bind x and y to those elements' values
[[x]] -- match a nested list; the outer and inner lists both have one element; bind x to the first element's first element

现在我们可以解决您的具体示例。模式 x:xs 是匹配任何具有至少一个元素的列表的模式,将 x 绑定到第一个元素,将 xs 绑定到列表的其余部分。模式 (x:xs) 做的事情完全一样。另一方面,模式 [x:xs] 匹配只有一个元素的列表,因为有方括号;然后,该列表的唯一元素与模式 x:xs 进行匹配,具有上述行为。

这样做的一个后果是 x:xs 可以匹配任何类型元素的列表——比如 Int——但 [x:xs] 只能匹配元素为列表的列表。>

,

(x:xs) 作为模式将匹配任何具有 x 头部和 xs 尾部的非空列表。

[x:xs] 作为模式将匹配一个单例列表——一个只包含一个项目的列表——它是一个非空列表,匹配模式 (x:xs)。模式 [x:xs] 实际上等同于模式 [(x:xs)]。这是一个嵌套模式,Haskell 允许这样做。外部模式匹配单例列表,内部模式匹配非空列表。此位置的括号是可选的。

这就是为什么您的第二个定义暗示第二个参数的类型为 [[a]],但您已将其声明为 [Int]。而 Int 不能匹配 [a]。 (a 也被确定为 Int,因为您比较 xy,并且 y 第一个参数被声明为 {{ 1}},但这不会改变任何东西)。

Int