はじめに
おそらく、 F#に関する私のレビュー記事へのコメントの中で最もよく聞かれる(したがって書き込みの)2つの質問は次のとおりでした。
1.なぜOCamlに似ているのですか?
2.彼は何をあきらめましたか?
最初の質問への答えは特に難しくありません-それは完全にその基礎に基づいて作られているため、OCamlに非常に似ています。 それは良いですか悪いですか? はい、かなり良いです。これは、それがどれほど良いかまだ知られていない完全に新しい構文を考え出すよりも明らかに優れています。 さらに、OCamlには非常に多くのドキュメントがあるため、最初は(自己)学習に問題はないはずです。
2番目の質問は理解するのがはるかに難しく、特に現在は言語がベータ状態にあり、これまでのところあまりにも好奇心programmer盛なプログラマーの研究対象にすぎません。 しかし、彼とはかなり簡単な知り合いでしたが、完全に実用的な目標を達成するために一度使用したことがありました。これについては、この短い投稿で説明します。
もちろん、F#の課題を解決するように促した最後のことではなく、新しい言語で練習したいという希望を事前に予約します。 もちろん、プログラムはC#で作成することもでき、おそらくもう少し長かったでしょう(繰り返しますが、おそらくチェックしませんでした)。 いずれにせよ、プログラムは作成され、その役割を果たしました。
問題
私が働いている小さな会社は、国内の潜水艦のためのさまざまな情報支援システムの作成に携わっています。 注文は一回限りの、非常に具体的なものであるため、これまで製品開発に固有の問題に遭遇したことはありませんでした。 たとえば、ローカライズ。 しかし、ロシアの防衛産業の外国の顧客が、私たちのシステムをいくつかの輸出船に搭載したいという思いが非常に予想外だったことがたまたまありました。 「事前にそのような機会を予見すべきでしたか?」というトピックについては説明しません。まあ、それが必要だったとしましょうが、これはトピックには当てはまりません。
アプリケーションには、XAMLで記述されたさまざまな計算タスク、情報ウィンドウなどに専用の数百の多様なフォームが含まれており、多数のプロジェクトとその中のサブフォルダーに配置されている必要があります。 そして、恐ろしいことに、ロシアの線はそれらのすべての上に均等な層で寛大に散らばっていました。 (やがて判明したように、約1000行ありました)。 そして、これで何かをしなければなりませんでした。
解決策
まず、Microsoftが推進しているローカリゼーションテクノロジーをほぼ即座に放棄しました。一方で、それはかなり複雑であるためです(これらのサテライトアセンブリはすべてフォルダーに散らばっており、すべてのコンポーネントにIDを割り当てる必要があり、使用モデルはあまり明確ではありません)。 その一方で、その能力、主にリアルタイムで言語を切り替える能力は、この状況ではまったく役に立ちません。なぜなら、別の言語でコピーを1つだけ取得する必要があったためであり、ベトナムの船員は船上でロシア語の類似物を緊急に必要とすることはほとんどありません。
そのため、最終的には、すべてをはるかに単純にすることに決定しました。すべての行をResourceDictionaryに配置し、App.xamlにあるメイン辞書と組み合わせて、フォームにStaticResourceとして追加します。 だから 、一般的に。
F#のプログラムは、ロシア語の行を検索してすべてのxamlファイルを解析し、それらを変更し、辞書用に別のファイルも作成します。1時間以内に書き込み、コメントとパイプライン内の後続の各機能に対する情熱を100行未満で完了します少し彼女は、新しい行に書きました。 そして、すべてのファイルを1秒強で処理しました。 速度については、後で説明します。
最初は各メソッドを順番に伝えることを考えましたが、自分でコードを読みやすくするためにテキスト全体をレイアウトし、シートから機能コードを読み取ることがどれほど難しいかを判断しました。 ちなみに、FYは多くのことを考えたいが少し書くことを望む人に適しているという一般的な信念に反して、この特定のプログラムは私をあまり考えさせませんでした。 私たちの西洋の兄弟が率直に言っているように、つまり額の中で、すべてが起こりました。
一般的に、コードは次のとおりです。
#light
open System
open System.Xml
open System.IO
open System.Collections
let mutable i = 0 //
// xml ,
let rec nodes (node:XmlNode) =
seq { if (node.NodeType <> XmlNodeType.Comment) then yield node
if (node.Attributes <> null) then
for attr in node.Attributes do yield attr
for child in node.ChildNodes do yield! (nodes child)}
// XAML
let rec xamlFiles dir filter =
seq { yield! Directory.GetFiles(dir, filter)
for subdir in Directory.GetDirectories(dir) do yield! xamlFiles subdir filter}
//
let writeXml (doc:XmlDocument) (file:string) =
let xtw = new XmlTextWriter(file, null)
xtw.Formatting <- Formatting.Indented
doc.WriteContentTo(xtw)
xtw.Close()
//
let needLocalize (node:XmlNode) =
let isRussian = Seq.exists ( fun ch -> match ch with
| '' .. '' | '' .. '' -> true
|_ -> false)
node.Value <> null && isRussian node.Value
// , . (string*string) option
let localizeNode (node:XmlNode) =
if (needLocalize node) then
let oldValue = node.Value.Trim()
i <- i+1
let key = "Title_" + i.ToString()
let newValue = sprintf "{StaticResource %s}" key
match node.NodeType with
|XmlNodeType.Element -> (node :?> XmlElement).SetAttribute( "Content" , newValue)
node.Value <- null
|XmlNodeType.Text -> (node.ParentNode :?> XmlElement).SetAttribute(" Content" , newValue)
node.Value <- null
|_ -> node.Value <- newValue
Some(key, oldValue)
else None
// XAML. (, )
let localizeXaml (file:string) =
let doc = new XmlDocument()
doc.Load(file)
let rusDict = nodes doc
|> Seq.to_list
|> List.choose localizeNode //map, Some
File.Copy(file,file+".tmp",true)
writeXml doc file
rusDict
//
let addResource (doc:XmlDocument) (key, value) =
let elm = doc.CreateElement( "system" , "String" , "clr-namespace:System;assembly=mscorlib" )
elm.SetAttribute( "Key" , "http://schemas.microsoft.com/winfx/2006/xaml" ,key)|>ignore
elm.AppendChild(doc.CreateTextNode(value))|> ignore
doc.FirstChild.AppendChild(elm) |> ignore
// XAML
let localizeDirectory dir =
let dict = // , namespaces
let tmp = new XmlDocument()
let fst = tmp.CreateElement( "" , "ResourceDictionary" , "http://schemas.microsoft.com/winfx/2006/xaml/presentation" )
fst.SetAttribute( "xmlns:system" , "clr-namespace:System;assembly=mscorlib" )
fst.SetAttribute( "xmlns:x" , "http://schemas.microsoft.com/winfx/2006/xaml" )
tmp.AppendChild(fst) |> ignore
tmp
xamlFiles dir "*.xaml"
|> Seq.to_list
|> List.filter ( fun file -> int (File.GetAttributes(file) &&& FileAttributes.ReadOnly) = 0)
|> List.map ( fun x -> async {return localizeXaml x})
|> Async.Parallel
|> Async.Run
|> Array.to_list
|> List.iter ( fun lst -> List.iter (addResource dict) lst)
writeXml dict "dict.xml"
//
localizeDirectory Environment.CurrentDirectory
リストの初期化のテクノロジーを使用する2つの機能に注目する価値があると思います-xmlノードとファイル名について、レビュー記事で引用した例の1つです。 興味深いのは、いわゆるオプション値を返すLocalizeNode関数だと思います。 これはヌル値可能型に類似しており、値が返される場合は2つのバリアントSome(値)があり、値がない場合はNoneがあります。 このタイプはList.concat関数で使用されます。List.mapに似ていますが、オプションタイプ(この場合はstring * stringオプション)を返すマッピング関数を受け入れ、一部の値のみを最終リストに追加する点が異なります。 基本的に、List.mapにList.filter( fun i- > i <> None)を自動的に追加します。
さらに、メイン関数localizeDirectoryでは、すべてのファイルの処理がコンピューターで使用可能なすべてのカーネルに並列化されているため、コンピューターを100%読み込み、操作時間を大幅に短縮できることに注意してください。 このため、他のセマフォを備えたモニターは言うまでもなく、3つのジェスチャだけで十分であり、ThreadPoolはありません。
一方、このプログラムは、CLR(この場合はXmlDocument、XmlNode、およびSystem.Xmlの他のクラス)を積極的に使用するため、興味深い(そしてF#に固有の)ものです。 現時点では、他の関数型言語よりも優れているのがこの点です。
まあ、それは基本的にそれです。 もちろん良いニュースではないことを理解していますが、この簡単な例で、誰かがF#の見通しやその欠如について結論を出すことができるかもしれません。