【发布时间】:2015-01-31 21:40:34
【问题描述】:
我想对列表中的每个第二个元素应用一个函数:
> mapToEverySecond (*2) [1..10]
[1,4,3,8,5,12,7,16,9,20]
我写了以下函数:
mapToEverySecond :: (a -> a) -> [a] -> [a]
mapToEverySecond f l = map (\(i,x) -> if odd i then f x else x) $ zip [0..] l
这可行,但我想知道是否有更惯用的方式来做这样的事情。
【问题讨论】:
-
当然还有其他方法。但我投票结束的理由是,在这种情况下,什么是“更好”的东西似乎非常主观。如果您可以描述您在实施中遇到的具体问题,那么我们就可以着手解决该问题。
-
我支持@DanielWagner,但这是一个标准的有趣答案:
map2nd f = zipWith ($) (cycle [id, f])。 -
@J.Abrahamson,从基础 4.8 开始,有时会比以前的版本慢(以前的版本是阴暗的)。你可以改用
map2nd f xs = zipWith (flip ($)) xs (cycle [id, f])。 -
你说得对,“更好”是主观的。但是当我刚刚开始学习 Haskell 时,我很想看到有经验的 Haskell 的解决方案。当我找到这个解决方案时,我只是想知道“就是这样”......来自 for-loop-languages 这感觉非常不同。谢谢@J.Abrahamson 的回答。
-
@BenjaminHodgson,这与编译器无关,也与部分应用程序无关。
zipWith长期以来一直试图与它的任何一个列表参数融合。不幸的是,与 right 列表参数融合实际上在语义上是错误的,并且可能会产生底部,当使用优化编译时不应该出现底部,否则不会。所以我推动将其删除,所以zipWith只会与它的 left 列表参数融合。