F#は2002年からMicrosoft ResearchのDon Symeらによって開発されたマルチパラダイム言語です.Visual Studioに載ったのは2010からとまだ新しいです.私はまったくの初心者ですが、書いてみるとけっこう面白いです.Amazonで本を探してみると、F#はScalaよりはぐっと少ないですね.でもScalaはJavaプラットフォーム専門で.NETプラットフォームでの開発はすでに中止されてしまったそうですから、F#は.NET上での関数型言語としてもっと注目されてもいいんじゃないかと思います.
さてF#は今までと違って、変数でも関数でもそれ以前に定義されたものしか使えません.なので、メインは順番で一番最後になります.プログラムは以下のようになります.
open System.IO
open System.Linq
open System.Xml
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
:> はキャスト演算子です.ProcessNodeはXNodeを返すことになっているので、XElementをXNodeにキャストして返します.
あとくだらないことですが、コメントを (* コメント *) で書くのも思わず昔を思い出してしまいました.私が一番最初に大学の研究室でやった高級言語がPascalだったのですが、この当時まだPascalのコメント { コメント } を書くことがそのマシンでは出来ず、(* コメント *) で代替していたのでした.何十年ぶりにこの形式のコメントに再会しました.