Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I skip a term with List.Map in OCAML?

Tags:

ocaml

Suppose I have some code like this:

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

Is there a way to do this? If so, how?

I want to both manipulate the item if it matches some criteria and ignore it if it does not. Thus List.filter wouldn't seem to be the solution.

like image 385
Steve Rowe Avatar asked Jul 08 '10 09:07

Steve Rowe


People also ask

How do I remove an item from an OCaml list?

Lists in OCaml are immutable. So you can't remove things from them. You normally create another list that doesn't have the things you don't want.

How do I split a list in OCaml?

Just have a function that takes the original list and a empty list. Build up the empty list(with the first half of the original list) until you reach the halfway point and then use the remainder of the original list and the built up empty list to generate your pairs.


7 Answers

You can also map values to singleton lists if you want to keep them or empty lists if you don't, and then concat the results.

List.concat (List.map (fun e -> if (e <> 1) then [e + 1] else []) my_list)
like image 77
rapha Avatar answered Oct 23 '22 06:10

rapha


SML has a function mapPartial which does exactly this. Sadly this function does not exist in OCaml. However you can easily define it yourself like this:

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)

Usage:

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

will return [1;3;4].

Or you can use filter_map from extlib as ygrek pointed out.

like image 34
sepp2k Avatar answered Oct 23 '22 06:10

sepp2k


Both Batteries and Extlib provide an equivalent of mapPartial: their extended List module sprovide a filter_map function of the type ('a -> 'b option) -> 'a list -> 'b list, allowing the map function to select items as well.

like image 29
Michael Ekstrand Avatar answered Oct 23 '22 07:10

Michael Ekstrand


Another solution would be to use directly a foldl :

let f e l = if (e <> 1) 
            then (e + 1)::l 
            else l
in List.fold_left f [] list  

But my preference is filter_map as Michael Ekstrand provided

like image 23
Benoît Fraikin Avatar answered Oct 23 '22 07:10

Benoît Fraikin


Alternatively you can filter your list then apply the map on the resulted list as follows :

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>

Usage :

# map_bis (fun e -> e<>1) (fun e -> e+1) [0;1;2;3];;
- : int list = [1; 3; 4]
like image 40
0xFF Avatar answered Oct 23 '22 08:10

0xFF


use

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

or tail recursive

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

or with a composition of standard functions

let process l = 
  l |> List.filter ((<>)1)
    |> List.map ((+)1) 
like image 33
Valentine Zakharenko Avatar answered Oct 23 '22 08:10

Valentine Zakharenko


The OCaml standard library has had List.filter_map since 4.08. This can therefore now be written as:

List.filter_map (fun e -> if e <> 1 then Some (e + 1) else None)
like image 36
glennsl Avatar answered Oct 23 '22 06:10

glennsl