列表理解 - 需要基础案例?

时间:2011-06-21 12:21:30

标签: haskell list-comprehension

我不明白为什么需要基本案例:

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

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

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

评估为[],而不是[[]],这似乎令人惊讶。

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

5 个答案:

答案 0 :(得分:8)

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

正如KennyTM所说,[]的类型为[[a]]。实际上[]来自这种类型: [a] [[a]] [[[a]]]等等。如果不是那么函数就没有办法返回一个空列表。

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

答案 1 :(得分:4)

让我们去糖!

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

变成

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

现在,更加脱糖!耶!

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

iirc,>>=列表为flip concatMap,而return只是\x -> [x]

我们只做一点替代。

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

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

答案 2 :(得分:3)

所以它归结为

的含义
[ x:y | x<-[], y<-[] ]

阅读此方法的方法是,“当x:y没有可能的值且x没有可能的值时,y有哪些可能的值?”希望很明显,在这种情况下,x:y根本没有值,因为您需要x的值和y的值才能获得可能性对于x:y,你没有。

选择xy的可能值组合的一般模式称为笛卡尔积,而“product”一词的使用部分是因为数字可能组合的数量等于x的可能性数量y的可能性数量。因此,如果x选项为零,y选项为零,则可以选择0 * 0 = 0选择两者的组合。

答案 3 :(得分:1)

这是另一个提示为什么有一个带有空列表的基本情况。 n个元素有多少个排列?有n!个排列。什么是0!?它是1.因此perm []的结果列表的长度必须为1.类型a的类型和缺少强制结果为[[]]。没有其他定义的值具有正确的类型和长度。

答案 4 :(得分:1)

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

[ x:y | stuff ]

然后您正在生成一个列表,其元素的格式为x:y,用于xy的某些选项,由stuff确定。 [[]]是一个列表,其元素的格式不是x:yx的{​​{1}},因此前者无法生成后者。

在期待y的{​​{1}}时,您似乎设置了[[]][ x:y | x <- [], y <- [] ],然后考虑x = []。这有两个原因:

  • y = []来自x:y = [[]]x,因此xs的类型为[a],而且(通常)不是列表
  • 列表推导的结果是x元素的列表,而不是单个元素。