我是 Haskell 的新手,正在尝试解决如何阻止我的程序在列表末尾爆炸。
例如,我有一个函数可以镜像关于 XY 轴的字符列表。
如果没有 ,我该如何重写take
?
mirrorXY' :: [[a]] -> [[a]]
mirrorXY' m = (map head m) : mirrorXY' (map tail m)
mirrorXY m = take (length $ m!!0) $ mirrorXY' m
PS我刚刚找到transpose
,但我仍然想要一个答案。
首先,您mirrorXY'
可以使用高阶函数map
而iterate
不是直接递归来编写:
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
您需要处理空列表,例如
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)
它停在第一个太短的列表上。请注意,这使用s monad 实例通过线路Maybe
进行短路。mapM safeHead m
甚至可以更紧凑地编写最后一个版本,使用maybe
:
mirrorXY [] = []
mirrorXY m = maybe [] (: mirrorXY (map tail m)) $ mapM safeHead m
但这不一定更清楚。