Open12

F# の学習ログ

F# をやってみたいと思った。

hello world

https://qiita.com/cannorin/items/59d79cc9a3b64c761cd4
を読んでいく。
dotnet new console -lang="F#"
// Learn more about F# at http://docs.microsoft.com/dotnet/fsharp

open System

// Define a function to construct a message to print
let from whom = sprintf "from %s" whom

[<EntryPoint>]
let main argv =
    let message = from "F#" // Call the function
    printfn "Hello world %s" message
    0
dotnet add package FSharp.Scanf

するとパッケージが入る。パッケージマネージャはnugetらしい。

open FSharp.Scanf

printfn "input a number"

try
    let ans = scanfn "%i"

    if ans = 42 then
        printfn "the ultimate answer"
    else
        printfn "recieved %i" ans
with _ -> printfn "not a number... :("
  • open というのがimportに相当するっぽい。正確には FSharp.Scanf 名前空間にある物体を展開する感じか。
  • "%i" でフォーマットなのはわかる。興味深いのは %i にシンタックスハイライトが効いているところ、関数適用に括弧がいらないところ。
    • 関数適用がスペースでできる言語でフォーマットするのが新鮮
  • scanfn はライブラリの関数みたいだけど、それに対してフォーマット文字列にハイライトが効いてるのが気になる
    • scanfnPrintfFormat<'a,'b,'c,'d, ^e>printfnTextWriterFormat<'T> 型を受け取っている。特定のクラスを継承した文字列を特別に扱っているのかもしれない。そしてそのカスタマイズの幅は明らかに広そう
  • [<EntryPoint>] という属性をつけるとエントリーポイントになるらしい。コマンドライン引数を受け取れる。
  • 関数定義はHaskellぽく名前から続けて引数を書くやつ。

年相応に英語の表現もメモしておく。

  • conciseness…簡潔さ
  • be cluttered up with…~でとっ散らかった
  • compared with <名詞>, …<>と比較して、

パイプライン演算子とかListを範囲指定で作るやつとかがある。そして地味にフォーマットで部分適用ができるのがすごい。

[ 1 .. 100 ] |> List.sum |> printfn "sum=%d"

型定義とか。けっこう軽率にADTが作れる。

type Person = { First: string; Last: string }

type Employee =
    | Worker of Person
    | Manager of Employee list

let asak = { Last = "Agoshi"; First = "Sakina" }
let schi = { Last = "Chiaki"; First = "Soen" }
let worker = Worker asak
let employee = Manager [ Worker schi; worker ]

1文字をパースするやつ。

let pChar char str =
  if System.String.IsNullOrEmpty(str) then
    let msg = "No more input"
    Error msg
  else
    let first = str.[0]

    if first = char then
      let remaining = str.[1..]
      Ok(char, remaining)
    else
      let msg = sprintf "Expected %c, got %c" char first
      Error msg

[<EntryPoint>]
let main _ =
  let pA = pChar 'A'
  printfn "%A" (pA "ABC")
  printfn "%A" (pA "ZBC")
  0

2つのパーサを試すやつ。

let either parser1 parser2 =
  let innerFn input =
    match run parser1 input with
    | Ok _ as r -> r
    | Error _ -> run parser2 input

  Parser innerFn

let (<|>) = either


[<EntryPoint>]
let main _ =
  let pA = pChar 'A'
  let pAOrB = pChar 'A' <|> pChar 'B'
  printfn "%A" (run pAOrB "ABC")
  printfn "%A" (run pAOrB "BBC")
  printfn "%A" (run pAOrB "ZBC")
  0
let choice parsers = List.reduce (<|>) parsers

[<EntryPoint>]
let main _ =
  let parseAOrBOrC =
    [ 'A'; 'B'; 'C' ]
    |> List.map pChar
    |> choice

  printfn "%A" (run parseAOrBOrC "ABC")
  printfn "%A" (run parseAOrBOrC "BBC")
  printfn "%A" (run parseAOrBOrC "CBC")
  printfn "%A" (run parseAOrBOrC "ZBC")
  0
ログインするとコメントできます