スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門第25回(レコード型(Record type))

今回のお題は「レコード型(Record type)」です。
 
宿題の前にCardSuit型とCard型の定義を再掲しておきます。
 
type CardSuit =
    | Spade
    | Club
    | Diamond
    | Heart
    
 
 type Card =
    | Ace of CardSuit
    | King of CardSuit
    | Queen of CardSuit
    | Jack of CardSuit
    | NumCard of int * CardSuit   
 
    member this.valueBJ =
        match this with
        | NumCard (x,_) -> x
        | Ace (_)       -> 11
        | _             -> 10
 
    member this.suit =
        match this with
        | Ace(c)       -> c
        | King (c)     -> c
        | Queen (c)    -> c
        | Jack (c)     -> c
        | NumCard(_,c) ->c
 
それでは宿題の答えです。
 
(宿題)
 
Card型の値のリストを引数にして、和が21なら、"Black Jack",それより小さければ”20以下”、大きければ"オーバー”と表示する関数dispBJを定義してください。ただし、エースは常に11として数えることとします。
始まりは次のようになります。
let dispBJ (cLst : Card lst) =
 
(答えの例)
 
let dispBJ (cLst : Card list) =
     let sum = List.fold (fun x (c :Card) -> x + c.valueBJ) 0 cLst
     match sum with
     | _ when sum < 21 -> printfn "20以下"
     | _ when sum = 21 -> printfn "BJ" 
     | _               -> printfn "オーバー"
 
(普通if式で処理するところだと思いますが、復習のためmatch式で扱いました。)
 
実行例
 
> dispBJ [Ace (Club) ;  NumCard ( 6, Heart) ; NumCard (4 ,Spade)];;
BJ
val it : unit = ()
 
> dispBJ [Ace (Club) ;  NumCard (7 ,Spade)];;
20以下
 
 
つぎに後半です。
またCard型の値のリストを引数にして、スペード、クラブ、ダイアモンド、ハートの枚数をint*int*int*intの型で、返す関数howManyEachSuitを定義してください。
始まりは次のようになります。
let howManyEachSuit (cLst : Card lst) =
 
(答えの例)
 
let howManyEachSuit (cLst : Card list) =
    let sumUp (s0,s1,s2,s3) (c : Card) =
        let s = c.suit
        match s with
        | Spade     -> (s0+1,s1,s2,s3) 
        | Club      -> (s0,s1+1,s2,s3) 
        | Diamond   -> (s0,s1,s2+1,s3) 
        | Heart     -> (s0,s1,s2,s3+1) 
    List.fold sumUp (0,0,0,0) cLst
 
実行例
 
> howManyEachSuit [ Ace (Club); NumCard (5,Spade) ; Jack (Club);King(Heart)];;
val it : int * int * int * int = (1, 2, 0, 1)
 
さて、今回の内容です。
タプルという型があります。例えば("Jon","Kate",5)はstring*string*int型のタプルですが、定義の型をみただけでは、それぞれの役割が分かりません。
カップルの型を作りたいのであれば、himフィールドとherフィールドと交際年数フィールドを作りここに、データを格納するほうが、データの意味合いが明確になります。
さて、それではカップル型を定義してみます。
 
type Coupt =
    { him : string ; //改行するときは;はなくても可
      her : string 
      period : int }
 
こうしておいて
 
let firstCouple = { him = "john" ; her = "Kate" ; period = 3}
 
とすれば交際期間3年のjohn とKateのカップルがfirstCoupleに束縛されます。
 
(注)let firstCouple = { her = "Kate"; period = 3;him = "john" }でも、同じです。
レコード型では、値の並ぶ順は任意です。
 
ドット演算子で、対応するフィールドの値を得ることができます。
 
> firstCouple.him;;
val it : string = "john"
 
パターンマッチは次のように行えます。
 
>let { him = x ; her = "Kate"; period = 3} = firstCouple
val x : string = "john"
 
パターンマッチしない場合がありえるという警告がでますが、ここでは気にしないでおきます。
 
フィールドの条件を省略するとワイルドカード扱いです。
 
>let { him = y ; her = "Kate"} = firstCouple
val y : string = "john"
 
F#はフィールド名も参考にして、レコード型の型推論を行いますので、複数のレコード型を使用する場合は、フィールド名に重複がないようにした方が、型注釈の手間が省けます。
 
それでは、氏名と年齢と身長を組にしたPerson型を定義してみます。
 
type Person =
    { name   : string ;
      age    : int ;
      height : float}
 
テスト用に次のようなPerson listを作っておきます。
 
let personLst = 
    [{name = "John" ; age = 34 ;height = 172.5} ; {name = "Ben" ; age = 21 ;height = 160.0} ; 
     {name = "Mike" ; age = 77 ;height = 161.5} ; {name = "Stehpen" ; age = 50 ;height = 182.5} ;
     {name = "Card" ; age = 62 ;height = 166.5} ; {name = "Martin" ; age = 8 ;height = 112.5} ]
 
さて、これらを、年齢で絞り込むことを考えます。
例えば、次のコードで50才未満の人を抽出できます。
 
List.filter (fun (x : Person) -> x.age < 50) personLst;;
 
また、Discriminated Unionと同様に、定義部分で関数を定義することもできます。
例えば定義部分を次のようにしておきます。
 
type Person =
 
    { name   : string ;
      age    : int ;
      height : float}
 
         member this.isAdult = 
        this.age >= 20    
        
 
let a ={name = "Johnson" ; age = 19 ;height = 173.5}で
 
> a.isAdult;;
val it : bool = false
 
となりますし
 
List.filter (fun (x : Person) -> x.isAdult) personLst で20才以上のperson型のデータが抽出されます。
 
レコード型の値を作り上げるには、フィールド名とフィールド値を全部書かなくてはならなくて結構大変です。そこで、すでに定義してあるレコード型の値を再利用する方法が準備されています。
 
例えば
 
let p ={name = "John" ; age = 24 ;height = 173.5}
 
に対し
 
let p2 = { p with name = "Ken" ; height = 143.2}
 
で、pのage部分が再利用できます。つまりKenの年齢は24として定義されます。
withがキーワードです。
 
実行例
 
> let p ={name = "John" ; age = 24 ;height = 173.5};;
 
val p : Person = {name = "John";
                  age = 24;
                  height = 173.5;}
 
> let p2 = { p with name = "Ken" ; height = 143.2};;
 
val p2 : Person = {name = "Ken";
                   age = 24;
                   height = 143.2;}
                   
さて宿題です。上のPerson型を使って、string型の仮引数 strName とPerson list 型の仮引数 pLstをとり、pLstの中でnameがstrNameと同じ要素があれば、その内の最初の要素のageをSome (age)で、なければNoneを返す関数tryFindByNameを定義してください。
始まりは次のようになります。
 
let tryFindByName (strName : string) (pLst : Person list) =
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

typo

レコードが他では→レコード型では

RE typo

修正しました。御指摘ありがとうございました。
プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。