技術memo

関数型ゴースト

基礎プログラミング演習(1),(2)をF# でやってみた

元々お手本コードを割と関数型風に書いていたので、ほとんどそのまま一対一対応で移植する形になりました。

比較すると、記法がどれほど簡潔かわかるんじゃないでしょうか。

BMIを計算するプログラム

基礎プログラミング演習(1) - 技術memo

units of measureを使ってみました。

module BmiProgram

module String =
    open System
    // convert string to int
    let tryParseInt (s:string) =
        Int32.TryParse(s) |> function | (true, x) -> Option.Some(x) | (false, _) -> Option.None

module Option =
    // Option.filter
    let filter (predicate : 'T -> bool) value =
        value |> function | None -> None | Some x -> if predicate x then Some x else None

module Metre =
    [<Measure>] type cm
    [<Measure>] type m

    let toCentiMetre (x:int<m>) =
        x * 100<cm/m>

    let toMetre (x:int<cm>) =
        let x = float x |> LanguagePrimitives.FloatWithMeasure<cm>
        x / 100.0<cm/m>

module Gram =
    [<Measure>] type kg

module Bmi =
    open Metre
    open Gram

    // compute bmi value
    let compute (h:int<cm>) (w:int<kg>) =
        let w = w |> (float)
        let h = h |> Metre.toMetre |> (float)
        w / (h * h)

    // define bmi type
    type BmiType = Fat | Slim | Normal

    // check bmi type by bmi value
    let check = function
        | x when x < 18.5 -> Slim
        | x when x >= 25.0 -> Fat
        | _ -> Normal

    // get string of bmi type
    let getBmiTypeName = function
        | Fat -> "肥満"
        | Slim -> "痩せ"
        | Normal -> "普通"

module Program =
    open System
    // validate input value
    let validate (s:string) =
        s
            |> String.tryParseInt
            |> Option.filter (fun x -> x > 0)

    // get correct integer value
    let rec getInput () =
        Console.ReadLine()
            |> validate
            |> function
                | Some x -> x
                | None -> printfn "error. 0より大きい整数を入力して下さい"; getInput ()

    let run () =
        printfn "身長は?" 
        let h = getInput () |> LanguagePrimitives.Int32WithMeasure<Metre.cm>
        printfn "体重は?"
        let w = getInput () |> LanguagePrimitives.Int32WithMeasure<Gram.kg>
        let bmi = Bmi.compute h w
        printfn "あなたのBMIは%fです" bmi
        printfn "あなたは%s体型です" (bmi |> Bmi.check |> Bmi.getBmiTypeName)

//(*
[<EntryPoint>]
let main args =
    Program.run ()
    #if DEBUG
    System.Console.ReadKey () |> ignore
    #endif
    0
//*)

じゃんけんゲーム

基礎プログラミング演習(2) - 技術memo

バリアントとパターンマッチが便利すぎてもうそれ無しには生きていけません。

module JankenProgram

module Janken =
    open System
    // define janken hands
    type Hand = Gu | Chi | Pa
    // define janken results
    type Result = Win | Aiko |Lose

    let getRandomHand (r:Random) = r.Next(0, 2) % 3 |> function
        | 0 -> Gu
        | 1 -> Chi
        | _ -> Pa

    // judge
    let isHumanWin (human, cp) = (human, cp) |> function
        | h, c when h = c -> Aiko
        | Gu, Chi | Chi, Pa | Pa, Gu -> Win
        | _ -> Lose

    // get janken hand name
    let getHandName = function
        | Gu -> "グー"
        | Chi -> "チョキ"
        | Pa -> "パー"

    // get janken result name
    let getResultName = function
        | Win -> "勝ち"
        | Aiko -> "あいこ"
        | Lose -> "負け"


module Program =
    open System
    type Input = Hand of Janken.Hand | Exit

    let validateChar = function
        | 'g' -> Janken.Hand.Gu |> Hand |> Some
        | 'c' -> Janken.Hand.Chi |> Hand |> Some
        | 'p' -> Janken.Hand.Pa |> Hand |> Some
        | 'q' -> Exit |> Some
        | _ -> None
        
    // get hand by input string
    let validate (input:string) =
        if input.Length <> 1
        then None
        else validateChar input.[0]

    let rec getInput () =
        Console.ReadLine()
        |> validate
        |> function
            | Some x -> x
            | None -> printfn "error.入力値が不正です"; getInput()
            
    let run () =
        let random = new Random()
        let rec loop () =
            printfn "出す手を入力してください(g:グー、c:チョキ、p:パー)。終了する場合はqを入力してください"
            getInput () |> function
                | Exit -> ()
                | Hand hand ->
                    let cpHand = Janken.getRandomHand random
                    let result = Janken.isHumanWin (hand, cpHand)
                    printfn "あなた: %s / CP: %s" (Janken.getHandName hand) (Janken.getHandName cpHand)
                    printfn "あなたの%sです" (Janken.getResultName result)
                    loop ()
        loop ()
        
//(*
[<EntryPoint>]
let main args =
    Program.run ()
    0
//*)