2009-12-13 5 views
12

私はVisual Studio 2010でF#を試しています。私はC#やJavaなどのオブジェクト指向言語でより多くのコード/アーキテクチャ設計経験を持つ開発者です。F#のコーディングプラクティス

私のスキルセットを広げ、より良い決定を下すために、私はさまざまなことをするためにさまざまな言語を試しています。特に、関数型言語(この場合はF#)を使用してコーディングを「正しく」行うことができます。

単純な例では、いくつかのXMLを生成してから、いくつかのフィルタを追加していくつかの要素を削除しています。

ここ

が私のコードです:

open System 
open System.Xml.Linq 


let ppl:(string * string) list = [ 
    ("1", "Jerry"); 
    ("2", "Max"); 
    ("3", "Andrew"); 
] 

/// Generates a Person XML Element, given a tuple. 
let createPerson (id:string, name:string) = new XElement(XName.Get("Person"), 
               new XAttribute(XName.Get("ID"), id), 
               new XElement(XName.Get("Name"), name) 
) 

/// Filter People by having odd ID's 
let oddFilter = fun (id:string, name:string) -> (System.Int32.Parse(id) % 2).Equals(1) 

/// Open filter which will return all people 
let allFilter = fun (id:string, name:string) -> true 

/// Generates a People XML Element. 
let createPeople filter = new XElement(XName.Get("People"), 
           ppl |> List.filter(filter) |> List.map createPerson 
) 

/// First XML Object 
let XmlA = createPeople oddFilter 

/// Second XML Object 
let XmlB = createPeople allFilter 


printf "%A\n\n%A" XmlA XmlB 


/// Waits for a keypress 
let pauseKey = fun() -> System.Console.ReadKey() |> ignore 


pauseKey() 

私の質問は以下のとおりです。物事は、私は、このシナリオでうまくやっていますか?どの部分をより良くすることができるでしょうか?

私は実際にいくつかのアイデアを楽しみにしており、機能的なパラダイムにも慣れ親しむことに非常に興奮しています! :)事前に

おかげで

+0

+1ここと全く同じ状況ですが(明らかに異なるコードを使用しています)、これに対する回答を見ることができます。 – Jay

+1

F#は基本的にはタイプを表すように作られていますので、非常に強力な推論エンジンを持っていますので、タイプアノテーションを省略し、できるだけ汎用的な関数にする必要があります。 – 0xFF

+0

@martain_net実際にはたくさんの意味があります:)異なるネイティブのF#タイプ(例えば(string * string)リストは2つの文字列を持つタプルのリストを意味します)を理解するために型定義を過ぎてしまったと思います。 – Russell

答えて

13

原則として、あなたのコードは大丈夫です。

構文上の観点から単純化できる点がいくつかあります。

let ppl:(string * string) list = [ 
    ("1", "Jerry"); 
    ("2", "Max"); 
    ("3", "Andrew"); 
] 

コンパイラは自分でほとんどの種類を推定することができます:

let ppl = [ "1", "Jerry"; 
      "2", "Max"; 
      "3", "Andrew" ] 

そしてもちろん、あなたが原因curryingにこのようなあなたのフィルタを再度書き込むことができます。

let oddFilter (id:string, name:string) = (int id) % 2 = 1 
let allFilter (id:string, name:string) = true 

最大改善は名前からインデックスを分離し、プログラムに番号を付けることになります。あなたは、文字列の代わりに数字で作業する必要はありませんし、より多くの慣用的なタプルフリー機能を使用することができます。

let ppl = [ "Jerry"; "Max"; "Andrew" ] 

let oddFilter id name = id % 2 = 1 
let allFilter id name = true 

let createPerson id name = ... 

一部

ppl |> List.filter(filter) |> List.map createPerson 

[ for (index, name) in List.mapi (fun i x -> (i, x)) do 
     if filter index name then 
      yield createPerson (string index) name ] 
+2

うん、型推論とカレー化。良いもの。 – cfern

+0

ありがとうDario、良いアイディアがいくつかあります。 ppl |> List.filter(filter)|> List.map createPersonをなぜ書き直すのか尋ねればいいですか?私は少し処理が混乱しています。私が探しているいくつかのトピックがあるように見えます。:) Thx again – Russell

+0

ポイントは本当に必要なインデックスを扱うことだけです。だから私はそれらを生成する* inside * 'createPeople'。 – Dario

4
let createPeople filter = new XElement(XName.Get("People"), 
          ppl |> List.filter(filter) |> List.map createPerson 
) 

この部分は手動でdeforestedすることができ、またはあなたが望むことができるデフォレストそれコンパイラがすることをあなたのために。

基本的には、中間的な構造(フィルタリングされた人物のリスト)があり、これは単純にコンパイルされた場合、一度だけ配信されるように割り当てられます。それぞれの要素にcreatePersonを適用するほうが良い。それが出入りするかどうかが決定され、最終結果を直接構築する。

EDIT:

let createPeople filter = 
    new XElement(
    XName.Get("People"), 
    List.foldBack 
     (fun P acc -> if filter P then (createPerson P)::acc else acc) 
     ppl 
     []) 

注: cfernはcreatePeopleのこの伐採バージョンに貢献filtercreatePersonに副作用があるかもしれないので、コンパイラはによりデフォレストに決定するために、F#で、それはかなり難しいです自体。この場合、たとえfilterに副作用があっても、createPersonは専門家ではないので、伐採防止は正しいと思われます。

+2

私はこれを今はテストできませんが、中間リストの作成を避けるために、Seq.filterとSeq.mapでList.filterとList.mapを置き換えることができますか? – cfern

+0

@cfernまだ 'filter'の後に' map'がある場合でも、割り付けられ、一度使用され、次に破棄される中間構造があります。私は、 'createPerson'を適用する単一の' fold 'によってそれらを置き換え、 'filter'テストが肯定的である場合にのみ、結果をアキュムレータに追加するという行に沿ってもっと考えていました。 –

+1

ああ、そうだ。私はまだ関数型プログラミングに新しいので、折りたたみにはまだ完全には使用されていません。これはあなたが考えていることですか? ... createPeople filter = new XElement(XName.Get( "People")、List.foldBack(fun P acc - >フィルタPならば(createPerson P):: acc else acc)ppl [])...ここで、私はfoldBackを使って人の秩序を保ちました。 – cfern

3

ほとんどに書き換えられます具体的な理由、一般的なパフォーマンスなしで、森林破壊の時間は、悪い考えです。これらのうちどれが読みやすく、エラーが起こりにくいと思いますか?コンテキストから取り除かれると、複雑さやコードへの結合が増えます。

let createPeople filter ppl = 
    ppl 
    |> List.mapi (fun i x -> (i, x)) 
    |> List.filter filter 
    |> List.map createPerson 

let createPeople filter ppl = 
    [ for (index, name) in ppl |> List.mapi (fun i x -> (i, x)) do 
      if filter (index, name) then 
       yield createPerson (index, string) ] 

let createPeople filter ppl = 
    (ppl |> List.mapi (fun i x -> (i, x)), []) 
    ||> List.foldBack (fun P acc -> if filter P then (createPerson P)::acc else acc) 

構文関数に慣れると、pplを削除できます。

let createPeople filter = 
    List.mapi (fun i x -> (i, x)) 
    >> List.filter filter 
    >> List.map createPerson 

これらはすべてタプルデータを使用します。

let filter (id, name) = 
    id % 2 = 1 

let allFilter (id, name) = 
    true 

let createPerson (id, name) = 
    () 
+0

gradbotに感謝します。好奇心のために、Fは何を意味するのですか?これは配管オペレータですか?>のようですか?それとも、ビット単位の演算子(ビットを右に移動する)ですか? – Russell

+0

2つの関数を結合し、左の関数が最初に呼び出されます。 let(>>)f g x = g(f x)として定義される。 – TimothyP

0

最近、XSLをXMLファイルに変換する必要もありました。 これは私がこれまで使っていたF#です。

.netメソッドを使用するといくつか興味深い欠点があります。

(* Transforms an XML document given an XSLT. *) 

open System.IO 
open System.Text 
open System.Xml 
open System.Xml.Xsl 

let path = @"C:\\XSL\\" 

let file_xml = path + "test.xml" 
let file_xsl = path + "xml-to-xhtml.xsl" 

(* Compile XSL file to allow transforms *) 
let compile_xsl (xsl_file:string) = new XslCompiledTransform() |> (fun compiled -> compiled.Load(xsl_file); compiled) 
let load_xml (xml_file:string) = new XmlDocument() |> (fun doc -> doc.Load(xml_file); doc) 

(* Transform an Xml document given an XSL (compiled *) 
let transform (xsl_file:string) (xml_file:string) = 
     new MemoryStream() 
     |> (fun mem -> (compile_xsl xsl_file).Transform((load_xml xml_file), new XmlTextWriter(mem, Encoding.UTF8)); mem) 
     |> (fun mem -> mem.Position <- (int64)0; mem.ToArray()) 

(* Return an Xml fo document that has been transformed *) 
transform file_xsl file_xml 
    |> (fun bytes -> File.WriteAllBytes(path + "out.html", bytes))