(hatena (diary ’Nobuhisa)) このページをアンテナに追加 RSSフィード

18/08/15 :

[]静的Duck Typingでプロパティにアクセスする

F#には静的Duck Typingを行うための(非常にマイナーな)(そして難解な)仕組みがありますが、

プロパティ(setter)にアクセスする方法は少し特殊です。

マイナーすぎて多分ググってもほとんどヒットしないと思うので、一応サンプルを載せておきます。


結論から言うと、setterのシグネチャを指定する際には

when ^a : (member set_MyProperty : int -> unit)

のようにします。



これを応用して、色んな型の"Value"にアクセスするサンプルコードを書いてみました。

// 普通のプロパティを持つ普通のクラス
type MyClass(value) =
    member val Value : int = value with get, set

// 変更可能なラベルを持つレコード型
type MyRecord = { mutable Value : int }

// 仕様が異なる(という想定の)クラス
type YourClass(value) =
    member val StringValue : string = value with get, set

// 型拡張を変換アダプタ的に使って仕様を合わせる
type YourClass with
    member this.Value with get()      = int this.StringValue
                      and  set(x:int) = this.StringValue <- string x


// Valueプロパティのgetterから値を取得する関数
let inline getValue (x:^a when ^a : (member Value : int)) =
    (^a : (member Value : int) x)

// 上記のgetValueの引数xは型推論可能なので以下のようにも定義できる
let inline getValue' x =
    (^a : (member Value : int) x)

// Valueプロパティのsetterを呼び出す関数(今回の主役)
let inline setValue x value =
    (^a : (member set_Value : int -> unit) x, value)

let inline increment x = getValue x + 1 |> setValue x

let inline (++) x y = x + getValue y

let inline print x = getValue x |> printfn "Value = %d"


let test () =
    let myClass   = MyClass(9)
    let myRecord  = {Value = 99}
    let myRef     = ref 999
    let yourClass = YourClass("9999")

    increment myClass
    increment myRecord
    increment myRef
    increment yourClass

    print myClass
    print myRecord
    print myRef 
    print yourClass

    1 ++ myClass ++ myRecord ++ myRef ++ yourClass

> test () ;;
Value = 10
Value = 100
Value = 1000
Value = 10000
val it : int = 11111


レコード型のmutableなラベルに対しても使用することができました。

また、参照セルRef<'T>にはもともとValueというメンバが定義されているのでmyRefにも適用可能です。


YourClassは、「他と若干仕様の異なるクラス」をイメージしたもので、型拡張を変換コネクタ的に使ってincrement関数が適用できる形にしています。(Adaptorパターンの簡易版)


正直あまり利用シーンは思い浮かばないですが、フレームワーク等の制約/ルールによってシグネチャが強制されるようなメンバに関しては使えるかもしれません。(継承関係には無いが、リフレクションを使って動的にメンバを特定しなきゃいけないような場面)

18/03/26 :

[][] 全角・半角・ひらがな・カタカナ

アルファベットの大文字/小文字を区別せずに文字列を比較するのはよくあることですが、

全角・半角・ひらがな・カタカナに対応する方法もあるようです。


以前せっかく調べたので忘れないようにメモしておこう。

open System.Globalization

let contains (value:string) source =
    let compareInfo = CompareInfo.GetCompareInfo("ja-JP")
    let options =
        CompareOptions.IgnoreCase         // 大文字・小文字区別なし
        ||| CompareOptions.IgnoreKanaType // ひらがな・カタカナ区別なし
        ||| CompareOptions.IgnoreWidth    // 全角・半角区別なし
        
    compareInfo.IndexOf(source, value, options) > -1
    
let test () =
    "雨ニモマケズ" |> contains "まけず" |> printfn "%b" // true
    "全角でF#" |> contains "F#" |> printfn "%b" // true
    "F♯" |> contains "F#" |> printfn "%b" // false

18/02/04 :

[][][]F#とAzure Functions

Azure Functionsがなかなか便利で、すでに仕事で何度か利用しています。

ご紹介も兼ねて簡単なサンプルを載せておきたいと思います。

ここではHTTPリクエストに対して自分自身を送り返すQuine的な関数を作ってみます。


前提知識やメモ

  • run.fsxに処理を記述する
  • Azureポータル上のエディタが意外と高機能なので簡単な関数ならブラウザだけで書けちゃう
  • run関数のシグネチャは変更できない(引数名を変えただけで怒られた)
  • Azure Functionsが参照しているFSharp.Coreのバージョンがちょっと古いんじゃないか疑惑
  • グローバル変数を定義すると実行時に例外が発生(原因がよく分かっていない)
  • 初回アクセス時は反応が遅い
  • cron的なタイマー付き関数も簡単に実装できる
  • Async.Parallelが使えた(検証は不十分だが結構うれしい)

関数の下準備

Azureポータル上で、[HTTP trigger][F#][Name:Quine][Authorization level:Anonymous]という設定で関数を作成します。


実装

次のようなスクリプトを書きます。

__SOURCE_DIRECTORY__以下にあるrun.fsxファイル(自身)を読み込んでレスポンスとして返しています。

#r "System.Net.Http"

open System.IO
open System.Text
open System.Net

let run (req:HttpRequestMessage, log:TraceWriter) =
    async {
        let code =
            Path.Combine(__SOURCE_DIRECTORY__, "run.fsx")
            |> File.ReadAllText

        let response = req.CreateResponse(HttpStatusCode.OK)
        response.Content <- new StringContent(code, Encoding.UTF8, "text/plain")
        return response
    }
    |> Async.RunSynchronously


動作確認

Azureポータル上でも動作テストが行えますが、関数のURLを取得すると実際にアクセスすることができます。

今回作成した関数のサンプルがこちら。

https://hellofsharp.azurewebsites.net/api/Quine

17/03/31 :

[][]はじめての Azure "F#" Notebooks

Azure Notebooksを試してみました。*1

Azure Notebooks(Jupyter)は本来、検証や実験の結果を共有したり、試行錯誤の記録を残す目的で使われることが多いのではないかなと思いますが、技術的なドキュメントを対話的に作成する際にも便利そうな予感がビシビシバシバシしています。


ということで、試しにF#の列挙体の解説ノートを作成してみました。*2

https://notebooks.azure.com/Nobuhisa/libraries/fsharp の中にあるenum.ipynbというノートがそれです。(ノート単位での共有の仕方が分からない。。)

Azure NotebooksもまだPreview段階ですので突発的にUIや仕様が変わったりすることもありましたが、やはりドキュメント作成はとてもラクでした。


HTMLやMarkdownでの出力もできます。PDFで出力しようとしたらエラーになってしまいました。祈ります。

出力したHTMLをGitHub Pagesで公開してみました。

https://nobuhisa.github.io/FSharp-Notebooks/Jupyter/enum.html

なかなかいい感じ。

*1:試したのはずいぶん前なのですが、ブログに投稿するのを忘れていた...

*2:列挙体を選んだのは気分です。

17/03/09 : 本日の一言 : ブログ移転したい

[]実行制限付きの並列計算

個人的にご質問をいただいたので、簡単なサンプルを書いてみました。

「たくさんの処理を並列に実行したいのだけど、同時に走るスレッド数は制限したい」というプログラムです。


let worker (id:int) = async {
    System.Console.WriteLine("<Worker:{0}>", id) // 並列環境ではprintfnは使用しない
    do! Async.Sleep(1500)
}

[<EntryPoint>]
let main argv = 
    List.init 15 worker     // 15個の処理をしたい
    |> List.chunkBySize 3   // 3つずつ並列化したい
    |> List.iter
        (Async.Parallel >> Async.Ignore >> Async.RunSynchronously)
    
    printfn "completed"
    0


実行すると、1.5秒ごとに3行ずつ表示されます。

<Worker:1>
<Worker:0>
<Worker:2>
<Worker:3>
<Worker:5>
<Worker:4>
<Worker:6>
<Worker:7>
<Worker:8>
<Worker:9>
<Worker:11>
<Worker:10>
<Worker:12>
<Worker:14>
<Worker:13>
completed


F#ではasync式によって構築された計算はオブジェクトのように扱うことができます。

サンプルでは、計算15個のリストを生成し、それを3つずつに分割して並列実行しています。

3つの式がほぼ同時に実行されますが、RunSynchronouslyメソッドで結果を待機しているので、その3つの計算が完了してから残りの式が実行されることになります。(以降はその繰り返し)