かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

手軽なスクリプト言語としてのF#

F#の本を、一通り読んで、なかなか関数脳になれない今日この頃です。特に再帰呼び出しが複雑になってきたりすると頭がパンクしちゃいます。ここらへんは、時間をかけて慣らしていくしかないんでしょうが、なかなか馴染まないところです。

ただ、F#はOOP言語としての側面もあるので、その気になればC#みたいなコードの書き方でいけなくもありません。しかも、F#で個人的に気に入ってる所があって、それは拡張子fsxをfsi.exe(F#の対話型シェル)に関連づけておくとコンパイルしなくてもコードを実行することが出来る所です。その上、型にはうるさい言語なのでVisual Studioを使えばインテリセンスつきの環境でコードが書けます。.NET Frameworkのクラスライブラリも使えるので、C#VB使いにとってはとてもとっつきやすいスクリプト言語としての一面もあると思います。


ということなので、邪道ですがOOPというか簡易的な処理を実装するためのスクリプト言語としてF#を見た場合に、どういったものがあると書けるかということで適当に出来ることを抜き出してみました。

Hello world

とりあえずHello worldから見てみましょう。

printfn "Hello world"

これだけです。Mainメソッドとかいりません。非常にお手軽です。こういうお手軽さがないと、ちょっとした作業を自動化するのには仰々しいと感じてしまいます。

変数の宣言

変数というと違うのですが、イメージとしては変数ですね。let 変数名 = 値で定義出来ます。早速やってみます。

let s = "Hello world"
printfn "%s" s

変数と違うというのは、再代入が出来ないからです。このようなコードを書くとコンパイルエラーになってしまいます。

let s = "Hello"
// 代入は <- 演算子を使う
s <- "こんにちは"

基本的に関数型言語は、こういう制約があるのが多いです。とはいっても、この制約を取っ払うことは出来ます。mutableキーワードがそれです。

let mutable s = "Hello world"
printfn "%s" s

s <- "こんにちは世界"
printfn "%s" s

その他にはref関数を使って変更可能な値を作ることも出来ます。

let s = ref "Hello world"
printfn "%s" !s

s := "こんにちは世界"
printfn "%s" !s

ref関数で作った値はstring型ではなくてstring refになります。こいつは、内部にmutableなフィールドを持った型になってて!演算子で値にアクセスしたり:=演算子で値の代入が出来るようになっています。普通の変数(C#やらから見たときの)が欲しいと思ったらmutableかrefを使えばいいと思います。

分岐

基本ですよね分岐。if文(if式っていうのが正しいのかな?)がF#にもあります。

let i = 10

if i % 15 = 0 then
    printfn "15で割れる"
else if i % 5 = 0 then
    printfn "5で割れる"
else if i % 3 = 0 then
    printfn "3で割れる"
else
    printfn "その他"

非常に簡単ですね。かっこじゃなくてインデントでブロックを表してるあたり何だかパイソンを思い出しました。

ループ

ループも出来ます。ループにはwhileとforがあります。これも書き方を覚えるだけなのでさくっと書いちゃいましょう。
まずはwhileです。

// 10からカウントダウンしていく
let i = ref 10
while !i > 0 do
    printfn "%d" !i
    decr i // decrはref intをデクリメントしてくれる関数

条件式の後にdoを書いてインデントでブロックを表してる感じです。一番潰しが効くループだと思われます。


次は原始的なforループです。

for i = 1 to 10 do
    printfn "%d" i

1〜10までを画面に出力します。特に難しいところはないですね。


最後にC#でいうforeachのfor文です。因みにF#ではリストは[値;値;値]のように記述します。それじゃ見てみましょう。

let lst = [1;2;3;4;5;6;7;8]
for i in lst do
    printfn "%d" i

これでなんと

ここまでで、なんと順次実行・分岐・ループが出来るようになったので頑張れば一通りのことは出来そうです。後は.NETの便利なクラスライブラリが使えれば・・・!?ということなので使ってみましょう。

.NETのクラスの使い方

ということで、ディレクトリ一覧の取得でもやってみようと思います。.NETでディレクトリの一覧を取得するとなるとSystem.IO.Directoryクラスの出番です。F#ではC#でいうusingに相当することをopenを使ってやります。あとは、ふつうにクラスを使えばOKです。

open System.IO

let dirs = Directory.GetDirectories(@"C:\Windows")
for dir in dirs do
    printfn "%s" dir

これでC:\Windowsフォルダの下にあるフォルダの一覧を画面に出すことが出来ます。

今日のまとめ

とりあえず、関数型言語のうまいところは何も使ってません!でも、これだけでもちょっとした処理を組むくらいなら出来そうです。こういう所からとっかかりを見つけて、ちょっとずつ関数型言語の深みにどっぷりとつかっていくのも一興かと思います。
気が向いたら、ちょっとずつ関数型言語ちっくな側面を紹介していけたらな〜と思います。ではでは。

おまけ

ついでに自分でクラスを定義する方法。C#でいう以下のようなクラスを定義してみました。

class Person
{
  public string Name { get; set; }
  public void Show()
  {
    Console.WriteLine("私の名前は {0} です", this.Name);
  }
}
type Person() =
    let mutable name : string = null

    member this.Name
        with get() = name
        and set(v) = name <- v

    member this.Show() =
        printfn "名前は %s です" this.Name

// 使ってみる
let p = Person()
p.Name <- "田中 太郎"
p.Show()