2010-03-08 15 views
7

Attualmente sto imparando F # e la programmazione funzionale in generale (da uno sfondo C#) e ho una domanda sull'utilizzo degli oggetti .net CLR durante la mia elaborazione.Come si passano i valori degli oggetti .net attorno a F #?

Il modo migliore per descrivere il mio problema sarà quello di dare un esempio:

let xml = new XmlDocument() 
      |> fun doc -> doc.Load("report.xml"); doc 

let xsl = new XslCompiledTransform() 
      |> fun doc -> doc.Load("report.xsl"); doc 

let transformedXml = 
    new MemoryStream() 
     |> fun mem -> xsl.Transform(xml.CreateNavigator(), null, mem); mem 

Questo codice trasforma un documento XML con un documento XSLT utilizzando oggetti .NET. Nota XslCompiledTransform.Load funziona su un oggetto e restituisce void. Anche XslCompiledTransform.Transform richiede un oggetto memorystream e restituisce void.

La strategia di cui sopra è quella di aggiungere l'oggetto alla fine (il; mem) per restituire un valore e far funzionare la programmazione funzionale.

Quando vogliamo fare questo uno dopo l'altro abbiamo una funzione su ogni riga con un valore di ritorno alla fine:

let myFunc = 
    new XmlDocument("doc") 
    |> fun a -> a.Load("report.xml"); a 
    |> fun a -> a.AppendChild(new XmlElement("Happy")); a 

Esiste un modo più corretto (in termini di programmazione funzionale) per gestire .net oggetti e oggetti che sono stati creati in un altro ambiente OO?

Il modo in cui ho restituito il valore alla fine ha avuto inline le funzioni ovunque sembra un trucco e non il modo corretto per farlo.

Qualsiasi aiuto è molto apprezzato!

risposta

10

Uno dei grandi vantaggi di F # è che consente di mescolare lo stile di programmazione funzionale con altri stili (ovvero orientato agli oggetti e imperativo). Poiché la maggior parte delle librerie .NET sono orientate agli oggetti e imperative, il modo migliore per accedere alle funzionalità .NET da F # è semplicemente utilizzare le funzionalità imperative di F #. Ciò significa che quando si lavora con oggetti .NET, il codice F # idiomatico sarà simile a C#.

EDIT: il seguente esempio leggermente modificato mostra come eseguire il wrapping della trasformazione XSL in una funzione che prende il nome del file di input e un nome del file xsl. Restituisce il MemoryStream in cui l'uscita è stata scritta:

let transformDocument inputFile xslFile = 
    let doc = new XmlDocument() 
    doc.Load(inputFile) 
    let xsl = new XslCompiledTransform() 
    xsl.Load(xslFile) 

    let mem = new MemoryStream() 
    xsl.Transform(xml.CreateNavigator(), null, mem) 
    mem 

E il secondo esempio:

let doc = new XmlDocument("doc") 
doc.Load("report.xml") 
doc.AppendNode(new XmlElement("Happy")) 

Questo non significa che si sta girando via dallo stile funzionale, in alcun modo - ci sono molte opportunità di utilizzare lo stile funzionale quando si lavora con classi .NET. Ad esempio, è possibile utilizzare funzioni di ordine superiore come Seq.filter e Seq.map (o espressioni di sequenza) per elaborare raccolte di dati (o elementi XML). Puoi ancora scrivere astrazioni usando le funzioni di ordine superiore.

Lo spazio dei nomi System.Xml è molto importante, quindi non c'è molto spazio per lo stile funzionale. Tuttavia, il codice che genera i dati archiviati in XML può essere completamente funzionale. Può valere la pena guardare le classi da LINQ a XML (in .NET 3.5+), perché sono progettate in modo molto più funzionale alla programmazione (come dovrebbero funzionare bene con LINQ, che è anche funzionale).

+0

Grazie per l'ottima risposta :) Posso inserire il codice (ad esempio il secondo esempio che hai scritto) tutto in una singola funzione che restituisce l'oggetto doc? – Russell

+0

Sì, sicuramente! Questo è il modo migliore per nascondere i (brutti) imperativi bit dal resto del (simpatico) programma funzionale. Ho modificato la risposta (il primo esempio), in modo che mostri una semplice funzione per trasformare i file XML usando i file XSL. –

+0

Eccellente grazie per gli esempi dettagliati e le spiegazioni! :) – Russell

9

Per aggiungere un altro consiglio elegante alla risposta già eccellente di Tomas, è l'opportunità di ricorrere a queste funzioni per dare una buona indicazione dell'utilità di F #, anche utilizzando tecniche di codifica imperative quando richiesto.

L'esempio 1 Tomas usato è stato quello di trasformare un documento XML in un documento xsl:

let transformDocument inputFile xslFile = 
    let doc = new XmlDocument() 
    doc.Load(inputFile) 
    let xsl = new XslCompiledTransform() 
    xsl.Load(xslFile) 

    let mem = new MemoryStream() 
    xsl.Transform(xml.CreateNavigator(), null, mem) 
    mem 

Dopo aver letto this post, ho pensato a un modo per fare un passo avanti e ci permettono di curry questa funzione. Ciò significa (se si sceglie) possiamo passare la funzione solo un documento XSL, verrà restituito una funzione in grado di trasformare qualsiasi documento XML dato il documento XSL specificato:

let transform = 
    (fun xsl -> 
     let xsl_doc = new XslCompiledTransform() 
     xsl_doc.Load(string xsl) 

     (fun xml -> 
      let doc = new XmlDocument() 
      doc.Load(string xml) 
      let mem = new MemoryStream() 
      xsl_doc.Transform(doc.CreateNavigator(), null, mem) 
      mem 
     ) 
    ) 

Così abbiamo potuto effettuare le seguenti operazioni:

let transform_report_xsl = transform "report.xsl" 
let transform_style_xsl = transform "style.xsl" 

transform_report_xsl "report.xml" 
transform_report_xsl "report2.xml" 
transform_style_xsl "page.xml" 
+1

Questo è un ottimo esempio! In realtà, potresti anche specificare un solo argomento per la mia funzione originale (poiché le funzioni F # vengono elaborate automaticamente). Tuttavia, la tua versione è molto migliore: quando assegni un solo parametro, caricherà la trasformazione XSL, quindi l'elaborazione dei singoli file XML sarà più veloce. Questo trucco (o pattern :-)) è solitamente chiamato _pre-computation_. –

+0

Grazie per le informazioni :) Separa anche la funzionalità in più (forza il codice XSL in una funzione e il codice XML nell'altra) che lo rende più leggibile. – Russell

+0

Proprio oggi ho pensato a questa soluzione nel passare le funzioni in giro facendo javascript con jQuery. Restituire le definizioni delle funzioni può essere davvero molto utile! – Russell

Problemi correlati