我正在尝试实现解压缩功能,我做了以下代码,但是我收到了错误。
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)'
答案 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