在haskell中实现解压缩功能

时间:2013-12-03 00:54:53

标签: haskell

我正在尝试实现解压缩功能,我做了以下代码,但是我收到了错误。

myUnzip [] =()
myUnzip ((a,b):xs) = a:fst (myUnzip xs)  b:snd (myUnzip xs)

我知道问题出在第二行的右侧,但我确实知道如何改进它。 任何提示请。

我得到的错误是

ex1.hs:190:22:
Couldn't match expected type `()' with actual type `[a0]'
In the expression: a : fst (myUnzip xs) b : snd (myUnzip xs)
In an equation for `myUnzip':
    myUnzip ((a, b) : xs) = a : fst (myUnzip xs) b : snd (myUnzip xs)


ex1.hs:190:29:
Couldn't match expected type `(t0 -> a0, b0)' with actual type `()'
In the return type of a call of `myUnzip'
In the first argument of `fst', namely `(myUnzip xs)'
In the first argument of `(:)', namely `fst (myUnzip xs) b'

ex1.hs:190:49:
Couldn't match expected type `(a1, [a0])' with actual type `()'
In the return type of a call of `myUnzip'
In the first argument of `snd', namely `(myUnzip xs)'
In the second argument of `(:)', namely `snd (myUnzip xs)'

5 个答案:

答案 0 :(得分:8)

你可以通过遍历列表两次效率低下来

myUnzip [] = ([], []) -- Defaults to a pair of empty lists, not null
myUnzip xs = (map fst xs, map snd xs)

但这不是很理想,因为与仅循环一次相比,它必然会非常慢。为了解决这个问题,我们必须以递归方式进行此操作

myUnzip [] = ([], [])
myUnzip ((a, b):xs) = (a : ???, b : ???)
    where ??? = myUnzip xs

我会让你填空,但是从这里开始应该是直截了当的,只要查看myUnzip的类型签名,并找出你可以代替{{{{{{{ 1}}

答案 1 :(得分:4)

我认为展示两种替代解决方案可能会很有趣。在实践中你不会使用这些,但他们可能会开放你的想法Haskell的一些可能性。

首先,使用折叠直接解决方案 -

unzip' xs = foldr f x xs
  where
    f (a,b) (as,bs) = (a:as, b:bs)
    x               = ([],   [])

这使用名为foldr的组合子来遍历列表。相反,您只需定义组合函数f,它会告诉您如何将单个对(a,b)与一对列表(as, bs)合并,并定义初始值x

其次,请记住,有一个很好看的解决方案

unzip'' xs = (map fst xs, map snd xs)

看起来整洁,但执行输入列表的两次迭代。能够像这样简单地编写一些东西会很好,但是它只能在输入列表中迭代一次。

我们几乎使用Foldl库实现此目的。有关它为什么不起作用的解释,请参阅最后的注释 - 也许有更多知识/时间的人可以解释修复。

首先,导入库并定义标识折叠。您可能必须先运行cabal install foldl才能安装库。

import Control.Applicative
import Control.Foldl

ident = Fold (\as a -> a:as) [] reverse

然后,您可以定义折叠,以提取对列表的第一个和第二个组成部分,

fsts = map fst <$> ident
snds = map snd <$> ident

最后,您可以将这两个折叠组合成一个折叠列表的折叠

unzip' = (,) <$> fsts <*> snds

这不起作用的原因是虽然您只遍历列表一次以提取对,但它们将以相反的顺序提取。这就需要在reverse的定义中额外调用ident,这会导致列表的额外遍历,从而使其按正确顺序排列。我有兴趣了解一种解决这个问题的方法(我希望使用当前的Foldl库是不可能的,但是可能有一个类似的Foldr库可以放弃流式传输以便保持输入的顺序。)


请注意,这些都不适用于无限列表。使用Foldl的解决方案永远无法处理无限列表,因为在列表终止之前,您无法观察到左侧折叠的值。

但是,使用右侧折叠的版本应该工作 - 但目前它还不够懒惰。在定义中

unzip' xs = foldr f x xs
  where
    f (a,b) (as,bs) = (a:as, b:bs) -- problem is in this line!
    x               = ([],   [])

模式匹配要求我们在第二个参数中打开元组,这需要再评估折叠的一个步骤,这需要打开另一个元组,这需要再评估折叠的另一个步骤,等等。但是,如果我们使用无可辩驳的模式匹配(总是成功,而不必检查模式)我们得到恰当的懒惰 -

unzip'' xs = foldr f x xs
  where
    f (a,b) ~(as,bs) = (a:as, b:bs)
    x                = ([],   [])

所以我们现在可以做到

>> let xs = repeat (1,2)
>> take 10 . fst . unzip'  $ xs
^CInterrupted
<< take 10 . fst . unzip'' $ xs
[1,1,1,1,1,1,1,1,1,1]

答案 2 :(得分:2)

这是Chris Taylor's answer使用(有些新的)“folds”包编写的:

import Data.Fold (R(R), run)
import Control.Applicative ((<$>), (<*>))

ident :: R a [a]
ident = R id (:) []

fsts :: R (a, b) [a]
fsts = map fst <$> ident

snds :: R (a, b) [b]
snds = map snd <$> ident

unzip' :: R (a, b) ([a], [b])
unzip' = (,) <$> fsts <*> snds

test :: ([Int], [Int])
test = run [(1,2), (3,4), (5,6)] unzip'

*Main> test
([1,3,5],[2,4,6])

答案 3 :(得分:0)

以上是我在上述指导之后的工作

myUnzip' [] = ([],[])
myUnzip' ((a,b):xs) = (a:(fst rest), b:(snd rest))
    where rest = myUnzip' xs

答案 4 :(得分:0)

myunzip :: [(a,b)] -> ([a],[b])
myunzip xs = (firstValues xs , secondValues xs)
          where
            firstValues :: [(a,b)] -> [a]
            firstValues [] = []
            firstValues (x : xs) = fst x : firstValues xs

            secondValues :: [(a,b)] -> [b]
            secondValues [] = []
            secondValues (x : xs) = snd x : secondValues xs