从Haskell函数中删除'take'

时间:2012-07-06 12:18:57

标签: list haskell

我是Haskell的新手,我正试图找出如何在列表结束时阻止我的程序爆炸。

作为一个例子,我有一个函数,它反映了关于XY轴的字符列表列表。

如何在没有take的情况下重写此内容?

mirrorXY' :: [[a]] -> [[a]]
mirrorXY' m = (map head m) : mirrorXY' (map tail m)
mirrorXY m = take (length $ m!!0) $ mirrorXY' m

P.S。我刚刚找到transpose,但我仍然想要一个答案。

2 个答案:

答案 0 :(得分:4)

首先,您的mirrorXY'可以使用高阶函数mapiterate而不是直接递归来编写:

mirr m = map (map head) . iterate (map tail) $ m

......正如你所发现的那样,这会打击空列表:

*Main> map (map head) . iterate (map tail) $ [[1..4],[2..5],[3..6]]
[[1,2,3],[2,3,4],[3,4,5],[4,5,6],[*** Exception: Prelude.head: empty list

让我们在没有第一部分的情况下试一试:

*Main> iterate (map tail) $ [[1..4],[2..5],[3..6]]
[[[1,2,3,4],[2,3,4,5],[3,4,5,6]],[[2,3,4],[3,4,5],[4,5,6]],[[3,4],[4,5],[5,6]],[
[4],[5],[6]],[[],[],[]],[*** Exception: Prelude.tail: empty list
*Main>

所以很容易解决:我们只需要停止点击输入列表中的[]

*Main> takeWhile (not.null.head) . iterate (map tail) $ [[1..4],[2..5],[3..6]]
[[[1,2,3,4],[2,3,4,5],[3,4,5,6]],[[2,3,4],[3,4,5],[4,5,6]],[[3,4],[4,5],[5,6]],[
[4],[5],[6]]]

所以,功能是

mirr xs = map (map head) . takeWhile (not.null.head) . iterate (map tail) $ xs

这预先假定所有子列表的长度相等(或至少第一个是最短的),但通过调整takeWhile中的测试很容易解决:

mirr xs = map (map head) . takeWhile (all (not.null)) . iterate (map tail) $ xs

答案 1 :(得分:2)

您需要处理空列表,例如

mirrorXY [] = []
mirrorXY ([]:_) = []
mirrorXY m = (map head m) : mirrorXY (map tail m)

假设列表长度统一。

更强大,类似

safeHead [] = Nothing
safeHead (a:_) = Just a

mirrorXY [] = []
mirrorXY m = case mapM safeHead m of
               Nothing -> []
               Just a -> a : mirrorXY (map tail m)

停在第一个太短的列表上。请注意,这会使用Maybe的monad实例通过mapM safeHead m行进行短路。


甚至可以使用maybe

更紧凑地编写最后一个版本
mirrorXY [] = []
mirrorXY m = maybe [] (: mirrorXY (map tail m)) $ mapM safeHead m

但这不一定更清楚。