如何最好地映射列表的所有元素,除了最后一个列表元素?
假设我们有一个列表let l = [1,2,3,4]
,并希望得到[2,3,4,4]
。
我确实有一个解决方案,但它并不像"功能性"这样做的方式(在ghci中):
let l = [1,2,3,4]
let len = toIntegral $ length l -- to avoid a type mismatch Integer <-> Int
let l1 = zip l [1..]
let l2 = map (\(x,y) -> if y < len then (x + 1,y) else (x,y)) l1
let l3 = map (fst) l2
不太好......我希望有更好的方法!由于我是函数式编程的新手,我不知道从哪里开始寻找它。
答案 0 :(得分:13)
只需重新编写map
,但只有一个元素时才会出现特殊情况:
mapBut1 :: (a -> a) -> [a] -> [a]
mapBut1 f [] = []
mapBut1 f [x] = [x]
mapBut1 f (x:xs) = f x : mapBut1 f xs
现在即使对于无限列表也是如此,它比计算长度快得多,并使其更具可读性。请注意,这确实会将您的函数限制为a -> a
类型,而不是a -> b
。
或者,您可以
mapBut1 f (x:y:xs) = f x : mapBut1 f (y:xs)
mapBut1 f other = other
他们是等同的定义,但后者使用的模式匹配减少1。不过,我更喜欢前者,因为处理案件的情况更为明显。
答案 1 :(得分:9)
像往常一样,这是假装 - 变形的工作:
import Data.List (tails)
mapButLast :: (a -> a) -> [a] -> [a]
mapButLast f = foldr g [] . tails where
g (x:_:_) r = f x : r
g xs _ = xs
或者使用正确的para
,我们只需编写
mapButLast f = para g [] where
g x [] r = [x]
g x _ r = f x : r
,其中
para f z (x:xs) = f x xs (para f z xs)
para f z [] = z
答案 2 :(得分:3)
您也可以使用标准图书馆的init
and last
(无需特殊导入)如果您不关心表演。
map f (init list) ++ [last list]
f
:您要映射的功能list
:您要映射的列表答案 3 :(得分:1)
是什么让这最后一个元素如此特别?
我知道它没有回答你的问题,但我会考虑类似
的类型data Foo a = Foo a [a]
和适当的Functor。
答案 4 :(得分:0)
我只想添加一个使用列表理解的替代方案,并试图比较解决方案(我第一次尝试它,所以如果我没有做对,请告诉我)
reverse $ last l : [f x | x <- tail (reverse l)]
它比大多数其他解决方案慢(请参阅下面的报告链接)。
以下是示例程序(allbutlast.hs)。它具有上述所有解决方案,简单的quickcheck测试和使用criterion的基准测试。
运行的先决条件:QuickCheck和标准
cabal update
cabal install QuickCheck
cabal install -j --disable-tests criterion
执行
ghc -O2 --make allbutlast.hs
./allbutlast --output allbutlast.html