【问题标题】:List comprehensions -require base case?列表推导 - 需要基本情况​​?
【发布时间】:2011-06-23 19:42:56
【问题描述】:

我不明白为什么这里需要基本情况​​:

-- perms :: Ord a => [a] -> [[a]]
perms [] = [[]]
perms xs = [ (x:ps) | x <- xs, ps <- perms (xs \\ [x])]

在我看来,从列表理解中它应该是自动的,但后来我注意到:

[ x:y | x<-[], y<-[] ]

计算结果为 [],而不是 [[]],这似乎令人惊讶。

即便如此,我也很惊讶它没有运行基本情况,但总是给出 [],这违反了类型签名。 有没有一种简单的方法来跟踪列表理解的执行? Debug.Trace.trace 似乎是原子的。

【问题讨论】:

  • 返回值[] 不违反类型签名。空数组仍然是[[a]]
  • 另外,为什么需要基本案例 - 似乎 LC 应该处理整个事情。还有,有什么简单的方法可以查看 LC 扩展的痕迹吗?

标签: haskell list-comprehension


【解决方案1】:

您可以将&lt;- 视为在左侧的表达式中使用右侧列表中的每个元素。 由于[] 没有元素,因此整个列表理解返回一个空列表。 如果[ x:y | x&lt;-[], y&lt;-[] ] 返回[[]] 会很奇怪,因为: 接受元素和列表。所以要生成[[]] y 必须是[] 但那么x 会是什么?

正如 KennyTM 所说,[] 的类型为 [[a]]。实际上[] 来自每一种类型: [a] [[a]] [[[a]]] 等等。如果不是,则函数无法返回空列表。

无论如何,忘记一些括号是一个非常常见的错误,这就是为什么需要类型注释。

【讨论】:

  • 我认为(不正确!),xs=[],所以 x=[] 和 []\[] = [],然后是 []:[] => [[]]。问 Gchi :type of [] 和 of [[]] 它确实以不同的方式显示它们,但我想一个包含另一个。
  • @guthrie:你猜对了。 [] 的类型是[a],适用于任何类型a,包括[b] 等类型。
  • @guthrie:您可能喜欢比较:t [] 的输出与:t [] :: [[a]] 在ghci 中的输出。前者给出了[] 的所谓原理类型——也就是最通用的类​​型。从某种意义上说,它是最通用的,可以通过将任何其他类型一致地替换任何类型变量来进行专门化,并且仍然给出有效的类型。
【解决方案2】:

让我们去糖吧!

[ x:y | x <- [], y <- [] ]

变成

do x <- []
   y <- []
   return (x:y)

现在,更脱糖!耶!

[] >>= \x -> ([] >>= \y -> return (x:y))

iirc,&gt;&gt;= 列表是 flip concatMapreturn 只是 \x -&gt; [x]

我们只做一点替换。

concatMap (\x -> ([] >>= \y -> return (x:y))) []

现在,你看到了吗? concatMap f [] 显然会评估为 []。因为concatMap f 就是concat . map f。所以将f 映射到空列表,然后连接结果。除非没有结果,所以最终评价是[]

【讨论】:

  • 如果您需要参考资料或不了解脱糖,请随时询问。
  • 谢谢。好办法。我得再多看几眼!我一直到 (x=[],y=[], return ([]:[]) - 但这意味着 return ([[]])。这是我缺少的 concatMap 步骤。
  • @guthrie 来自 C 或 Java 背景,其中return 表示“使用此值退出函数”,有时会对您这样做。永远记住 Haskell 的 return 是完全不同的。
【解决方案3】:

所以归根结底是什么意思

[ x:y | x<-[], y<-[] ]

阅读方式是,“x:y 的可能值是多少,x 没有可能值,而y 没有可能值?”希望应该清楚,在这种情况下,x:y 根本没有值,因为您需要x 的值和y 的值才能获得x:y 的可能性,并且你两个都没有。

这种为xy 选择可能值组合的一般模式称为笛卡尔积,使用“产品”一词的部分原因是可能的组合数量为等于x 的可能性数乘以 y 的可能性数。因此,如果x 的选择为零,y 的选择为零,则您可以期望0 * 0 = 0 选择两者的组合。

【讨论】:

  • 我猜“秘密”是,如果第一个来源用尽,则不会为链中的其他元素生成更多元素,因此不会返回任何内容 - 即 []。如果 x 的选择为零,则生成器链停止在那里 - 从未尝试过 y 生成(?)。我当然得到了生成器的整体逻辑,但没有得到这类事情的细节,一旦任何一个变量用尽,它是否会停止 - 似乎合乎逻辑,似乎是的?
【解决方案4】:

这是另一个提示,为什么会有一个空列表的基本案例。 n 元素有多少种排列?有n! 排列。 0! 是什么?是 1。所以perm [] 的结果列表的长度必须为 1。a 类型的类型和缺少元素会强制结果为[[]]。没有其他定义的值具有正确的类型和长度。

【讨论】:

  • 是的,谢谢 - 我知道理论和基本思想,只是还不熟悉 LC 的 Haskell 实现。
【解决方案5】:

如果您对表单有列表理解:

[ x:y | stuff ]

那么您将生成一个列表,其元素的形式为x:y,其中某些选择xystuff 确定。 [[]] 是一个列表,其元素对于任何xy 都不是x:y 形式,因此前者不能产生后者。

在从[ x:y | x &lt;- [], y &lt;- [] ] 中期待[[]] 时,您似乎在设置x = []y = [],然后考虑x:y = [[]]。这是错误的,有几个原因:

  • x 来自 xs 类型的 [a],因此 x 具有 a 类型,并且(通常)不是列表
  • 列表理解的结果是x:y 元素的列表,而不是单个元素。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-13
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多