我应该如何从列表中删除给定元素?例如,假设我有列表['A'; 'B'; 'C'; 'D'; 'E']
并想要删除索引2处的元素以生成列表['A'; 'B'; 'D'; 'E']
?我已经编写了以下代码来完成任务,但是当我已经知道索引时,遍历列表的开头似乎效率很低。
let remove lst i =
let rec remove lst lst' =
match lst with
| [] -> lst'
| h::t -> if List.length lst = i then
lst' @ t
else
remove t (lst' @ [h])
remove lst []
let myList = ['A'; 'B'; 'C'; 'D'; 'E']
let newList = remove myList 2
或者,我应该如何在给定位置插入元素?我的代码与上述方法类似,也很可能效率低下。
let insert lst i x =
let rec insert lst lst' =
match lst with
| [] -> lst'
| h::t -> if List.length lst = i then
lst' @ [x] @ lst
else
insert t (lst' @ [h])
insert lst []
let myList = ['A'; 'B'; 'D'; 'E']
let newList = insert myList 2 'C'
答案 0 :(得分:25)
删除指定索引处的元素不是函数式编程中的典型操作 - 这就是为什么很难找到这些操作的正确实现。在函数式编程中,您通常使用递归逐个元素地处理列表,或者根据更高级别的声明性操作实现处理。也许如果你能澄清你的动机是什么,我们可以给出更好的答案。
无论如何,要实现你想要的两个操作,你可以使用现有的高阶函数(遍历整个列表几次,因为没有好的方法可以在不遍历列表的情况下执行此操作):
let removeAt index input =
input
// Associate each element with a boolean flag specifying whether
// we want to keep the element in the resulting list
|> List.mapi (fun i el -> (i <> index, el))
// Remove elements for which the flag is 'false' and drop the flags
|> List.filter fst |> List.map snd
要将元素插入指定的索引,您可以编写:
let insertAt index newEl input =
// For each element, we generate a list of elements that should
// replace the original one - either singleton list or two elements
// for the specified index
input |> List.mapi (fun i el -> if i = index then [newEl; el] else [el])
|> List.concat
但是,如前所述 - 除非您有充分的理由使用这些功能,否则您应该考虑更广泛地描述您的目标并使用替代(更多功能)解决方案。
答案 1 :(得分:15)
似乎是最惯用的(不是尾递归):
let rec insert v i l =
match i, l with
| 0, xs -> v::xs
| i, x::xs -> x::insert v (i - 1) xs
| i, [] -> failwith "index out of range"
let rec remove i l =
match i, l with
| 0, x::xs -> xs
| i, x::xs -> x::remove (i - 1) xs
| i, [] -> failwith "index out of range"
似乎相当低效 当我走过列表的开头 已经知道指数。
F#列表是单链接列表,因此您没有索引访问它们。但大多数时候,你不需要它。数组上的大多数索引操作都是从前到后的迭代,这正是不可变列表上最常见的操作。将项添加到数组的末尾也很常见,这对于单链表并不是最有效的操作,但大多数时候你可以使用“缺点和反向”成语或使用不可变队列来获取同样的结果。
如果您需要索引访问,Arrays和ResizeArrays确实是最佳选择,但它们不是不可变的。像VLists这样的一些不可变数据结构允许您创建支持O(1)cons和O(log n)索引随机访问的类似列表的数据结构,如果你真的需要它。
答案 2 :(得分:10)
如果您需要在列表中进行随机访问,请考虑使用System.Collections.Generic.List<T>
或System.Collections.Generic.LinkedList<T>
而不是F#列表。
答案 3 :(得分:0)
以下内容还包括一些错误检查
let removeAt index = function
| xs when index >= 0 && index < List.length xs ->
xs
|> List.splitAt index
|> fun (x,y) -> y |> List.skip 1 |> List.append x
| ys -> ys
让我们通过它并解释代码
// use the function syntax
let removeAt index = function
// then check if index is within the size of the list
| xs when index >= 0 && index < List.length xs ->
xs
// split the list before the given index
// splitAt : int -> 'T list -> ('T list * 'T list)
// this gives us a tuple of the the list with 2 sublists
|> List.splitAt index
// define a function which
// first drops the element on the snd tuple element
// then appends the remainder of that sublist to the fst tuple element
// and return all of it
|> fun (x,y) -> y |> List.skip 1 |> List.append x
//index out of range so return the original list
| ys -> ys
如果你不喜欢简单地在indexOutOfRange上返回原始列表的想法 - 将返回包装成某种东西
let removeAt index = function
| xs when index >= 0 && index < List.length xs ->
xs
|> List.splitAt index
|> fun (x,y) -> y |> List.skip 1 |> List.append x
|> Some
| ys -> None
我认为这应该比Juliet或Tomas&#39;建议,但莫里西奥的评论肯定会回家。如果需要删除或删除项目,其他数据结构似乎更合适。
答案 4 :(得分:0)
我知道这已经存在了一段时间了,但最近我不得不做这样的事情,我想出了这个解决方案,也许它不是最有效的,但肯定是最短的惯用代码我找到了它
let removeAt index list =
list |> List.indexed |> List.filer (fun (i, _) -> i <> index) |> List.map snd
List.Indexed
返回列表中的元组列表,列表中的索引和该位置的实际项目之后,只需过滤匹配输入索引的一个元组,然后获取实际项目。
我希望这可以帮助那些不太关心效率并想要简短代码的人