タイダログ

もっと怠けますか? (y/n)

タイダログが1周年なので PowerShell と F# にお祝いしてもらった

f:id:taidalog:20211114231514p:plain
2月8日はタイダログの誕生日

2月8日は記念すべきタイダログの誕生日です!

いつもお読みくださっているみなさま、ありがとうございます! おかげで1周年を迎えることができました。今後ともよろしくお願いいたします!

おや、1周年と聞きつけて PowerShell と F# が駆けつけてくれましたよ……?

f:id:taidalog:20211127032829p:plainf:id:taidalog:20211127034230p:plainf:id:taidalog:20211123174711p:plain
駆けつけてくれました!

作業環境

「おめでとう」

PowerShell と F# が "Happy Birthday to You" を歌ってくれるようです! 嬉しいですね!

PowerShell

まずは PowerShell から歌ってもらいましょう!

コンソールに貼り付けて Enter するか、.ps1 で保存して読み込むと歌ってくれます。

Enum DoReMi {
    Do0  = 440
    DoS  = 466
    Re   = 494
    ReS  = 523
    Mi   = 554
    Fa   = 587
    FaS  = 622
    So   = 659
    SoS  = 699
    La   = 740
    LaS  = 784
    Si   = 831
    Do1  = 880
    DoS1 = 932
    Re1  = 988
    ReS1 = 1047
    Mi1  = 1109
    Fa1  = 1175
    FaS1 = 1245
    So1  = 1319
    SoS1 = 1397
    La1  = 1480
    LaS1 = 1568
    Si1  = 1661
    Do2  = 1760
    DoS2 = 1865
    Re2  = 1976
    ReS2 = 2093
    Mi2  = 2218
    Fa2  = 2349
    FaS2 = 2489
    So2  = 2637
    SoS2 = 2794
    La2  = 2960
    LaS2 = 3136
    Si2  = 3322
}

$duration = 600
$duration1 = $duration / 3 * 1
$duration2 = $duration / 3 * 2
$duration3 = $duration / 3 * 3
$duration6 = $duration / 3 * 6

$interval = 20

$keyNotes = (
    ([DoReMi]::So, $duration2, $interval),
    ([DoReMi]::So, $duration1, $interval),
    ([DoReMi]::La, $duration3, $interval),
    ([DoReMi]::So, $duration3, $interval),
    ([DoReMi]::Do1, $duration3, $interval),
    ([DoReMi]::Si, $duration6, $interval),
    ([DoReMi]::So, $duration2, $interval),
    ([DoReMi]::So, $duration1, $interval),
    ([DoReMi]::La, $duration3, $interval),
    ([DoReMi]::So, $duration3, $interval),
    ([DoReMi]::Re1, $duration3, $interval),
    ([DoReMi]::Do1, $duration6, $interval),
    ([DoReMi]::So, $duration2, $interval),
    ([DoReMi]::So, $duration1, $interval),
    ([DoReMi]::So1, $duration3, $interval),
    ([DoReMi]::Mi1, $duration3, $interval),
    ([DoReMi]::Do1, $duration3, $interval),
    ([DoReMi]::Si, $duration3, $interval),
    ([DoReMi]::La, $duration6, ($interval + $duration * 3)),
    ([DoReMi]::Fa1, $duration2, $interval),
    ([DoReMi]::Fa1, $duration1, $interval),
    ([DoReMi]::Mi1, $duration3, $interval),
    ([DoReMi]::Do1, $duration3, $interval),
    ([DoReMi]::Re1, $duration3, $interval),
    ([DoReMi]::Do1, $duration6, $interval)
)

$invokeDoReMi = {
    param (
        [DoReMi[]]
        $Key,
        
        [int]
        $Duration,
        
        [int]
        $Interval = 50
    )
    
    [Console]::Beep([DoReMi]::$Key, $duration)
    Start-Sleep -Milliseconds $Interval
}

$keyNotes | ForEach-Object { &$invokeDoReMi -Key $_[0] -Duration $_[1] -Interval $_[2] }

わーい!

FSharp

それでは F# に歌ってもらいます! お願いします!

↓コンソールに貼り付けて一番最後に ;; を加えて Enter するか、.fsx で保存して dotnet fsi から #load すると歌ってくれます。詳しくはこちらの「外部スクリプトやモジュールの読み込み」へどうぞ。

taidalog.hatenablog.com

type DoReMi =
    | Do0  = 440
    | DoS  = 466
    | Re   = 494
    | ReS  = 523
    | Mi   = 554
    | Fa   = 587
    | FaS  = 622
    | So   = 659
    | SoS  = 699
    | La   = 740
    | LaS  = 784
    | Si   = 831
    | Do1  = 880
    | DoS1 = 932
    | Re1  = 988
    | ReS1 = 1047
    | Mi1  = 1109
    | Fa1  = 1175
    | FaS1 = 1245
    | So1  = 1319
    | SoS1 = 1397
    | La1  = 1480
    | LaS1 = 1568
    | Si1  = 1661
    | Do2  = 1760
    | DoS2 = 1865
    | Re2  = 1976
    | ReS2 = 2093
    | Mi2  = 2218
    | Fa2  = 2349
    | FaS2 = 2489
    | So2  = 2637
    | SoS2 = 2794
    | La2  = 2960
    | LaS2 = 3136
    | Si2  = 3322

let duration = 600
let duration1 = duration / 3 * 1
let duration2 = duration / 3 * 2
let duration3 = duration / 3 * 3
let duration6 = duration / 3 * 6

let interval = 20

let keyNotes = [|
    (DoReMi.So, duration2, interval)
    (DoReMi.So, duration1, interval)
    (DoReMi.La, duration3, interval)
    (DoReMi.So, duration3, interval)
    (DoReMi.Do1, duration3, interval)
    (DoReMi.Si, duration6, interval)
    (DoReMi.So, duration2, interval)
    (DoReMi.So, duration1, interval)
    (DoReMi.La, duration3, interval)
    (DoReMi.So, duration3, interval)
    (DoReMi.Re1, duration3, interval)
    (DoReMi.Do1, duration6, interval)
    (DoReMi.So, duration2, interval)
    (DoReMi.So, duration1, interval)
    (DoReMi.So1, duration3, interval)
    (DoReMi.Mi1, duration3, interval)
    (DoReMi.Do1, duration3, interval)
    (DoReMi.Si, duration3, interval)
    (DoReMi.La, duration6, (interval + duration * 3))
    (DoReMi.Fa1, duration2, interval)
    (DoReMi.Fa1, duration1, interval)
    (DoReMi.Mi1, duration3, interval)
    (DoReMi.Do1, duration3, interval)
    (DoReMi.Re1, duration3, interval)
    (DoReMi.Do1, duration6, interval)
|]

let invokeDoReMi (frequency, duration, interval) =
    System.Console.Beep(int(frequency), duration)
    System.Threading.Thread.Sleep(int(interval))

keyNotes |> Array.map invokeDoReMi

いえーい!

新しく覚えたこと

タプル

タプルというのは複数の値をひとまとめにしたものです。こんなの↓。

// タプル
("Rei", 0)

複数のデータをひとまとめという点では PowerShell の PSCustomObject や F# の匿名レコードに近いものだと捉えています(他にもたくさんあるんでしょうけどね、まだよくわからんのですよ)。

PSCustomObject や匿名レコードとの相違点として、値に名前を付けないということが挙げられるようです。

// タプル
("Rei", 0)
# PSCustomObject
[PSCustomObject]@{ Name = "Rei";  Eva = 0 }
// 匿名レコード
{| Name = "Rei"; Eva = 0 |}

タプル、シンプル。

ちなみにリストや配列と比較した場合、タプルには以下のような特徴があるようです。

  • 複数の型を保持できる
  • 素数が変更できない

タプルを引数に取る関数を書くのも簡単。これは3つの要素を持つタプルを引数に取る関数です。

let invokeDoReMi (frequency, duration, interval) =
    System.Console.Beep(int(frequency), duration)
    System.Threading.Thread.Sleep(int(interval))

受け取ったタプルの1つ目の要素を frequency、2つ目を duration、3つ目を interval として関数内で扱います。名前ではなくデータの順番で識別するというのは面白いですね。

System.Threading.Thread.Sleep

PowerShellStart-Sleep にあたるのは System.Threading.Thread.Sleep でよろしいのでしょうか。Async.Sleep だとか Task.Delay だとかが出てきたのですがまだ何もわかっておりません。

結び

ありがとー!

零号機改カラーの Windows PowerShell とね、黒波カラーの PowerShell とね、綾波カラーの F# がね、お祝いしてくれるなんてね、もう感涙に噎びますよ。それでね、たどたどしさがね、一生懸命練習してくれたんだなって思うとね、もうね、もうね。

System.Console.Beep を連続で呼び出すとき、duration を短くすると音を飛ばすのが解せん。あとたまに長い音がブツブツ途切れるのも不可解。

System.Speech.Synthesis.SpeechSynthesizer を忘れていたのは内緒だ!

docs.microsoft.com

謝辞

画像は以下のサイト様からお借りしました。ありがとうございました。

トップのケーキの画像
夏のパーティーブルー系イラスト - No: 22059005/無料イラストなら「イラストAC」

Windows PowerShell のアイコン画
PowerShell Team - Automating the world one-liner at a time…

PowerShell のアイコン画
PowerShell - Wikipedia

F# のアイコン画
F# Software Foundation

参考