【问题标题】:What is the generalisation of unzip?unzip的概括是什么?
【发布时间】:2016-03-01 10:25:12
【问题描述】:

我从列表中知道的大多数方法实际上都是一些知名类型类的特例。一些方法和相关类型类的例子:

  • map :: (a -> b) -> [a] -> [b]Functor
  • foldr :: (a -> b -> b) -> b -> [a] -> bFoldable
  • forM :: Monad m => [a] -> (a -> m b) -> m [b]Traversable
  • concat :: [[a]] -> [a]Monad

可能这个列表还在继续(请原谅双关语)。

我想知道unzip :: [(a, b)] -> ([a], [b]) 背后的“深层含义”。它可以使用[] 的一些著名实例来实现吗,例如(,a) 的仿函数实例?还是其他一些情况?理想情况下,我希望这种类型有一个更抽象的函数:SomeClass m => unzip :: m (a, b) -> (m a, m b)。是否有课程可以完成这项工作?

【问题讨论】:

标签: list haskell typeclass


【解决方案1】:

您可以简单地进行第一次和第二次预测:

gunzip :: Functor f => f (a, b) -> (f a, f b)
gunzip ps = (fmap fst ps, fmap snd ps)

但请注意gunzip 遍历ps 两次不同于通常的zip 用于列表,所以它很麻烦,因为ps 在第一次通过后不会被垃圾收集并停留在内存中,这会导致内存泄漏大名单。

【讨论】:

  • 好答案。我认为还值得注意的是,gunzip 专用于[] 的效率低于unzip(它需要两次而不是一次)。
  • 我认为它有时会更快,但内存泄漏会咬人。
  • 您可以一次性完成此操作,使用Control.Arrow: gunzip = fmap (fst &&& snd) 最容易。
  • @chepner,它不进行类型检查。 fst &&& snd ≗ idfmap id ≗ id。因此你的定义是gunzip = id
  • @chepner,这个函数是pointfree的,但这并不意味着它遍历列表一次。 (fmap fst &&& fmap snd) ps 立即减少为 (fmap fst ps, fmap snd ps),所以它是相同的功能。
【解决方案2】:

直观地说,unzip' 函数必须拆除一个包含 (a,b) 的结构,然后将其构建为结构的两个副本,第一个包含 a,第二个包含 @ 987654324@.

另一个答案中已经提到的一种可能的概括是

unzip' :: (Functor t) => t (a, b) -> (t a, t b)
unzip' xs = (fmap fst xs, fmap snd xs)

这符合要求,但一个缺点是它必须传递初始结构两次 - 每次调用 fmap 一次。


Foldable 类描述了可以迭代的结构,并在我们进行时构建结果。我们可以利用这个属性来确保我们只传递一次初始结构(一次调用foldr),但我们仍然需要知道如何再次构建结构的副本。

MonadPlus 类型类提供了获取空结构和组合两个结构的方法(有点像高阶 Monoid)-

class Monad m => MonadPlus m where
  mzero :: m a
  mplus :: m a -> m a -> m a

有了这个,我们可以写

import Control.Monad
import Data.Foldable (foldr)

unzip' :: (Foldable t, MonadPlus m) => t (a, b) -> (m a, m b)
unzip' = foldr f (mzero, mzero)
  where
    f (a,b) (as, bs) = (mplus (return a) as, mplus (return b) bs)

然后我们可以做类似的事情

>> unzip' [(1,2), (3,4)] :: ([Int], [Int])
([1,3],[2,4])

还有

>> unzip' (Right (1,2)) :: ([Int], Maybe Int)
([1],Just 2)

最后一个想法 - 我们需要调用mplus (return a) as 有点难看。有一个像这样的类可能会更好

class Listish m where
  null :: m a
  cons :: a -> m a -> m a

但我还没有真正探索过这部分设计空间。

【讨论】:

  • 但是unzip'“线性化”了一个结构:解压一棵树不会产生一个树元组,而是一个伪装的列表元组。
  • @user3237465 如果树是正确定义的Functor 实例,那么fmap 将生成树,而不是列表。 fmap 的类型是 a -> b -> f a -> f b,而不是 a -> b -> f a -> [b]
  • @chepner,我的意思是第二个unzip' = foldr f
  • 那不是Prelude.foldr;它适用于任何 Foldable 类型,而不仅仅是列表。
  • 不过,第二个unzip' 似乎确实存在问题;它只需要一个参数,但除了传递给foldr 的函数f 之外,它还需要提供两个参数。
猜你喜欢
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
相关资源
最近更新 更多