part5 - update, delete, list
前回はreadコマンドを実装した。今回は、update、delete、listコマンドを実装する。どんどんサクサク進めていこう。
ユーザー情報の更新
やはりUpdadate.fsを作成し、そこでコマンドを作成してみよう。このコマンドはユーザーからIDの入力を受け付け、そのIDに基づいてcsvファイルを読み込む。存在しないときは、存在しないとコンソールに表示する。あとは読み込んだcsvデータをパースし、正しい情報であればそのまま表示、おかしなデータになっていたらデータが破損している旨を報告する。
module update
open read
let createNewCsvData (originalValues: string array) (newName: string) (newAge: string) (newEmail: string) = let id = originalValues.[0]
let name = if System.String.IsNullOrEmpty(newName) then originalValues.[1] else newName
let age = if System.String.IsNullOrEmpty(newAge) then originalValues.[2] else newAge
let email = if System.String.IsNullOrEmpty(newEmail) then originalValues.[3] else newEmail
sprintf "%s,%s,%s,%s" id name age email
let update = System.Console.Clear() printfn "ID を入力してください。" let id = System.Console.ReadLine() let path = System.IO.Path.Combine("./users", id + ".csv") let csvData = readCsv path
match csvData with | Some values -> let id = values.[0] let name = values.[1] let age = values.[2] let email = values.[3] printfn "ID: %s" id printfn "名前: %s" name printfn "年齢: %s" age printfn "メールアドレス: %s" email
printfn "名前を入力してください(1文字以上、150字以内)。空欄の場合は変更しません。" let newName = System.Console.ReadLine()
printfn "年齢を入力してください(0以上、150以下)。空欄の場合は変更しません。" let newAge = System.Console.ReadLine()
printfn "メールアドレスを入力してください(任意)。空欄の場合は変更しません。" let newEmail = System.Console.ReadLine()
let csvData = createNewCsvData values newName newAge newEmail System.IO.File.WriteAllText(path, csvData) |> ignore | None -> printfn "ユーザーが見つかりませんでした。"createNewCsvData関数により、空欄のときにもとの値とする処理を追加している。ほとんどreadコマンドと同じだが、最後にcsvデータを更新する処理が追加されている。だいたいreadとcreateを合体させたようなものだろう。readCsv関数はreadモジュールにあるので、Update.fsにもreadモジュールをインポートしている。fsprojファイルにUpdate.fsを追加するとき、依存するモジュールを先に追加する必要がある。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> </PropertyGroup>
<ItemGroup> <Compile Include="Create.fs" /> <Compile Include="Read.fs" /> <Compile Include="Update.fs" /> <Compile Include="Program.fs" /> </ItemGroup>
</Project>Program.fsにも追加したいところだが、delete, listコマンドも作成するので、それらを先に作成する。
ユーザー情報の削除
Delete.fsを作成し、そこでコマンドを作成してみよう。このコマンドはユーザーからIDの入力を受け付け、そのIDに基づいてcsvファイルを削除する。削除が成功した場合は、削除した旨をコンソールに表示する。存在しないときは、存在しないとコンソールに表示する。
module delete
let yesAnswers = [| "y"; "yes"; "Y"; "YES"; "Yes" |]
let delete = System.Console.Clear() printfn "削除するユーザーのID を入力してください。" let id = System.Console.ReadLine() let path = System.IO.Path.Combine("./users", id + ".csv")
if System.IO.File.Exists(path) then
printfn "ID: %sを削除します。よろしいですか?" id
let answer = System.Console.ReadLine()
if yesAnswers |> Array.contains answer then System.IO.File.Delete(path) printfn "ユーザーを削除しました。" else printfn "削除をキャンセルしました。" else printfn "ユーザーが見つかりませんでした。"deleteコマンドは、readコマンドと似ているが、最後にファイルを削除する処理が追加されている。削除するかどうかの確認をするために、yesAnswersという配列を用意している。yesAnswersに含まれている入力であれば、削除を実行する。fsprojファイルにDelete.fsを追加するとき、依存するモジュールを先に追加する必要がある。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> </PropertyGroup>
<ItemGroup> <Compile Include="Create.fs" /> <Compile Include="Read.fs" /> <Compile Include="Update.fs" /> <Compile Include="Delete.fs" /> <Compile Include="Program.fs" /> </ItemGroup>
</Project>この調子で、listコマンドも作成してみよう。
ユーザー情報の一覧表示
List.fsを作成し、そこでコマンドを作成してみよう。このコマンドは./usersフォルダ内のすべてのcsvファイルを読み込み、それぞれのユーザー情報を表示する。ユーザーがいない場合は、ユーザーがいない旨を表示する。
module idList
let idList = System.Console.Clear() let files = System.IO.Directory.GetFiles("./users") printfn "ユーザーID一覧"
files |> Array.map (fun file -> System.IO.Path.GetFileNameWithoutExtension(file)) |> Array.iter (fun id -> printfn "%s" id)Array.iterは、他の言語でいうところのforEachに近い。意味のある返り値はなく、ただリストの順番に副作用を起こす。
それでは、それぞれの関数をProgram.fsに追加する。その前に、まずはコマンドを追加する。
type Command = | Create | Read | Help | Update // 追加 | Delete // 追加 | List // 追加 | Unknown of stringCommandが追加されると、パターンマッチングの部分でエラーになっているはずだ。パターンマッチングは網羅的(exhaustive)でなければならない。Helpの部分にUpdate、Delete、Listを追加する。
let parseCommand args = match args with | [| "create" |] -> Create | [| "read" |] -> Read | [| "update" |] -> Update | [| "delete" |] -> Delete | [| "list" |] -> List | [| "--help" |] -> Help | [| "-h" |] -> Help | [||] -> Help // コマンドがないときもヘルプを表示 | _ -> Unknown args.[0]あとは実際にコマンドを実行する部分に、Update, Delete, Listを追加する。
全体像は以下の通りとなる。
open createopen readopen update // 追加open idList // 追加open delete // 追加
type Command = | Create | Read | Help | Update // 追加 | Delete // 追加 | List // 追加 | Unknown of string
let parseCommand args = match args with | [| "create" |] -> Create | [| "read" |] -> Read | [| "update" |] -> Update | [| "delete" |] -> Delete | [| "list" |] -> List | [| "--help" |] -> Help | [| "-h" |] -> Help | [||] -> Help // コマンドがないときもヘルプを表示 | _ -> Unknown args.[0]
let showHelp = printfn "Usage: dotnet run [command]" printfn "Commands:" printfn " create Create a new user" printfn " read <ID> Read a user by ID" printfn " update Update a user by ID" printfn " delete Delete a user by ID" printfn " list List all users" printfn " --help, -h Show this help"
[<EntryPoint>]let main argv = let command = parseCommand argv
match command with | Create -> create | Read -> read // 追加 | Update -> update // 追加 | Delete -> delete // 追加 | List -> idList // 追加 | Help -> showHelp | Unknown s -> printfn "Unknown command: %s" s
0まとめ
これで、update, delete, listコマンドが実装された。dotnet runで実行してみよう。それぞれのコマンドが実行できることを確認しよう。
次は、いままで放置してきたエラーハンドリングを実装する。いろいろなエラーを見てみぬふりをしてきた。F#はエラー処理の扱いが非常に面白い言語である。