F# List入門問題精講
この文書は、F#のリストの様々な関数を使いこなすための練習問題集のPart 1 (基礎編) である。15問を通して、F#のリスト操作の基本をマスターすることを目指す。
問題1: 空リストを作成する
出力:
- 空のリスト
解答例と解説
let emptyList = []
printfn "%A" emptyList解説:
[] は空のリストを表す。
問題2: リストの先頭に要素を追加する
入力:
- 既存のリスト:
[2; 3; 4] - 追加する要素:
1
出力:
[1; 2; 3; 4]
ヒント
::演算子 (コンス演算子) を使用する
解答例と解説
let existingList = [2; 3; 4]let elementToAdd = 1let newList = elementToAdd :: existingList
printfn "%A" newList解説:
:: 演算子 (コンス演算子) は、リストの先頭に要素を追加する。elementToAdd :: existingList は、existingList の先頭に elementToAdd を追加した新しいリストを作成する。
問題3: 2つのリストを結合する
入力:
[1; 2; 3][4; 5; 6]
出力:
[1; 2; 3; 4; 5; 6]
ヒント
@演算子、またはList.append関数を使用する
解答例と解説
let list1 = [1; 2; 3]let list2 = [4; 5; 6]
// @ 演算子を使用let combinedList1 = list1 @ list2
// List.append 関数を使用let combinedList2 = List.append list1 list2
printfn "%A" combinedList1printfn "%A" combinedList2解説:
@ 演算子と List.append 関数はどちらも、2つのリストを結合して新しいリストを作成する。list1 @ list2 と List.append list1 list2 はどちらも [1; 2; 3; 4; 5; 6] を返す。
問題4: リストの先頭要素を取得する
入力:
[1; 2; 3; 4; 5]
出力:
1
ヒント
List.head関数を使用するList.tryHead関数も類似の機能を提供する (Option型で結果を返す)
解答例と解説
let list = [1; 2; 3; 4; 5]let headElement = List.head list
printfn "%d" headElement解説:
List.head 関数はリストの先頭の要素を返す。この例では、list の先頭要素は 1 なので、headElement は 1 になる。
注意: 空リストに List.head を適用すると例外が発生する。List.tryHead を使うと、Option 型で結果が返され、空リストの場合は None になるため、例外を回避できる。
問題5: リストの先頭を除いた残りのリストを取得する
入力:
[1; 2; 3; 4; 5]
出力:
[2; 3; 4; 5]
ヒント
List.tail関数を使用する
解答例と解説
let list = [1; 2; 3; 4; 5]let tailList = List.tail list
printfn "%A" tailList解説:
List.tail 関数はリストの先頭を除いた残りのリストを返す。この例では、list の先頭要素 1 を除いた [2; 3; 4; 5] が tailList になる。
注意: 空リストに List.tail を適用すると例外が発生する。
問題6: リストが空かどうかを判定する
入力:
[][1; 2; 3]
出力:
truefalse
ヒント
List.isEmpty関数を使用する
解答例と解説
let list1 = []let list2 = [1; 2; 3]
let isEmpty1 = List.isEmpty list1let isEmpty2 = List.isEmpty list2
printfn "%b" isEmpty1printfn "%b" isEmpty2解説:
List.isEmpty 関数は、与えられたリストが空であれば true を、そうでなければ false を返す。list1 は空リストなので true、list2 は要素を含むので false が返される。
問題7: リストの長さを取得する
入力:
[1; 2; 3; 4; 5]
出力:
5
ヒント
List.length関数を使用する
解答例と解説
let list = [1; 2; 3; 4; 5]let length = List.length list
printfn "%d" length解説:
List.length 関数はリストの要素数を返す。この例では、list は5つの要素を持つため、length は 5 になる。
問題8: リストのパターンマッチング
入力:
[1; 2; 3]
出力:
head: 1tail: [2; 3]
ヒント
match ... with構文を使用する
解答例と解説
let list = [1; 2; 3]
match list with| head :: tail -> printfn "head: %d" head printfn "tail: %A" tail| [] -> printfn "empty list"解説:
match ... with 構文を使うと、リストの構造に基づいてパターンマッチングを行うことができる。head :: tail というパターンは、リストの先頭要素を head、残りのリストを tail に束縛する。空リストの場合は [] というパターンにマッチする。
問題9: リストの要素を逆順にする
入力:
[1; 2; 3; 4; 5]
出力:
[5; 4; 3; 2; 1]
ヒント
List.rev関数を使用する
解答例と解説
let list = [1; 2; 3; 4; 5]let reversedList = List.rev list
printfn "%A" reversedList解説:
List.rev 関数は、リストの要素を逆順にした新しいリストを返す。この例では、reversedList は [5; 4; 3; 2; 1] となる。
問題10: リストの各要素に関数を適用し、新しいリストを作成する
入力:
[1; 2; 3]- 適用する関数: 要素を2倍する関数
出力:
[2; 4; 6]
ヒント
List.map関数を使用する- 類似関数として
List.mapi(インデックス付き)、List.map2、List.map3(それぞれ2つ、3つのリストを扱う) がある
解答例と解説
let list = [1; 2; 3]let doubledList = List.map (fun x -> x * 2) list
printfn "%A" doubledList解説:
List.map 関数は、リストの各要素に関数を適用し、その結果からなる新しいリストを作成する。この例では、fun x -> x * 2 という関数を使って各要素を2倍にしている。doubledList は [2; 4; 6] となる。
List.mapi は、要素だけでなくインデックスも関数に渡す場合に使う。List.map2 と List.map3 は、それぞれ2つと3つのリストの対応する要素に関数を適用する。
問題11: リストの各要素に対して副作用のある操作を実行する
入力:
["a"; "b"; "c"]
出力:
a(改行)b(改行)c(改行)
ヒント
List.iter関数を使用する- 類似関数として
List.iteri(インデックス付き)、List.iter2、List.iter3(それぞれ2つ、3つのリストを扱う) がある
解答例と解説
let list = ["a"; "b"; "c"]List.iter (printfn "%s") list解説:
List.iter 関数は、リストの各要素に対して指定された関数を適用する。List.map とは異なり、戻り値はない (つまり、unit 型を返す)。主に副作用のある操作を実行するために使用される。この例では、printfn "%s" という関数を使って各要素を表示している。
List.iteri は、要素だけでなくインデックスも関数に渡す場合に使う。List.iter2 と List.iter3 は、それぞれ2つと3つのリストの対応する要素に関数を適用する。
問題12: リストから条件を満たす要素のみを抽出する
入力:
[1; 2; 3; 4; 5; 6]- 条件: 偶数
出力:
[2; 4; 6]
ヒント
List.filter関数を使用するList.filteriはインデックス付きで要素をフィルタリングする
解答例と解説
let list = [1; 2; 3; 4; 5; 6]let evenNumbers = List.filter (fun x -> x % 2 = 0) list
printfn "%A" evenNumbers解説:
List.filter 関数は、リストの各要素に条件を適用し、true を返す要素のみを含む新しいリストを作成する。この例では、fun x -> x % 2 = 0 という関数を使って、各要素 x が偶数かどうかを判定している。evenNumbers は [2; 4; 6] となる。List.filteri を使うと、要素のインデックスも考慮したフィルタリングができる。
問題13: リストのすべての要素が条件を満たすかどうかを判定する
入力:
[2; 4; 6; 8]- 条件: 偶数
出力:
true
ヒント
List.forall関数を使用する
解答例と解説
let list = [2; 4; 6; 8]let areAllEven = List.forall (fun x -> x % 2 = 0) list
printfn "%b" areAllEven解説:
List.forall 関数は、リストのすべての要素が指定された条件を満たす場合に true を、そうでない場合に false を返す。この例では、fun x -> x % 2 = 0 という関数を使って、各要素 x が偶数かどうかを判定している。list のすべての要素は偶数なので、areAllEven は true になる。
問題14: リストの少なくとも1つの要素が条件を満たすかどうかを判定する
入力:
[1; 3; 5; 6; 7]- 条件: 偶数
出力:
true
ヒント
List.exists関数を使用するList.exists2関数は2つのリストを扱う
解答例と解説
let list = [1; 3; 5; 6; 7]let existsEven = List.exists (fun x -> x % 2 = 0) list
printfn "%b" existsEven解説:
List.exists 関数は、リストの少なくとも1つの要素が指定された条件を満たす場合に true を、そうでない場合に false を返す。この例では、fun x -> x % 2 = 0 という関数を使って、各要素 x が偶数かどうかを判定している。list には偶数 6 が含まれているので、existsEven は true になる。List.exists2 は、2つのリストの対応する要素が条件を満たすかどうかを判定する。
問題15: リストから条件を満たす要素を検索する
入力:
[1; 3; 4; 5; 6]- 条件: 3の倍数
出力:
6
ヒント
List.find関数を使用する- 似た関数として以下のようなものがある:
List.tryFind:Option型で結果を返すList.findBack/List.tryFindBack: リストを後方から検索するList.findIndex/List.tryFindIndex: 条件を満たす要素のインデックスを返すList.findIndexBack/List.tryFindIndexBack: リストを後方から検索してインデックスを返す
解答例と解説
let list = [1; 3; 4; 5; 6]let foundElement = List.find (fun x -> x % 3 = 0) list
printfn "%d" foundElement解説:
List.find 関数は、リストの先頭から要素を調べ、指定された条件を満たす最初の要素を返す。この例では、fun x -> x % 3 = 0 という関数を使って、3の倍数を見つけようとしている。最初に見つかる3の倍数は 6 なので、foundElement は 6 になる。
List.tryFind は、条件を満たす要素が見つからなかった場合に例外を発生させる代わりに None を返す。List.findBack と List.tryFindBack はリストを後方から検索する。List.findIndex, List.tryFindIndex, List.findIndexBack, List.tryFindIndexBack は、要素そのものではなく、そのインデックスを返す。
これで基礎編の15問が完成しました。`List.tryFind`, `List.findBack`, `List