既存の型に関数を追加する
#!/usr/bin/env fsharpi
module MyEx =
type System.String with
member x.smile() = x + " :)"
open MyEx
"Hello World".smile() |> printfn "%A"
// "Hello World :)"
fsharp には try-with と try-finally がそれぞれ用意されているため、try-catch-finally する場合にはネストする必要がある
#!/usr/bin/env fsharpi
//例外が発生する関数
let someFunction1 = function
| n when n < 1 -> failwith "例外です"
| n -> n
let test01 =
try
try
someFunction1 0 |> printfn "result : %d"
with
| e -> printfn "Error: %s" e.Message
finally
printfn "Finallyです"
test01
//Error: 例外です
//Finallyです
処理結果をEitherで返却し、結果をパターンマッチでハンドルする
#!/usr/bin/env fsharpi
open System
type Either<'T, 'U> =
| Left of 'T
| Right of 'U
let someFunction2 = function
| n when n < 1 -> Left (ArgumentException "例外です")
| n -> Right n
let test02 =
match (someFunction2 0) with
| Left e -> printfn "%s" e.Message
| Right x -> printfn "result: %d" x
test02
//Error: 例外です
パターンマッチ処理の機能は、1)分類する、2)処理するに分けられる。何らかの値を分類することに特化した機能がActivePattern。
EvenとOddと2つの分類を定義し、渡された数値をそのいずれかに分類する
#!/usr/bin/env fsharpi
let (|Even|Odd|) n =
if n % 2 = 0 then Even else Odd
let evenOrOdd n =
match n with
| Even -> printfn "%A is Even." n
| Odd -> printfn "%A is Odd." n
evenOrOdd 1 // 1 is Odd.
evenOrOdd 2 // 2 is Even.
evenOrOdd 3 // 3 is Odd.
evenOrOdd 4 // 4 is Even.
System.DateTimeを引数に取り、年月日に分類する
#!/usr/bin/env fsharpi
let (|Date|) (dt:System.DateTime) =
(dt.Year, dt.Month, dt.Day)
(|Date|) System.DateTime.Now |> printfn "Date: %A"
//Date: (2015, 12, 9)
失敗する場合の分類は (|XXX|_|)
とする
#!/usr/bin/env fsharpi
let (|Fizz|_|) n = if n % 3 = 0 then Some "Fizz" else None
let (|Buzz|_|) n = if n % 5 = 0 then Some "Buzz" else None
let fizzBuzz = function
| Fizz fizz & Buzz buzz -> fizz + buzz //ANDできる
| Fizz fizz -> fizz
| Buzz buzz -> buzz
| n -> string n
[1..20] |> List.map fizzBuzz |> printfn "FizzBuzz1: %A"
//FizzBuzz1: ["1"; "2"; "Fizz"; "4"; "Buzz"; "Fizz"; "7"; "8"; "Fizz"; "Buzz"; "11"; "Fizz"; "13"; "14"; "FizzBuzz"; "16"; "17"; "Fizz"; "19"; "Buzz"]
Fizz も Buzz も処理構造は同じなので引数を2つ取る関数でまとめられる
#!/usr/bin/env fsharpi
let (|Mul|_|) m n = if n % m = 0 then Some 1 else None
let fizzBuzz2 = function
| Mul 15 _ -> "FizzBuzz"
| Mul 5 _ -> "Buzz"
| Mul 3 _ -> "Fizz"
| n -> string n
[1..20] |> List.map fizzBuzz2 |> printfn "FizzBuzz2: %A"
//FizzBuzz2: ["1"; "2"; "Fizz"; "4"; "Buzz"; "Fizz"; "7"; "8"; "Fizz"; "Buzz"; "11"; "Fizz"; "13"; "14"; "FizzBuzz"; "16"; "17"; "Fizz"; "19"; "Buzz"]
文字列が正規表現でマッチするかをActivePatternで定義する
#!/usr/bin/env fsharpi
open System.Text.RegularExpressions
//Log文字列パーサー
let (|LogParser|_|) text =
let regex =
Regex (
@"^(?<date>[0-9\/]*?) " +
@"(?<time>[0-9:]*?) " +
@"(?<log>.*)$",
RegexOptions.IgnoreCase)
let matchResult = regex.Match(text)
if matchResult.Success then
let matchedValue (matchResult:Match) (key:string) = matchResult.Groups.[key].Value.Trim()
let ms = matchedValue matchResult
Some (ms "date", ms "time", ms "log")
else
None
//Log型
type Log = {
Date: string
Time: string
Log: string
}
//Parserがマッチしたら、Log型で返却する
let parser = function
| LogParser (date, time, log) -> Some { Date = date; Time = time; Log = log}
| _ -> None
//ログ行をパースする
[ "Apache...";
"2015/12/05 11:43 hoge...";
"2015/12/05 11:44 moge..." ]
|> List.map parser
|> List.filter(fun log -> log.IsSome)
|> printfn "Log: %A"
//Log: [Some {Date = "2015/12/05"; Time = "11:43"; Log = "hoge...";}; Some {Date = "2015/12/05"; Time = "11:44"; Log = "moge...";}]
コンピュテーション式で Maybeモナドを定義する
#!/usr/bin/env fsharpi
//ビルダークラスの定義
type MaybeBuilder() =
//return時に呼び出される
member this.Return(x) = Some x
//let!時に呼び出される( haskellのbind >>= )
member this.Bind(x, f) =
match x with
| Some x -> f x
| _ -> None
//ビルダークラスのインスタンスを生成
let maybe = new MaybeBuilder()
//ビルダークラスのインスタンスを利用することで、コンピュテーション式が記述できる
//コンピュテーション式はビルダークラスのルールが適用される(ここではNoneを処理をしないというルール、つまり処理の失敗を無視する)
//haskellのdo記述するものと同じ
let someCompEx n =
//とある処理(Noneになる=失敗する可能性がある)
let someFunction = function
| n when n % 2 = 0 -> Some n
| n when n % 3 = 0 -> Some n
| _ -> None
//コンピュテーション式
maybe {
let! ret1 = someFunction n
let! ret2 = someFunction (n + 1)
return ret1 + ret2
}
//失敗せずに計算できる
[1..10] |> Seq.map someCompEx |> printfn "%A"
//seq [null; Some 5; Some 7; null; ...]
haskellのmaybeモナドと同じ
class Monad m where
return :: a -> m a --上記Retern関数に相当
(>>=) :: m a -> (a -> m b) -> m b --上記Bind関数に相当
instance Monad maybe where
return = Just --上記Return定義と同様
Just x >>= f = f x --上記Bind定義と同様
Nothing >>= _ = Nothing --上記Bind定義と同様
bind処理 m a -> (a -> m b) -> m b
は、「aをbにする関数」を a に適用して、b にするだけ
maybe {
let! ret1 = someFunction n // bind
let! ret2 = someFunction (n + 1) // bind
return ret1 + ret2 // return
}
つまり上記はJava8で書けば、下記と同様
Optional.of(someFunction(n))
.flatMap(ret1 -> someFunction(n+1)
.map(ret2 -> ret 1 + ret2)
下記のコードでは外部モジュールを利用するため、パッケージマネージャNuGetをインストールする
- https://www.nuget.org/ から latest nuget.exe をダウンロード
- 適当なフォルダに配置する
- 以下のシェルスクリプト nuget を作成
#!/bin/sh
script_dir="$(cd "$(dirname "${BASH_SOURCE:-${(%):-%N}}")"; pwd)"
mono --runtime=v4.0 ${script_dir}/nuget.exe $*
正しく実行されることを確認
$ chmod 700 nuget
$ ./nuget
$ nuget install FSharp.Data
$ cp FSharp.Data.2.2.5/lib/net40/*.dll .
$ rm -rf FSharp.Data.2.2.5
ダウンロードしたDLLを #r
で指定
#!/usr/bin/env fsharpi
#r @"FSharp.Data.dll"
open FSharp.Data
open FSharp.Data.JsonExtensions
let content = Http.RequestString("http://api.zipaddress.net/?zipcode=4530809")
let info = JsonValue.Parse(content)
printfn "content: %A" info
let pref = info?data?pref
printfn "pref: %A" pref
$ nuget install FSharp.Data
$ nuget install FSharp.Data.SqlClient
参考:http://fsprojects.github.io/FSharp.Data.SqlClient/configuration%20and%20input.html
#!/usr/bin/env fsharpi
#r @"FSharp.Data.dll"
#r @"Microsoft.SqlServer.TransactSql.ScriptDom.dll"
#r @"FSharp.Data.SqlClient.dll"
open FSharp.Data
open FSharp.Data.SqlClient
//Literal属性で定数化する
//Blogデータベースに sa/password で接続する
[<Literal>]
let connStr = @"Server=10.19.255.84;Initial Catalog=BLOG;User ID=sa;Password=password"
[<Literal>]
let query = "select id,name from dbo.Person"
type SelectUser = SqlCommandProvider<query, connStr>
(new SelectUser())
.Execute()
|> Seq.toArray
|> printfn "result: %A"
$ nuget install SQLProvider -prerelease
$ nuget install Npgsql
参考:http://fsprojects.github.io/SQLProvider/
#!/usr/bin/env fsharpi
#r @"FSharp.Data.SQLProvider.dll"
open FSharp.Data.Sql
type sql =
SqlDataProvider<
ConnectionString =
"""
Host=127.0.0.1;
Port=5432;
Database=Blog;
Username=postgres;
Password=password
""",
DatabaseVendor = Common.DatabaseProviderTypes.POSTGRESQL,
ResolutionPath = @"/Users/hana/fsharp/test/",
UseOptionTypes = true
>
let ctx = sql.GetDataContext()
//とりあえず取得する
ctx.``[public].[blogpost]``
|> Seq.map (fun e -> e.postid)
|> Seq.toArray
|> printfn "post: %A"
//where句やjoin句を指示して取得する
query {
for post in ctx.``[public].[blogpost]`` do
//Join指示
//comment.postid は nullable なので、Optionとして型推論される、そのために.Valueを付加する
join comment in ctx.``[public].[blogcomment]`` on (post.id = comment.postid.Value)
where (post.postid="99999")
sortByDescending post.id
select (post.id,commet.id)
}
|> Seq.toArray
|> printfn "posts: %A"