Open6

F# 備忘録

あめ☔️あめ☔️

再帰的に非同期処理を実行する (再帰関数)

let rec patchy (ms: int) = async {
  "むきゅー" |> stdout.WriteLine
  do! Async.Sleep ms
  return! patchy ms
}

2000 |> patchy |> Async.StartImmediate
stdin.ReadLine()

Async.StartImmediate は現在のスレッド、Async.Start はスレッドプールで実行され、いずれも結果を待機しない。そのため、ReadLine() しないとプログラムが終了してしまう。
通常、結果を待つ場合は Async.RunSynchronously を利用するが、ReadLine() しない場合は1回目の再帰関数が終了するタイミングで上記と同様にプログラムが終了してしまう。

あめ☔️あめ☔️

複数行にまたがる文字を表示する (AA など)

let jumpluff = """
                         , ‐'''" ̄``ヽ、
                        /          ',
                        !          |
                        l             !
                         \        /
                           -7‐トt―‐''"
             , ―― 、, -‐`^‐‐'‐- 、  _
            l     /  o   o   >'"   ヽ
            |    ,! "  、_,、,. ,,  /      |
            \_,!         |       |
                    !         ヽ、_____/
                    ヽ、           /
                        ー‐、---,- ‐<)
                            `ー'
"""

jumpluff |> stdout.WriteLine

""" で囲うだけ。

あめ☔️あめ☔️

Stopwatch で処理速度を計測する

open System.Diagnostics

// 経過時間の整形 (hh:nn:ss.fff)
let elapsed (sw: Stopwatch)  =
  sprintf "%02d:%02d:%02d.%03d" sw.Elapsed.Hours sw.Elapsed.Minutes sw.Elapsed.Seconds sw.Elapsed.Milliseconds

// 計測開始
let sw = Stopwatch()
sw.Start()
// 1行でも書ける
// let sw = Stopwatch.StartNew()

// 処理例
async {
  do! Async.Sleep 2000
} |> Async.RunSynchronously

// 計測終了
sw.Stop()

// 経過時間の出力
sw |> elapsed |> stdout.WriteLine

// 経過時間をリセットして再計測する場合
sw.Reset()
sw.Start()
// 1行でも書ける
//sw.Restart()

// 以下略
あめ☔️あめ☔️

ProcessX で外部プロセスを実行する

#r "nuget: ProcessX, 1.5.5"
open Cysharp.Diagnostics

try
  // 結果を文字列で取得
  let result = ProcessX.StartAsync("dotnet --version").FirstAsync() |> Async.AwaitTask |> Async.RunSynchronously
  result |> stdout.WriteLine

  // 終了の待機 (結果を利用しない場合)
  ProcessX.StartAsync("cmd /c mkdir mukyu").WaitAsync() |> Async.AwaitTask |> Async.RunSynchronously

  // 結果の出力
  ProcessX.StartAsync("dotnet --info").WriteLineAllAsync() |> Async.AwaitTask |> Async.RunSynchronously
with
| ex -> ex.Message |> stdout.WriteLine

自前で ProcessStartInfo を定義する必要がなくなって楽。ほかにもいろいろ小回りがきくので、なにか困ったら公式ドキュメントを参照。


https://www.nuget.org/packages/ProcessX
https://github.com/Cysharp/ProcessX

あめ☔️あめ☔️

DateTimeOffset の曜日表記をシステム言語以外に設定する

open System
open System.Globalization

let now = DateTimeOffset.Now
let format = "yyyy/MM/dd(ddd) HH:mm:ss.fff"
let culture = CultureInfo("ja-JP")
now.ToString(format, culture) |> stdout.WriteLine

System.Globalization.CultureInfo で言語コードを指定する。

// 2006/1/2(月) 15:04:05.000 // "ja-JP"
// 2006/1/2(Mon) 15:04:05.000 // "en-US"
あめ☔️あめ☔️

FSharp.Data のパース例

type DummyJson = JsonProvider<"dummy.json">

let json =
  """
    [ { "id": 1, "name": "remiria" }, { "id": 2, "name": "flandre" } ]
  """

let parse = json |> DummyJson.Parse

printfn "Id: %d, Name: %s" parse[0].Id parse[0].Name
type DummyXml = XmlProvider<"dummy.xml">

let xml =
  """
    <ScarletDevilMansion>
      <resident>
        <id>1</id>
        <name>remiria</name>
      </resident>
      <resident>
        <id>2</id>
        <name>flandre</name>
      </resident>
    </ScarletDevilMansion>
  """

let parse = xml |> DummyXml.Parse

printfn "Id: %d, Name: %s" parse.Residents[1].Id parse.Residents[1].Name
type DummyCsv = CsvProvider<"dummy.csv">

let csv =
  """
    id,name
    1,remiria
    2,flandre
  """

let parse = csv |> DummyCsv.Parse

parse.Rows
|> Seq.iter (fun x -> printfn "Id: %d, Name: %s" x.Id x.Name)

サンプルデータをプロバイダーに噛ませることでよしなに解釈してくれる。