XMLファイルをコピーする(5)


F#は2002年からMicrosoft ResearchのDon Symeらによって開発されたマルチパラダイム言語です.Visual Studioに載ったのは2010からとまだ新しいです.私はまったくの初心者ですが、書いてみるとけっこう面白いです.Amazonで本を探してみると、F#はScalaよりはぐっと少ないですね.でもScalaJavaプラットフォーム専門で.NETプラットフォームでの開発はすでに中止されてしまったそうですから、F#は.NET上での関数型言語としてもっと注目されてもいいんじゃないかと思います.

さてF#は今までと違って、変数でも関数でもそれ以前に定義されたものしか使えません.なので、メインは順番で一番最後になります.プログラムは以下のようになります.

open System.IO
open System.Linq
open System.Xml
open System.Xml.Linq
open System.Text

let CreateXDocument(inputPath:string)= 
    let rs = XmlReaderSettings()
    rs.IgnoreProcessingInstructions <- true 
    rs.IgnoreComments <- true 
    rs.ConformanceLevel <- ConformanceLevel.Document 
    let xr = XmlReader.Create(inputPath, rs) in
    let xd = XDocument.Load(xr) 
    xd

let CreateXmlWriter(outputPath:string)=
    let settings = XmlWriterSettings()
    settings.CloseOutput <- true
    settings.ConformanceLevel <- ConformanceLevel.Document
    settings.Encoding <- Encoding.UTF8
    settings.Indent <- false
    settings.NewLineChars <- "\r\n"
    settings.NewLineHandling <- NewLineHandling.None
    settings.OmitXmlDeclaration <- false
    settings.WriteEndDocumentOnClose <- false
    let xw:XmlWriter = XmlWriter.Create(outputPath, settings)
    xw

let rec ProcessNode(node:XNode):XNode =
    match node with
    | :?XElement as elem -> let newElem = new XElement(elem.Name)
                            newElem.Add(elem.Attributes())
                            elem.Nodes() |> Seq.iter (fun node -> ProcessNode(node) |> newElem.Add )
                            newElem :> XNode
    | :?XText as text -> let newText = new XText(text.Value)
                         newText :> XNode
    | _ -> failwith "Unexpected node type"

let XmlCopy(xd:XDocument)=
    let root = xd.Root
    ProcessNode(root)

[<EntryPoint>]
let main argv = 
    let xd=CreateXDocument("MusicLibrary.xml")
    let xw=CreateXmlWriter("MusicLibrary_Copy.xml")
    let root:XElement = XmlCopy(xd) :?> XElement
    root.Save(xw)
    xw.Close()
    0 

XDocumentを作るところや、XmlWriterを作るところはVBと変わりません.違うところの核心点はProcessNode関数です.ProcessNode関数の定義にrecと書いてあるのは、再帰を表します.

VBではXNodeをたどって、そのNodeTypeでそれが何物であるかを判定していました.しかしF#ではmatch式で「型で判定」できてしまうのです.これはもう感涙ものです.:? は型テスト演算子と呼ばれます.値が指定された型に一致するとき真を返してくれます.

あとXElementの流れるような処理もF#ならではです.以下の箇所です.

    | :?XElement as elem -> (* 新しいXElementを作り*)
                            let newElem = new XElement(elem.Name)
                            (* それに属性を追加する *)
                            newElem.Add(elem.Attributes())
                            (* 子ノードを再帰的に処理して、XElementにAddする *)
                            elem.Nodes() |> Seq.iter (fun node -> ProcessNode(node) |> newElem.Add )
                            (* XElementを返す *)
                            newElem :> XNode

|> はパイプライン演算子と呼ばれます.XElementの子ノード群をSeq.iterに引渡し、Seq.iterに指定された関数に処理させます.
:> はキャスト演算子です.ProcessNodeはXNodeを返すことになっているので、XElementをXNodeにキャストして返します.

あとくだらないことですが、コメントを (* コメント *) で書くのも思わず昔を思い出してしまいました.私が一番最初に大学の研究室でやった高級言語Pascalだったのですが、この当時まだPascalのコメント { コメント } を書くことがそのマシンでは出来ず、(* コメント *) で代替していたのでした.何十年ぶりにこの形式のコメントに再会しました.

さてF#とかScalaはやりだすと面白くて間違いなくハマるのではないかと思います.次回はScalaでどう書くかを紹介したいと思います.