Haskell中如何从列表按多个指定索引选取对应元素
Haskell按索引列表提取目标元素的实现
你已经掌握的单索引取值写法可以直接扩展到多索引场景,不需要照搬命令式语言的循环逻辑,也完全可以用filter实现,两种方案的行为有明显区别,按需选择即可:
直接映射索引的写法
你原来用的head (drop idx myList)本身就是纯函数,只要把这个逻辑应用到索引列表的每一项上就能得到结果,Haskell里用map就能完成这个遍历,根本不用手写循环:
-- 完全复用你现有逻辑的写法 getAtIndices myList myIndices = map (\idx -> head (drop idx myList)) myIndices -- 用内置的!!运算符可以简化,myList !! idx 和 head (drop idx myList) 效果完全一致 getAtIndices' myList myIndices = map (myList !!) myIndices
这个方案的特点:
- 结果顺序和你传入的
myIndices顺序完全一致,比如传[3,1,5],结果就按第3位、第1位、第5位的顺序返回 - 如果索引列表里有重复值,结果里会对应返回重复的元素
- 和你原来的单索引写法有一样的问题:传负数、或者索引超出列表长度时会直接抛运行时错误,要更稳妥的话可以自己写个返回
Maybe类型的安全索引函数替换!!,自动跳过非法索引即可
基于filter的实现
用filter完全可以实现,只是思路要转个弯:filter是按条件筛选原列表元素,所以得先给原列表的每个元素绑上它对应的位置索引,再把索引在目标集合里的元素筛出来就行:
import qualified Data.Set as Set getAtIndicesFilter myList myIndices = map snd $ filter (\(idx, _) -> idx `Set.member` idxSet) (zip [0..] myList) where idxSet = Set.fromList myIndices
这里把索引列表转成Set是为了提升查询效率,要是直接用elem在列表里判断索引是否存在,索引多的时候性能会差很多。
这个方案的特点:
- 结果顺序和原列表的元素顺序完全一致,和你传入的索引列表顺序无关,哪怕传
[3,1,5],结果还是按第1位、第3位、第5位的原列表顺序返回 - 索引列表里的重复值不会导致结果出现重复元素
- 天然不存在索引越界问题,超出列表长度的索引会被直接忽略,不会抛错
选型建议
如果你需要严格匹配传入索引的顺序、或者需要支持重复索引取重复值,选第一种map的写法最简洁;如果你只需要拿到所有对应位置的元素、不需要保持索引的输入顺序,选第二种filter写法更稳定,不会因为非法索引崩溃。
内容的提问来源于stack exchange,提问作者halloleo
相关产品推荐
相关产品推荐

