ホームに戻る
 F#メモ

0、はじめに

F#は関数型言語です。
.NETフレームワークでの使用に適しています。
C#との違いは主にF#が関数型であるという点です。
コンパイルは以下のようにします。

>fsc test.fs

また、モジュールは、
文中に module test と記述し -a をつけます。
すると test.dll というファイルができます。

>fsc -a test.fs

モジュールを使用する場合には、
open test と記述します。

1、例文その1(基本)

// test.fs

(*  コメント
   (* F# においてインデントは重要である *)
*)

(* モジュールの読み込み *)
open System

(*  単位
   計算に単位を用いることができる
   単位の整合性が保たれる
*)

[<Measure>] type km
// let d(km: float<km>) = 1.0<km> + 2.0<km>

(*  判別共用体
   文字列を型と結びつける
*)

type Gender = Male | Female
// let a : Gender = Male などとして使用

(*  列挙型
   文字列と数字を結びつける
*)

type bt = A = 0 | B = 1 | O = 2 | AB = 3
// let a = bt.A などとして使用

(*  レコード
   ある複数の型を持つセットの型を定義します
*)

type man = {name:string; age:int; gen:Gender}
//let a = {"Tom", 20, Male} として値をセット
//let s = a.name として値を取り出す

(*  ジェネリック型
   後から自由に型を決めることができる型
*)

type man<'T> = {name:string; value:'T}
// let a<int> = {"Tom", 20} 

[<EntryPoint>]
let main(args : string[]) =

  (*  変数
     変数は値を変更しないことが前提である
     mutable を使えば代入できるが特別な使い方である
     型は基本的には指定しない
     もし、指定するのであれば :int などをつける
     sbyte, byte, int, uint, int64, float, double
     char, string, bool, unit などの型がある
  *)
  let n = 3
  let n2:int64 = 4L
  let n3:int = 0xFF
  let n4:int = 0B1101
  let f:float = 3.9
  let b:bool = true
  let c:char = 'c'
  let s1 : string = "\\100"
  let s2 : string = n.ToString()

  (*  文字列
     文字列は結合できます
     また、範囲を決めて部分的に取り出すことができます
  *)

  let s1 = "abc"
  let s2 = s1 + "def"
  printfn "string:%c" s1.[1]
  printfn "string:%s" s2.[2..4]
  printfn "string:%d" s2.Length

  (*  mutable
     値を代入する変数を指定できる
     代入は <- を使う
     = は代入の意味では無いので注意する
  *)

  let mutable x = 3
  x <- 4

  (*  参照型 変数
     実体を直接持たずに値を参照だけする
  *)

  let r = ref 4
  r := 1     // 参照を 4 から 1 に変更
  let x = !r // ! で参照値を得る

  (*  nan と infinity
     非数と無限大を意味します
  *)

  let x = 0.0 / 0.0
  let b = if x = nan then 1 else 0
  let x = 1.0 / 0.0
  let b = if x = infinity then 1 else 0

  (*  関数
     関数は関数自体が値である
     すでに変数は値を代入しないのが基本であると書いたが
     変数は関数と結びつけることにより値を変更できる
     また、fun を使って関数名を指定しない関数を定義できる
     これをラムダ式とか呼ぶ
  *)
  let f1 x (y:int) : int = x * 2 + y
  let f2 x = let a = 1 in x + a // a = 1 を固定の変数とする
  let swap (aa, bb) = (bb, aa)
  let x = (fun y -> y + 1) 4

  let x = f1 3 0
  let y = f1 (f2 1) 1
  let z = float x // 型のキャスト

  (*  合成関数
     関数を続けて順番に呼ぶことができる
     例えば (f1 >> f2 >> f3) とし f1, f2, f3 の順に呼ぶ
     (f1 << f2 << f3) も可能である
  *)

  let f1 x = x + 1
  let f2 x = x + 2
  let f3 x = x + 3

  let x = (f1 >> f2 >> f3) 1

  (* unit 型
    属性の存在しない型に unit 型がある
    unit 型が唯一もつ値は () である
    例えば、 let f () = 3 という関数を書くことができる
    これは () を略して let f = 3 と書くこともできる
    すなわち定数 f に 3 を設定する表現と同じである
   *)

  let f () = 3

  (*  box と unbox
     box x とすると定数をオブジェクト型に変換する
     unbox<int> x とすると int 型に戻す
  *)

  let check x = // 型チェック
    match box x with
    | :? int -> "int"
    | :? float -> "float"
    | _ -> "other"

  // unbox<int> x // 戻す場合

  (*  累乗
     2 ** 3 で2の3乗の意味になる
  *)

  (*  演算子
     !%&*+-./<=>@^|~? を使って演算子を記述できる
  *)

  let (!*) x = x + 1
  let y = !*3

  (*  ビット演算子
    &&&, |||, <<<, >>> を使用する
   *)

  let x = 6 &&& 3

  (*  パイプライン
     パイプラインは |> と書きます
     通常は左から右への流れを右から左に変えることができます
     また |> ignore と書くと結果を無視することができます
     引数が2のときは ||> となります
  *)

  let f1 x = x + 1
  let f2 x y = x + y
  let x = (f1 1 |> f1)
  f1 1 |> ignore
  (1, 3) ||> f2 |> ignore

  (*  再帰関数
     関数の内部から自らの関数を呼び出す構造です
  *)
  let rec fact n = (
    if n < 0 then
      1
    else
      fact (n - 1)
    )

  (*  inline 関数
    インライン化できる関数のことです
  *)

  let inline f x = x + 1

  (*  比較
     =, <>, <, >, <=, >=, not, ||, && が使える
  *)

  let t = if 1 <> 1 then 1 else 0 // 1 = 1 なので t = 0

  (*  タプル
    複数の型をセットで持つことができる
  *)

  let tuple = (3.0, "abc")
  let f (x, y) = (x + 1.0, y + "+a")
  let (a, b) = tuple |> f

  (*  配列
     配列の要素は特に指定しなくても mutable です
     配列の長さは固定です 要素の追加はできません
     多次元配列の作成が可能です
  *)

  let buf = [| "abc"; "def"; "ghi" |]
  buf.[0] <- "xyz"
  let array = Array.zeroCreate<int> 4
  let array = Array.create 4 "X" // X の入った要素 4 の配列
  let array2d = Array2D.create<int> 4 4 1 // 2次元配列
  array2d.[0, 0] <- 4

  for i in 0 .. array.Length - 1 do
    Array.set array i (i.ToString())
    printfn "array:%s " (Array.get array i)

  (*  リスト
     リストは配列と異なり新たな要素を追加できる
     リストは配列に変換できる この逆も可能
  *)

  let lst = [1; 2; 3]@[4..7]

  printfn "list:%d" (List.head lst)

  let nums = List.toSeq lst
  for n in nums do
    printfn "list:%d" n

  let lst2 = List.ofArray buf

  lst2 |> List.iter (fun x -> printfn "list:%s " x)

  let array2 = List.toArray lst2

  (*  begin と end
     ( と ) の代わりで使用できる
  *)
  begin

  end

  // if
  let mutable i = 0

  if i = 0 then
    printfn "if:%s" "i = 0"
  else if i = 1 then
    printfn "if:%s" "i = 1"
  elif i = 2 then
    printfn "if:%s" "i = 2"
  else
    printfn "if:%s" "false"

  // while
  let mutable i = 0

  while i < 3 do
    printfn "while:%d " i
    i <- i + 1

  // for
  for i = 0 to 3 do
    printfn "for:%d " i

  for i = 3 downto 0 do
    printfn "for:%d " i

  (*  シーケンス
    ループで使用する値を生成することができる
  *)

  let nums = seq{for n in 1..9 -> n * n}
  for n in nums do
    printfn "seq:%d" n

  let nums = seq{for n in 1..9 do
                   if n % 2 = 1 then yield n}
  for n in nums do
    printfn "seq:%d" n

  let nums = seq{for i in 1..9 do
                   for t in 1..9 do yield (i, t)}
  for (i, t) in nums do
    printfn "seq:%d %d" i t

  (*  match
    条件分岐できる
    ワイルドカード _ が使用できる
  *)

  match i with
  | 3 -> printfn "match:%s" "i = 3"
  | 4 | 5 -> printfn "%s" "i = 4 or 5"
  | _ -> printfn "%s" "i <> 3"

  match lst2 with
  | [_; _; x] -> printfn "match:%s" x
  | _ -> printfn "other"

  (*  function
     match の関数型
  *)

  let t =
    4 |> function
      | n when n > 0 -> 1
      | n when n < 0 -> -1
      | _ -> 0

  let f x = function
    | 1 -> x + 1
    | _ -> 0

  let t = f x 0

  (*  option 型
    Some と None を持つ
    関数の結果の判別などに使用できる
  *)

  let div x y = if y <> 0 then Some (x / y) else None
  let check = function
    | Some n -> n
    | None -> 0

  (5, 1) ||> div |> check |> printfn "option:%d"
  (5, 0) ||> div |> check |> printfn "option:%d"

  // try
  try
    1 / 0 |> ignore
  with
  | :? System.DivideByZeroException
       -> printfn "zero"

  0
  
2、例文その2(文字列とコア ライブラリ)
  
// test2.fs

(* 文字列 *)

let s1 = "abcdefg"
let s2 = "12345"

printfn "%c" (s1.Chars(3))
printfn "%b" (s1.Contains("b"))
printfn "%s" (s1.Insert(1, "b"))
printfn "%s" (s1.Remove(1, 4))
printfn "%d" (s1.IndexOf('b'))
printfn "%s" (s1.Trim('a'))
printfn "%d" (int s2)

// コア ライブラリ

(* 配列 *)

let print_array array =
  for i = 0 to ((Array.length array) - 1) do
    printf "%d " array.[i]
  printfn ""

let array1 = [| 1 .. 10 |]
printfn "%A" array1
let array2 = Array.zeroCreate 20
Array.blit array1 3 array2 5 4 // array1 の 3 から array2 の 5 に4つの要素を移動
print_array array2
Array.fill array1 2 3 0
print_array array1
Array.iter (fun x -> printf "%d:" x) array1
printfn ""

//let array = Array.ofList list
//let list = Array.toList array
//let seq = Array.toSeq array

(* リスト *)

let rec print_list list =
  match list with
  | head :: tail ->
    printf "%d " head
    print_list tail
  | [] -> printfn ""

let list = [3; 7; 4; 1]

print_list list

print_list (List.sort list)

print_list begin List.sortWith (fun x y ->
      match x < y with
      | true -> y - x
      | false -> y - x
      )
    list
  end

print_list (List.permute (fun x -> (x + 1) % (List.length list)) list)

print_list (List.append list list)

if (List.isEmpty list) then printfn "empty" else print_list list

print_list (List.rev list)
printfn "%d" (List.nth list 2) // 3番目の要素を取り出す
printfn "%d" (List.max list)
printfn "%d" (List.min list)
printfn "%d" (List.sum list)
printfn "%f" (List.average (List.map float list))

printfn "%b" (List.exists (fun x -> x < 0) list) // いずれか1つ
printfn "%b" (List.forall (fun x -> x > 0) list) // すべてが

print_list (List.map (fun x -> x + 1) list)

printfn "%d" (List.reduce (+) list) // 結果は sum と同じになる
printfn "%d" (List.fold (+) 0 list) // fold は初期値 0 を持ちます
print_list (List.scan (+) 0 list)    // 経過をリストで返す

print_list (List.filter (fun x -> x > 3) list) // 3 より大きい数のリストへ

let result = (List.tryFindIndex (fun x -> x = 7) list) |> function
  | Some n -> n
  | None -> -1

printfn "%d" result

//let array = List.toArray list
//let seq = List.toSeq list
//let list = List.ofArray [|1; 2; 3|]

(* シーケンス *)

let seq = seq ( { 1..9 } )
let seq1 = Seq.singleton "sequence:1"
for i in (Seq.take 4) seq do // 4つ目までのシーケンスを使用
  printf "%d " i
printfn ""
for i in (Seq.skip 4) seq do // 4つ目までのシーケンスを読み飛ばす
  printf "%d " i
printfn ""

let list2 = [1;2;3;4;3;6]
for i in (Seq.takeWhile ((<>) 3) list2) do
  printf "%d " i
printfn ""
for i in (Seq.skipWhile ((<>) 3) list2) do
  printf "%d " i
printfn ""
for i in (Seq.distinct list2) do // 重複を取り除く
  printf "%d " i
printfn ""
for i in (Seq.unfold (fun x -> if x <= 9 then Some(x, x+1) else None) 0) do
  printf "%d " i
printfn ""

(* マップ *)

let dic = Map.ofList [1, "One"; 2, "Two"; 3, "Three"]
printfn "%s" ((Map.tryFind 2 dic) |> function | Some n -> n | None -> "none")

3、例文その3(クラス)

// test3.fs

open System

(* 他のファイルから利用されないシンプルなクラス *)
type private aClass = class end

[<AbstractClass>]
type A(x, y) =
  do printfn "inited"
  abstract member f : int -> int   // 抽象
  default this.f n = n             // 仮想

type A1(x, y) =
  inherit A(x, y)                  // 継承
  let mutable num1 = 0
  override this.f n = n + 1        // オーバーライド
  member this.Num with get() = num1
                  and set newNum = num1 <- newNum
  member this.print = printfn "a1:%d" num1
  member private this.f1 n = n + 2 // 隠蔽
  member this.add a = x + a
  member this.add a b = x + a + b  // オーバーロード

type A2(x, y) =
  inherit A1(x, y)
  static let num2 = 5
  override this.f n = n + 1
  member this.print = printfn "a2:%d" num2

type IA = // インターフェイス
  abstract member print : unit -> unit

type AA =
  new () = {}
  interface IA with
     member this.print() = printfn "interface"

[<EntryPoint>]
let main (args:string[]) =
  let a1 = new A1(2, 3)

  a1.Num <- 1

  a1.print

  printfn "a1:f:%d" (a1.f 2)

  let a2 = new A2(2, 3)

  a2.Num <- 2

  a2.print

  printfn "a2:f:%d" (a2.f 2)

  let aa = new AA()

  (aa :> IA).print()

  0

4、例文その4(.NET)

// test4.fs

open System                // Random,Math,String を含む
open System.Windows.Forms  // Form,RichTextBox,Button を含む
open System.Drawing        // Point,Size を含む

Application.EnableVisualStyles()

let form = new Form(Text="GuiApp", Visible=true)
form.ClientSize <- new Size(240, 72)

let edit = new RichTextBox()
edit.Multiline <- false
edit.Location <- new Point(0, 0)
edit.Size <- new Size(240, 24)

let btn = new Button()
btn.Text <- "Click"
btn.Location <- new Point(0, 24)
btn.Size <- new Size(240, 24)

let lbl = new Label()
lbl.Text <- "   "
lbl.Location <- new Point(0, 48)
lbl.Size <- new Size(240, 24)

let OnKeyDown (e : KeyEventArgs) =
    if e.KeyCode = Keys.Return
      then
        if not (String.IsNullOrEmpty edit.Text) then
          lbl.Text <- edit.Text
          edit.Text <- ""
        e.Handled <- true

let OnClick() =
  let rnd:Random = new Random()
  let num = rnd.Next(6)
  lbl.Text <- edit.Text + ":" + (num + 1).ToString()

edit.KeyDown.Add(OnKeyDown)
btn.Click.Add(fun e -> OnClick())

form.Controls.Add edit
form.Controls.Add btn
form.Controls.Add lbl

[<STAThread>]
do Application.Run(form)

inserted by FC2 system