タイダログ

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

PowerShell 片手に F# を始める

f:id:taidalog:20211123174711p:plain
F# のアイコン

先日、F# を始めました。「PowerShell のあれは F# ではどうやるんだろう」という風に調べながら書いたので、わかったことをまとめておきます。随時更新します。

F# を始めた理由

  • 新しいことを始めたかったから
  • 関数型プログラミングに興味があったから
  • .NET 上で動くから
  • パイプラインが使えるから
  • アイコンが綺麗だから

生徒に勉強せい勉強せいと言っておきながら自分が勉強しないのはいけないと思い、かねてより興味があった関数型プログラミングを始めることにしました。言語は、すでにパソコンに入っていた F# を選びました。.NET 上で動くので PowerShell の経験を活かせますし。

関数と聞くとExcel関数を思い浮かべるのではないでしょうか。カッコの中にカッコを入れて、さらにカッコをカッコするとわけわからんですよね。それに、思考と関数の順番が逆なのがしんどいです。先に処理する関数を内側に、後に処理する関数を外側に書き、読むときは内側から外側へ向かっていくという、あれです。

たとえばこの数式。検索して、置換して、スペースを削除して、数値化します。

=VALUE(TRIM(SUBSTITUTE(VLOOKUP(A1,$D$2:$G$100,2,FALSE),"'","")))

F# も関数がメインですが、パイプラインという機能を使って括弧迷宮からの脱出を図っています。先ほどのExcel の数式を例にするとこんなイメージです。

=VLOOKUP(A1,$D$2:$G$100,2,FALSE) |> SUBSTITUTE(,"'","") |> TRIM() |> VALUE()

検索して、置換して、スペースを削除して、数値化。順番どおりです。パイプラインが使えることも PowerShell との共通点となり、ますます入門のハードルが下がったのでした。

あとアイコンが青系統の色の組み合わせで綺麗。この感じ好き。スマホの壁紙と待受とカバーも青系統です。

f:id:taidalog:20211123174711p:plain
F# のアイコン

画像は F# Software Foundation のサイトより
https://fsharp.org/

そういえばこの色合い、どこかで見たような……水色と青色……水色の髪…青いスカート……綾波

f:id:taidalog:20211123181333j:plain
綾波レイ

画像は EVANGELION STORE ONLINE さんのツイッターより
https://twitter.com/eva_store/status/1316682824468983810

綾波じゃないか!

ということは 'F#' の 'F' は 'First children' の 'F' なのでは?1 拡張子2の '.fs' は 'FirSt' ですね!3 '#' はシャープではなく番号記号またはナンバーサインといって数字に付ける記号なので4、やはり 'F' は数を表しているわけですよ!5

……いかんいかん興奮してしまいました。一旦落ち着いて話をまとめましょう。

'F#' という名前は「ファーストチルドレン」という意味で、アイコンの水色と青色はそれぞれ綾波の髪と制服を表している。

決まりですね。綾波です。これは F# を始めるしかない!6

作業環境

PowerShell のあれは F# ではこうする

外国語を勉強する時、まずは母語で考えますよね。「日本語ではこう言うけど、英語ではなんて言うんだろう」みたいなことです。もちろん体系的に学ぶことも必要ですが、まずは自分の知っていることと結び付けるのがいいと思っています。

そういうわけで、「PowerShell ではこうするけど、F# ではどうやるんだろう」という風に調べたので備忘録として書いておきます。

「続きは、もう少し上手くなったら書く」(随時更新)

パイプライン

PowerShell

|

F#

|>

実際の使用例は後ほど。

1..10

PowerShell

# Array
1..10

F#

// List
[1..10];;

// Array
[|1..10|];;

// Seq
{1..10};;

F# では配列的なものが何種類かあるようです。カッコで種類が変わるんですね。カッコいい。違いはまだよくわかっていません。List要素の追加要素への再代入ができなくて、Array はできて、Seq は頭がいいらしいです。この記事では List を使います。(2021/11/18 訂正)

2021/11/18 加筆ここから

Array も要素の追加はできないようで、できるのは要素への再代入でした。List, Array ともに、append 関数でリスト同士、あるいは配列同士をつなげることができるようです。List の場合は :: (cons) 演算子@ 演算子もあるんですね。

2021/11/18 加筆ここまで

スクリプトブロック

PowerShell

{ param ( [int]$Age ) $Age -eq 14 }

# `&` 演算子で実行する
$reiAge = 14;
&{ param ( [int]$Age ) $Age -eq 14 } $reiAge
# True

F#

fun age -> age = 14;;

// 引数を渡すだけで実行できる
let reiAge = 14;;
(fun age -> age = 14) reiAge;;
// val it: bool = true

F# ではスクリプトブロックの代わりにラムダ式を使うようです。

ForEach-Object

PowerShell

1..10 | ForEach-Object { $_ * 2 }

# 変数を2つ使う
$n = 2
1..10 | ForEach-Object { $_ * $n }

F#

[1..10] |> List.map (fun x -> x * 2);;

// パラメータ2つ
let n = 2;;
[1..10] |> List.map ((fun x y -> x * y) n);;

ForEach-Objectmap 関数なんですね。

PowerShell$_ 自動変数を使って簡潔に書けるので楽です。一方 F# のラムダ式は複数のパラメータを持つ場合でも書きやすくていいと感じました。(fun x y -> x * y)xy の2つのパラメータを持つラムダ式です。(fun x y -> x * y) n という風に引数をひとつだけ渡すと、残りの引数を待っている状態のラムダ式が出来上がります(nx に渡っています)。その残りのひとつをパイプラインで渡すことで式の評価が始まるわけですね(たぶん)。

Where-Object

PowerShell

1..10 | Where-Object { $_ % 2 -eq 0 }

F#

[1..10] |> List.filter (fun x -> x % 2 = 0);;

Where-Objectfilter 関数。filter という名前が処理の内容を的確に表していていいですね。

変数への代入

PowerShell

$myEva = 0

F#

let myEva = 0;;

F# では、というか関数型では代入ではなく「束縛」というんですね。そして変数への再代入は原則できません。どうしても必要ならば mutable キーワードを付けます。

let myEva = 0;;
myEva <- 2;;
// この値は変更可能ではありません。というエラーが出る
// 他人のエヴァには乗れません!

let mutable myEva = 5;;

// 再代入
myEva <- 2;;
// できる。なんですって!?

function の定義

PowerShell

function CanPilotEva {
    Param (
        [int]
        $Age
    )


    $Age -eq 14
}

# 呼び出す
$reiAge = 14
CanPilotEva -Age $reiAge

F#

let canPilotEva age = 
    age = 14;;
// val canPilotEva: age: int -> bool

// 呼び出す
let reiAge = 14;;
canPilotEva reiAge;;
// val it: bool = true

パラメータの型は自動で推測してくれます(型推論)。この場合、14 と比較するということは age も整数だろうということで int だと判断してくれています。int -> bool は、「int をひとつ受け取って bool を返す関数」という意味です。

.NET の静的メソッドの呼び出し

PowerShell

[Math]::Sqrt(42)

F#

System.Math.Sqrt(42);;

// あるいは open する
open System;;
Math.Sqrt(42);;

PSCustomObject

PowerShell

[PSCustomObject]@{
    Age = 14
    Number = 1
    EVA = EVA-00
}

F#




一番近いのは構造体なのかな?

外部スクリプトやモジュールの読み込み

PowerShell

# 外部スクリプトの読み込み
. .\script.ps1

# モジュールの読み込み
Import-Module .\module.psm1

F#

#load "script.fsx";;
open Script;;

open Script が必須です。open <ファイル名の拡張子を除いた部分の先頭大文字> の形式です。ファイル内で名前空間やモジュール名を付けている場合はそれを使うようです。

F# インタラクティブ (dotnet) リファレンス | Microsoft Docs#他のスクリプトの読み込み


続きは鋭意製作中。

結び

覚えたい英単語と日本語をペアにしてノートに書くように、PowerShell と F# をペアで書いてみました。今後調べて分かったことは追々書いていきます。

参考

更新履歴

  • 1..10」 の ListArray について誤りを訂正。「要素の追加ができる」は誤りで、正しくは「要素への再代入ができる」でした。 (2021/11/28)

  1. ではない。

  2. 拡張子は他にも ‘.fsi’ や ‘.fsx’ があるそうです。

  3. ではない。

  4. 正しくは、 #123 のように数字の前につけます。

  5. ではない。

  6. 始めるしかない。