【问题标题】:What is the difference between square brackets and parentheses in regard to lists?关于列表,方括号和括号有什么区别?
【发布时间】:2021-05-12 07:04:45
【问题描述】:

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

定义了两个版本的 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
  |  

【问题讨论】:

  • FWIW,这个问题经常出现。我总是觉得这很有趣,这会让人们专门寻找列表,而不是其他数据类型。例如,对于data Tree a = Leaf | Branch (Tree a) (Tree a),没有人会问为什么Tree Branch l r 不是匹配分支的正确模式。这不仅仅是前缀与中缀或命名空间冲突。同样,没有人问为什么Complex a :+ b 不是匹配复数的正确模式。但是当它突然列出一些类型的残余应该/可能出现在模式中?人脑就是这么复杂!
  • 真的很简单。 [x] 作为类型匹配任意长度的列表。 [x] 作为一个模式看起来完全一样。
  • IIRC 有人询问有关使用 Branch (Tree l) (Tree r) 作为模式的问题。
  • @DanielWagner:我认为这是一个误导,因为[x] 类型的意思是“x 类型的任意数量值的列表”,[] x 的糖,而模式[x] 表示“价值x 的单一列表”,糖表示完全不同的东西,x : [] = (:) x []。在一个上下文中使用[] 作为“标志”使得在看起来相似的上下文中尝试使用这种方式是合理的。如果我们有其他类型的糖,比如Maybe 拼写为data a? = () | a?,我敢打赌我们会看到类似的混淆f (x?) 应该匹配什么。
  • @DanielWagner:我确实经常看到这种类型的困惑,无论是在这里还是在 /r/haskell 和 /r/haskellquestions 中的 Reddit 上。它会产生许多不同的范围/类型错误,并以多种方式表现出来——例如,Maybe x 而不是 Just xBranch a (Tree l) (Tree r)Tree x l rTree (Branch x l r)Branch b + left b/value b 而不是 Branch x l r;或给定data Tree = Leaf a | Node [Tree a],使用Node [Tree a] 而不是Node ts。 H98 data 定义(相对于GADTSyntax)的教学困难导致将Foo f 解释为f :: Foo

标签: haskell recursion types pattern-matching type-mismatch


【解决方案1】:

与几乎所有其他语言一样,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] 只能匹配其元素是列表的列表。

【讨论】:

    【解决方案2】:

    (x:xs) 作为模式将匹配任何非空列表,其头部为 x,尾部为 xs

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

    这就是为什么你的第二个定义暗示第二个参数的类型是[[a]],但你已经声明它是[Int]。而Int 不能匹配[a]。 (a 也被确定为Int,因为您比较了xy,而y 第一个参数被声明为Int,但这不会改变任何事情) .

         [ [a] ]
         [ Int ]
     ----------------
      ***mismatch***
    

    【讨论】:

    • 可能值得明确指出,(x:xs) 的括号后面没有句法“魔法”,它们只是因为优先级而使用(insert y x:xs 将被解析为 @987654341 @)
    • 表明在其他位置,除了形式参数之外,它不需要括号(如 \(xs::[Int]) -&gt; case xs of { hd:tl -&gt; 0; _ -&gt; 1 } 中的单行)可能很有用。
    • @pedrofurla 一个具体问题,一个具体答案。 :) 此外,它很复杂,有时会,有时不会,所以为什么不必要地使答案复杂化。
    • 嗯,说明()和[]不一样。
    • @pedrofurla 您的示例在没有扩展名的情况下无效。当然 () 不是 [],问题不是问它们是否相同,而是问有什么区别以及为什么会导致他们观察到的错误。
    猜你喜欢
    • 2012-02-12
    • 2014-04-18
    • 2016-06-26
    • 2013-04-01
    • 2012-04-05
    • 2021-05-16
    • 1970-01-01
    • 2019-02-22
    相关资源
    最近更新 更多