LoginSignup
24
11

More than 5 years have passed since last update.

F# de データ読み取り超超超入門

Last updated at Posted at 2019-03-31

この記事 is 何?

F#の型プロバイダが優秀なので, データIO部分をF#でやって, C#と連携しよう!的な記事です.
今回は input 部分の紹介となります.

注) このエントリではサンプルがいくつか出てきますが, ぜひご自身でVisualStudioなどを起ち上げて, インテリセンスなどの動作を見ていただければと思います. ちょっとした感動を得られると思います.

型プロバイダって?

F#の機能のひとつで, データソースから自動で型を生成することができる機能です.
簡易的な説明はこちらのページを参考にしてください.

型プロバイダを利用することで, DBなどとのインピーダンスミスマッチ問題などを比較的容易に解決することができます.
O/Rインピーダンスミスマッチに関しては, 岩永先生の解説が非常にわかりやすいため, ぜひ一読してみてください.

JSONデータを読み込んでみる!

まずは, JSONデータの構成を記したJSONファイルを準備します.
今回は以下のようなデータ構成としました.

{
    "name":"x"
    "age":0
}

「string型のname」と「int型のage」があるだけの非常にシンプルなものです.
それをプロジェクトファイルを同じ階層に保存しておいてください. 以下のような構造になっていればOKだと思います.
image.png

JSON型プロバイダを使ってみよう!

使い方はとっても簡単です. 以下のサンプルにコメントを付けましたので, 読んでいただけると雰囲気がつかめると思います.

// Jsonプロバイダを利用するために必要です.
// また, Nugetから「Fsharp.Data」(https://www.nuget.org/packages/FSharp.Data/)を導入しておく必要があります.
open FSharp.Data

// Json型プロバイダ機能を利用することで,
// "sample.json" を参考に, 新たな型(今回でいうと, JsonSampleType型)を作成することができます.
type JsonSampleType = JsonProvider<"./sample.json">

// 読み込むjsonデータを用意します.
let json = 
    """{
        "name":"midoliy",
        "age":29
    }"""

// 'json' の文字列データを解析して, JsonSampleType型の値に変換します.
let a = JsonSampleType.Parse json

// すると, 自動で Name と Age というプロパティが使えるようになっています!!
printfn "%s (%d)" a.Name a.Age
// output:
//         "midoliy" (29)

これの何がうれしいかというと, 「a.Name」や「a.Age」と記述しているところでインテリセンスが効くところです.
しかも, 型情報までしっかりあります!!

こんな感じ(↓)でプロパティの候補が現れます.
image.png

クラスや構造体を作成しなくても, 自動で作ってくれるなんて素敵ですね.

XMLデータを読み込んでみる!

型プロバイダはいろいろなデータ構造に対応しています. もちろんXMLにだって対応済みです.
JSONと同じようにファイルからサンプルを読み込んでもいいのですが, それだと面白くないので, Web APIからサンプルデータを取得してみるサンプルを作成してみました.

今回はログインやAPIキーの取得をしなくても利用できる, 「ニコニコAPI」を使いました.
サンプルのXMLデータは, @hiki_neet_pさんにご協力いただきました. この場を借りて感謝申し上げます.

// 元となるサンプルデータがWeb上にあってもOK!!
// また, XML用の型プロバイダも用意されています. (サンプルデータは"新・豪血寺一族 -煩悩解放 - レッツゴー!陰陽師"を利用しました.)
type XmlSampleType = XmlProvider<"http://ext.nicovideo.jp/api/getthumbinfo/sm9">

// 本来はここでHTTP通信をして, レスポンスをもらう必要がありますが, 
// サンプルなので, XMLべた書きで良しとします.
// 今回は, tanaさんにご協力いただきまして, 動画情報開示を許諾いただきました.
let xml = 
    """
    <nicovideo_thumb_response status="ok">
        <thumb>
            <video_id>sm32207616</video_id>
            <title>【BGM素材】褪せし青の故郷【村】</title>
            <description>
            ゲームミュージック風ループBGM素材です。ニコニ・コモンズよりダウンロード可能です。[ nc168769 ]自由にお使いください。使用した画像[ nc4917 ][ nc4915 ]他動画 → [ mylist/24914088 ]Twitter @hiki_neet_p
            </description>
            <thumbnail_url>http://tn.smilevideo.jp/smile?i=32207616</thumbnail_url>
            <first_retrieve>2017-11-03T11:52:07+09:00</first_retrieve>
            <length>3:23</length>
            <movie_type>mp4</movie_type>
            <size_high>43159514</size_high>
            <size_low>11022667</size_low>
            <view_counter>163</view_counter>
            <comment_num>2</comment_num>
            <mylist_counter>5</mylist_counter>
            <last_res_body>うぽ うぽつ </last_res_body>
            <watch_url>https://www.nicovideo.jp/watch/sm32207616</watch_url>
            <thumb_type>video</thumb_type>
            <embeddable>1</embeddable>
            <no_live_play>0</no_live_play>
            <tags domain="jp">
            <tag category="1" lock="1">音楽</tag>
            <tag lock="1">ゲーム音楽</tag>
            </tags>
            <genre>音楽・サウンド</genre>
            <user_id>4581403</user_id>
            <user_nickname>tana@ヒキニートP</user_nickname>
            <user_icon_url>
            https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/s/458/4581403.jpg?1337178687
            </user_icon_url>
        </thumb>
    </nicovideo_thumb_response>
    """

// XMLデータをXmlSampleType型のインスタンスにします.
let x = XmlSampleType.Parse xml

printfn "status: %s" x.Status
printfn "\ttitle: %s" x.Thumb.Title
printfn "\tlength: %s" (x.Thumb.Length.ToString ())
printfn "\tuser_nickname: %s" x.Thumb.UserNickname
// output:
//      status: ok
//          title: 【BGM素材】褪せし青の故郷【村】
//          length: 03:23:00
//          user_nickname: tana@ヒキニートP

もちろんXMLでもインテリセンスはバッチリ効きます.
image.png

F#とC#を連携させてみる

XMLデータを読み取るコードをdll化してC#から参照してみましょう.
そのときに, Nugetから "FSarp.Data" を導入する必要があるので注意してください.
image.png

F#とC#で型プロバイダを直接やりとりさせると不都合が生じます. どういうことか見ていきましょう.

上のサンプルで "x" だったところを "xmlData" としていますが, 根本的にコードは変わっていません.
F#上ではキチンとインテリセンスが効いていますね.
image.png

しかし, C#側からだとインテリセンスが効きません...
image.png

そんなときは, レコード型を利用して, 適当なデータ構造に変換する必要があります.
これに関しては型を用意する必要があるので, 面倒ですが, C#でデータマッピングするよりも何倍もマシなコード量になります.
image.png

コピペ用

type ThumbInfo = {
    Status:string
    VideoId:string
    Title:string
    Description:string
    UserNickname:string }

let xmlData = XmlSampleType.Parse xml
              |> (fun x -> { Status=x.Status
                             VideoId=x.Thumb.VideoId
                             Title=x.Thumb.Title
                             Description=x.Thumb.Description
                             UserNickname=x.Thumb.UserNickname })

ThumbInfo型に変換してあげることで, C#側でもインテリセンスを効かせることができます(当たり前と言えば当たり前ですが...).
image.png

そして, F#を挟んでいることで実はうれしい副次的な効果が発生しています. F#は基本的にnullを許容していないため, C#側でわざわざnullチェックをしなくてもよいのです!

また, 同じことをC#でやろうとすると, とんでもないコード量になりますが, F#ならばそんな事態には陥りません.
ここではサンプルを出しませんが, 暇なときに同様のコードをC#で書いてみるとその違いがよくわかると思います.

コードの全体像

《F#のdllコード》

FSharpLib.fs
namespace FSharpLib

open FSharp.Data

module FsharpXml =
    type XmlSampleType = XmlProvider<"http://ext.nicovideo.jp/api/getthumbinfo/sm9">

    let xml = 
        """
        <nicovideo_thumb_response status="ok">
            <thumb>
                <video_id>sm32207616</video_id>
                <title>【BGM素材】褪せし青の故郷【村】</title>
                <description>
                ゲームミュージック風ループBGM素材です。ニコニ・コモンズよりダウンロード可能です。[ nc168769 ]自由にお使いください。使用した画像[ nc4917 ][ nc4915 ]他動画 → [ mylist/24914088 ]Twitter @hiki_neet_p
                </description>
                <thumbnail_url>http://tn.smilevideo.jp/smile?i=32207616</thumbnail_url>
                <first_retrieve>2017-11-03T11:52:07+09:00</first_retrieve>
                <length>3:23</length>
                <movie_type>mp4</movie_type>
                <size_high>43159514</size_high>
                <size_low>11022667</size_low>
                <view_counter>163</view_counter>
                <comment_num>2</comment_num>
                <mylist_counter>5</mylist_counter>
                <last_res_body>うぽ うぽつ </last_res_body>
                <watch_url>https://www.nicovideo.jp/watch/sm32207616</watch_url>
                <thumb_type>video</thumb_type>
                <embeddable>1</embeddable>
                <no_live_play>0</no_live_play>
                <tags domain="jp">
                <tag category="1" lock="1">音楽</tag>
                <tag lock="1">ゲーム音楽</tag>
                </tags>
                <genre>音楽・サウンド</genre>
                <user_id>4581403</user_id>
                <user_nickname>tana@ヒキニートP</user_nickname>
                <user_icon_url>
                https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/s/458/4581403.jpg?1337178687
                </user_icon_url>
            </thumb>
        </nicovideo_thumb_response>
        """

    type ThumbInfo = {
        Status:string
        VideoId:string
        Title:string
        Description:string
        UserNickname:string }

    let xmlData = XmlSampleType.Parse xml
                  |> (fun x -> { Status=x.Status
                                 VideoId=x.Thumb.VideoId
                                 Title=x.Thumb.Title
                                 Description=x.Thumb.Description
                                 UserNickname=x.Thumb.UserNickname })



《C#のコンソールコード》

Console.cs
using FSharpLib;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var data = FsharpXml.xmlData;

            System.Console.WriteLine($"status= {data.Status}");
            System.Console.WriteLine($"video id= {data.VideoId}");
            System.Console.WriteLine($"title= {data.Title}");
            System.Console.WriteLine($"description= {data.Description}");
            System.Console.WriteLine($"user nickname= {data.UserNickname}");
            // output:
            //      status = ok
            //      video id = sm32207616
            //      title = 【BGM素材】褪せし青の故郷【村】
            //      description = ゲームミュージック風ループBGM素材です。ニコニ・コモンズよりダウンロード可能です。[nc168769] 自由にお使いください。使用した画像[nc4917][nc4915] 他動画 → [mylist/24914088 ] Twitter @hiki_neet_p
            //      user nickname = tana@ヒキニートP
        }
    }
}

おわりに

今回はあまり詳細に説明をしませんでした. これは実際にコードをコピペでもいいので書いていただいた方が, 言葉で説明するよりも型プロバイダの良さをわかっていただけると考えたためです.

C#でいうところの "クラス" や "構造体" を用意していないのに, 勝手にインテリセンスが効くのを体験すると, おそらく感動すると思います.
特に, ネットワーク越しにサンプルデータを指定できるところがイケてます. ぜひ, ご自身でサンプルコードを記述してみてください.

また, C#よりもF#で書いた方がコードが簡潔になり, 可読性と生産性の高いプログラムを作成できる場合があります.
今回のような場合が, 特に顕著だと思います. これはC#を長年使ってきた私の感覚値となってしまいますが, データI/O関連は割とF#の方がキレイに書けると思います(というか, 書けました).

そして, DBとも直接繋げられたりもするので, SQL Providerのサンプルを見てみていただくと, さらに素敵さがわかると思います.

このように, C#オンリーではなく, F#も混ぜ込むことで, より一層プログラミングの幅が広がると思います.

余談

現在, 私のホームページでは, F#の日本語リファレンスを絶賛追加中です!
もし, F#に興味がでてきた方がいらっしゃいましたら, ぜひご利用ください!!

そして, まだ準備中の段階ですが, F#とC#をコラボさせるWPF/MVVMの記事を連載する予定(コチラ)ですので, 公開できるように頑張ります.

また, 記事の内容やC#/F#などで質問がある方は, お気軽にYoutube Liveの方でコメントしていただければと思います.
基本的に毎日放送していますので, 思い立ったときにでも来ていただければと思います.



ここまで駄文を読んでいただきありがとうございました.

24
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
11