【问题标题】:Adding something to the beginning of a list with ':' operator in Haskell在 Haskell 中使用 ':' 运算符将某些内容添加到列表的开头
【发布时间】:2015-10-30 17:06:57
【问题描述】:

几天前我开始学习 Haskell,学习了基础知识,还有一些我无法理解的东西。

首先, 当我尝试在 GHCi 控制台中使用 ':' 运算符将某些内容添加到列表的开头时,它仅在我尝试添加单个文字而不是列表时才有效。 (5:[1,2,3,4]) 会起作用,但 ([1,2]:[3,4,5]) 不会。但是有这个功能:

replicate2 n x
| n <= 0    = []
| otherwise = x:replicate2 (n-1) x

据我了解,它是如何递归工作的,让我们调用replicate2 2 [1,2]: 它会滑过第一个守卫,因此:

= [1,2]:replicate (2-1) [1,2]

= [1,2]:[1,2]:replicate (1-1) [1,2] 

 -- since n is now 0 it's an edge condition and first guard returns [], so recursion ends and we get:

= [1,2]:[1,2]:[]

函数返回[[1,2],[1,2]]

我的问题:这是如何工作的? Haskell 不应该生气并吐出错误吗?当我尝试在 GHCi 中这样做时,它确实如此。

【问题讨论】:

  • (:) 运算符将一项添加到列表的开头。这就是为什么1:[2,3,4] 可以,但[1,2]:[3,4] 不行;您正在尝试将 list 添加到前面。
  • 是的,没错,我的问题是关于我提供的功能。它确实有效,我不明白为什么,因为它不应该。
  • 如果您正在构建一个列表的列表,那么列表就是“一个项目”。如果这是有道理的......
  • 请注意,您可以使用1:2:[3, 4, 5],因为它具有右关联性。
  • [1,2]:[1,2]:[] 在 ghci 中应该可以正常工作。

标签: list haskell recursion functional-programming where


【解决方案1】:

好吧,让我们回顾一些事情。 : 运算符的 principal(= 最一般的)类型是这样的:

(:) :: a -> [a] -> [a]

a 是一个类型变量。这整个事情的意思是,每次实际使用运算符时,它的使用上下文决定了a 在该特定用途中代表什么类型。所以如果你这样做:

example1 :: [Integer]
example1 = 5 : [1, 2]

...在那种情况下aInteger,如果你这样做:

example2 :: [[Integer]]
example2 = [1,2] : [[1,2]]

...那么在这种情况下a[Integer]。那么类型是什么意思:

(:) :: a -> [a] -> [a]

...是您放在列表头部的值的类型a 必须与进入该列表的值的类型相同。如果它是一个元素为Integers 的列表,那么该值也必须是Integer。在让您感到困惑的示例中,我们有一个Integers 列表列表,因此: 在该上下文中的第一个参数是Integer 列表。

Haskell 对此非常精确,因此我们可以继续使用其他示例来说明相同的原理。例如,我们将列表列表放在列表列表的前面:

example3 :: [[[Integer]]]
example3 = [[1, 2], [3, 4]] : [[[1, 2]]]
-- Value: [[[1, 2], [3, 4]], [[1, 2]]] 

所以再次回到 : 的主要类型:

(:) :: a -> [a] -> [a]

另一种看待它的方式是作为一个模式: 的所有用法都必须遵守:第一个参数的类型必须比第二个参数和结果。它告诉我们的另一件事是: 并不关心a 可能是什么类型——它可以是像Integer 这样的简单值,也可以是像[[[[Integer]]]] 这样的复杂值,而这个函数只是无法区分。

【讨论】:

    【解决方案2】:

    我的问题:这是如何工作的?我不明白为什么,因为它不应该。

    你错了,它不应该,你错了,因为你忘记了 a 也可能意味着 [a]

    让我们看一些类型方程:

    []          :: [a]
    (:)         :: a -> [a] -> [a]
    [1,2,3]     :: [Int]
    0 : [1,2,3] :: [Int]
    

    现在有一个转折:

    []                 :: [[a]]
    (:)                :: [a] -> [[a]] -> [[a]]
    [[1,2], [3]]       :: [[Int]]
    [0] : [[1,2], [3]] :: [[Int]]
    

    列表列表仍然是列表,其中:附加的元素是...列表。

    【讨论】:

    • 好吧,现在更清楚了,但仍然如此。在函数 replicate2 中,我没有在构建列表列表的任何地方定义。如果在 CHCI 中尝试 5:6 之类的东西,它不会自动构建一个列表 [5,6] 它会引发错误。如果我尝试[1,2]:[3,4],它会自动增加[[1,2],[3,4]]。然而,在这个函数中,我基本上是这样做的:[1,2]:[1,2]:[] 并且它返回 [[1,2],[1,2]]。你明白我的意思吗?如果您查看 replicate2 函数,它会将列表添加到列表中,而不是将列表添加到列表列表中。这种魔法是如何发生的?
    • 哦不..等等,如果我用 :t 检查函数类型定义,它会说: replicate2 :: (Num i, Ord i) => i -> a -> [a] 。所以它定义为返回一个列表,因为我将列表添加到那个列表中它返回一个列表列表?我想我明白了。
    • @EasternDude 你说:“如果我尝试 [1,2]:[3,4] 它不起作用,但这个函数本质上是在做 [1,2]:[1,2 ]:[]"。好吧,最后那额外的:[] 让一切变得不同!使用[1,2]:[3,4],您尝试将元素[1,2] 添加到具有元素34 的列表中,并且[1,2]3 具有不同的类型。但是对于[1,2]:[1,2]:[],您首先将元素[1,2] 添加到没有元素的列表中(因此还没有类型不匹配),然后将元素[1,2] 添加到带有一个元素[1,2] 的列表中。它们具有相同的类型,因此没有类型不匹配。
    猜你喜欢
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 2018-05-14
    相关资源
    最近更新 更多