[英]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.