繁体   English   中英

haskell中解压功能的实现

[英]implementation of unzip function in 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)'

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

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的类型签名,然后找出你可以用什么来代替问号在where ??? = myUnzip xs where ??? = myUnzip xs

我认为展示两种替代解决方案可能会很有趣。 在实践中你不会使用这些,但它们可能会让你对 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

这并不完全有效的原因是,尽管您只遍历列表一次以提取对,但它们将以相反的顺序提取。 这就是需要在ident的定义中额外调用reverse的原因,这会导致对列表进行额外的遍历,以将其按正确的顺序排列。 我有兴趣了解一种解决方法(我希望当前的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]

这是克里斯泰勒使用(有些新的)“折叠”包编写的答案

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])

这是我在上述指导后得到的工作

myUnzip' [] = ([],[])
myUnzip' ((a,b):xs) = (a:(fst rest), b:(snd rest))
    where rest = myUnzip' xs
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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM