F#从列表中插入/删除项目

时间:2010-05-22 22:14:48

标签: algorithm list f#

我应该如何从列表中删除给定元素?例如,假设我有列表['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'

5 个答案:

答案 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返回列表中的元组列表,列表中的索引和该位置的实际项目之后,只需过滤匹配输入索引的一个元组,然后获取实际项目。

我希望这可以帮助那些不太关心效率并想要简短代码的人