在haskell中发出理解非递归列表理解concat的问题

时间:2013-07-08 05:22:37

标签: haskell list-comprehension

Hello I'am目前正在攻读考试并且在回答主题时遇到问题,作为标题状态,目标是使用理解列表创建非递归concat函数,查看解决方案:

concat3 :: [[a]] -> [a]
concat3 xss = [x | xs <- xss, x <-xs]

然而我无法理解为什么会有效,任何帮助都会受到赞赏。

2 个答案:

答案 0 :(得分:10)

列表理解箭头(<-)可以读作“in”,如[x | xs <- xss, x <- xs]读取“x表示xs中的xs和xs中的x”,表示我们正在解压缩列表中的每个列表 - 列表的组成元素 - 有点像concat

但是,有很多方法可以查看它。


从机制上讲,列表推导转换为do符号

do xs <- xss
   x  <- xs
   return x

do表示法转换为(>>=)(>>)

xss >>= \xs -> xs >>= \x -> return x
当我们在列表上实例化时,

然后(>>=)本身会变成concatMapreturn(\x -> [x])

concatMap (\xs -> concatMap (\x -> [x]) xs) xxs

如果您考虑concatMap (\x -> [x]),您可能会将其视为通过列表,将每个元素转换为单个列表,然后将它们连接起来......这只是一种无所作为的复杂方式。

concatMap id xss

从我们的concatMap定义

concat (map id xss)

最后只是(来自Functor法则!或常识)

concat xss

所以这个函数就像concat一样工作也不足为奇。


如何解释do符号,因为我们倾向于在“list monad”中进行语义思考?

do xs <- xss
   x  <- xs
   return x

从本质上讲,这可以理解为“从我们的列表列表中选择非确定性的组成列表之一,然后从该列表中选择非确定性的一个元素 - 从此过程中收集所有可能性”这再次导致了我们只是连接的想法。


我们也可以从Control.Monad函数join

中获取幸运的对应关系
join              :: (Monad m) => m (m a) -> m a  -- this looks `concat`-like!
join x            =  x >>= id

如果我们考虑内部xs >>= \x -> return x然后使用eta-conversion,我们只有xs >>= return xss >>= \xs -> xs >>= \x -> return x === xss >>= \xs -> xs >>= return === xss >>= \xs -> xs === xss >>= id === join xss ,这有助于我们看到

join

然后我们可以查看如何在列表monad中实例化join = concat并查看concat


因此,有很多方法可以通过列表推导来实现{{1}},具体取决于您想要如何理解列表推导。最重要的是,这些都是等价的,可以相互建立,形成列表及其monad实例真正含义的基础。

答案 1 :(得分:7)

您可以将列表理解描绘为嵌套循环。所以,

[ z | x <- list1, y <- list2 ]

表示“为x中的每个list1y中的每个list2,产生z”,结果列表是所有列表按顺序产生价值。请注意,此处要生成的值z在符号中排在第一位。所以,如果我们有:

[ (x,y) | x <- [1,2], y <- [3,4,5] ]

这表示“对于x中的每个[1,2],对于y中的每个[3,4,5],都会产生(x,y)”,因此我们得到:

[ (1,3), (1,4), (1,5),   -- when x = 1
  (2,3), (2,4), (2,5) ]  -- when x = 2

配备了列表推导的助记符,我们可以阅读您的concat3定义。

concat3 xss = [ x | xs <- xss, x <- xs ]

我将重命名变量以便于阅读:

concat3 listOfLists = [ x | list <- listOfLists, x <- list ]

我们现在可以将其视为“list中的每个listOfListsx中的每个list,产生x”。也就是说,从第一个列表中获取所有元素,然后从第二个列表中获取所有元素,依此类推,这对应于连接所有列表。

我使用的命名不太可能在野外看到。对于用于表示列表的变量,通常使用以s结尾的“复数”名称。将xs称为“exes”。考虑到语言学上的类比可能太过分了(但它仍然是常见的惯例),我们将列表列表“{double}复数化”xss。我通常不会发音,因为“发出”的声音太傻了。因此,您可以通过名称看到xss是一个列表列表,xs是一个列表,您可以帮助阅读这些密集表达式。