在OCAML中,我如何使用List.Map跳过一个项?

11

假设我有以下的代码:

List.map (fun e -> if (e <> 1) then e + 1 else (*add nothing to the list*))

有没有办法做到这一点?如果有,怎么做呢?

我想要在符合某些条件的情况下对项目进行操作,并在不符合条件的情况下忽略它。因此,List.filter 似乎不是解决方案。

7个回答

14

SML有一个函数叫做mapPartial,可以实现这个功能。不幸的是,OCaml中没有这个函数。但是,您可以像这样轻松地自己定义它:

let map_partial f xs =
  let prepend_option x xs = match x with
  | None -> xs
  | Some x -> x :: xs in
  List.rev (List.fold_left (fun acc x -> prepend_option (f x) acc) [] xs)

用法:

map_partial (fun x -> if x <> 1 then Some (x+1) else None) [0;1;2;3]

将返回[1;3;4]

或者你可以像ygrek指出的那样使用extlib中的filter_map


7

BatteriesExtlib 都提供了mapPartial的等效功能:它们扩展的List模块提供了一个filter_map函数,类型为('a -> 'b option) -> 'a list -> 'b list,允许映射函数进行选择性操作。


5
另一种解决方案是直接使用foldl函数:
let f e l = if (e <> 1) 
            then (e + 1)::l 
            else l
in List.fold_left f [] list  

但是我更喜欢 Michael Ekstrand 提供的 filter_map 方法。


4

或者,您可以对列表进行筛选,然后按如下方式在结果列表上应用地图:

let map_bis predicate map_function lst =
    List.map map_function (List.filter predicate lst);;

# val map_bis : ('a -> bool) -> ('a -> 'b) -> 'a list -> 'b list = <fun>

使用方法:

# map_bis (fun e -> e<>1) (fun e -> e+1) [0;1;2;3];;
- : int list = [1; 3; 4]

2

如果您想保留值,则可以将其映射到单例列表,如果不需要则将其映射为空列表,然后连接结果。

List.concat (List.map (fun e -> if (e <> 1) then [e + 1] else []) my_list)

1

自从4.08版本起,OCaml标准库就有了List.filter_map。因此,现在可以这样写:

List.filter_map (fun e -> if e <> 1 then Some (e + 1) else None)

1
使用
let rec process = function
  | 1 :: t -> process t
  | h :: t -> (h + 1) :: (process t)
  | []     -> []

或尾递归
let process = 
  let rec f acc = function
    | 1 :: t -> f acc t
    | h :: t -> f ((h + 1) :: acc) t
    | []     -> List.rev acc in
  f []

或者使用标准函数的组合。
let process l = 
  l |> List.filter ((<>)1)
    |> List.map ((+)1) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接