【发布时间】:2012-01-21 17:20:59
【问题描述】:
可能是一个简单的问题,但我浏览了文档并搜索了示例,但我仍然不确定答案。
如果我有这样的列表:
[1,2,3,4,5,6,7,8,9,0]
我想提取一个切片,比如从索引 4 到索引 8,即我想要:
[5,6,7,8,9]
在 Haskell 中执行此操作的惯用方式是什么?
【问题讨论】:
标签: haskell
可能是一个简单的问题,但我浏览了文档并搜索了示例,但我仍然不确定答案。
如果我有这样的列表:
[1,2,3,4,5,6,7,8,9,0]
我想提取一个切片,比如从索引 4 到索引 8,即我想要:
[5,6,7,8,9]
在 Haskell 中执行此操作的惯用方式是什么?
【问题讨论】:
标签: haskell
> drop 4 (take 9 [1,2,3,4,5,6,7,8,9,0])
[5,6,7,8,9]
【讨论】:
嗯,不是很实用,但也许可以改进一下?
(\(x,y) -> if 4 <= y && y <= 9 then [x] else []) =<< zip [1,2,3,4,5,6,7,8,9] [0..]
【讨论】:
map snd . filter (liftA2 (&&) (>= 4) (< 9) . fst) . zip [0..] 怎么样(注意 (< 9) 以匹配问题的行为)?
indexedFilter f = map snd . filter (f.fst) . zip [0..] 之类的东西或库中的概括?
你可能对Data.Vector (slice)感兴趣。
ghci> import Data.Vector
ghci> let v = fromList [1..10]
ghci> v
fromList [1,2,3,4,5,6,7,8,9,10]
ghci> slice 4 5 v
fromList [5,6,7,8,9]
注意Data.Vector 中的slice 将开始索引和切片长度作为输入。
【讨论】:
首先,这不是一个数组,而是一个列表。我不是(仅仅)迂腐,因为在 Haskell 中数组比列表更成问题。
也就是说,一种常见的方法是将take 和drop 一起使用:
Prelude> drop 4 . take 9 $ [1,2,3,4,5,6,7,8,9,0]
[5,6,7,8,9]
Prelude> take (9-4) . drop 4 $ [1,2,3,4,5,6,7,8,9,0]
[5,6,7,8,9]
后者效率更高。
【讨论】:
slice begin end = take (end - begin) . drop begin - 我也很确定 GHC 可以优化第一次实施的低效率。
take thunk 计算(额外的比较和增量)。虽然第二个版本一次性完成所有删除,但take 使用普通列表进行操作。显然,如果这对性能至关重要,则分析是必不可少的。可以这样想:要获取第一个值,第一个版本需要从take 获取5,而第二个版本只从take 获取一个元素。两个版本在 drop 和计算列表中的工作量相同。