OCamlで'a option listから'a listを生成する

タイトルだとなんだかよくわかりません。 やりたいことは、optionな値(SomeかNone)が格納されているリストから、Someな値の中身だけ抽出してリストにしたい、という感じです。

具体的に言うと [(Some 1); (Some 2); None; (Some3);][1; 2; 3]という値になります。

自分で再帰関数を定義

コードは以下のとおりです。

utop # let get_reliable_values list =
    let rec f result = function
    | [] -> result
    | head::tail -> begin
        match head with
        | Some v -> f (v :: result) tail
        | None -> f result tail
    end
    in
    f [] list
;;
val get_reliable_values : 'a option list -> 'a list = <fun>

実行結果は

utop # get_reliable_values [(Some 100); None; None; (Some 200); None];;
- : int list = [200; 100]
utop # get_reliable_values [(Some "aaa"); None; None; (Some "bbb"); None];;
- : string list = ["bbb"; "aaa"]

Option.is_someとOption.get

Optionis_someという関数が有るのを発見しました。コレを使うことで上記で自作した再帰関数を使わずとも、Listのfiltermapを使うことで簡単に実現することができました。

utop # [(Some 1); None; (Some 2); None; None; (Some 3)]
|> List.filter Option.is_some
|> List.map Option.get;;
- : int list = [1; 2; 3]

Option.get はもし値がNoneの場合には例外が投げられます。 が、前の処理のfilterの時点でSomeの値だけ抽出しているので例外の起きようがありません。

Listのfilter_map

F#には、そのものズバリなList.chooseという関数が有るようです。 OCamlでは同等の関数がList.filter_mapとして存在しています。(OCaml 4.08以降の新機能) コレまたTwitterで指摘してもらえるまで全く気づけ来ませんでした。。。

utop # [(Some 1); None; (Some 2); None; None; (Some 3)]
|> List.filter_map (fun v -> v);;
- : int list = [1; 2; 3]

まとめ

新しいOCamlを使っているならList.filter_mapを使いましょう! 古いOCamlでもOption.is_someOption_getを使えば簡単に実現できます!

公開日:2020/07/23

OCaml

About me

ドイツの現地企業でWeb Developer/System Administratorとして働いているアラフォーおじさんです。

プログラミングとかコンピュータに関する事がメインですが、日常的なメモとか雑多なことも書きます。

Links :
目次

自分で再帰関数を定義


Option.is_someとOption.get


Listのfilter_map


まとめ