2015-05-11 12 views
6

Al fine di calcolare l'area di quadrato e cerchio, ho definito il seguente tipo:modo funzionale di scrivere questi metodi in F #

type Square = {width: float; length: float;} with 
    member this.area = this.width * this.length 
    member this.perimeter = (this.width + this.length) * 2. 

type Circle = {r:float} with 
    member this.area = System.Math.PI * this.r * this.r 
    member this.perimeter = 2. * System.Math.PI * this.r 

let s1 = {width = 3.; length = 4.} 
let c1 = {r = 8.3} 

printfn "%A" s1 
printfn "The area of s1 is: %A" s1.area 
printfn "The perimeter of s1 is: %A" s1.perimeter 


printfn "%A" c1 
printfn "The area of c1 is: %A" c1.area 
printfn "The perimeter of c1 is: %A" c1.perimeter 

Quando ho letto questo articolo: http://fsharpforfunandprofit.com/posts/type-extensions/

Essa afferma:

  • metodi non giocare bene con inferenza di tipo
  • metodi non giocare bene con highe R ORDINANZA funzioni

Quindi, un appello per chi è nuovo al punto di vista funzionale la programmazione. Non utilizzare affatto i metodi se possibile, specialmente quando si sta imparando. Sono una stampella che ti impedirà di trarre il massimo vantaggio dalla programmazione funzionale .

Allora qual è il modo funzionale per risolvere questo problema? o qual è il modo di F # idomatic?


Edit:

Dopo aver letto le "F # Component Design Guidelines" (inchino a @VB), e @ commento di JacquesB, ritengo che implementano il metodo membro all'interno del tipo è il più semplice, modo intrinseco:

type Square2 (width: float, length: float) = 
    member this.area = width * length 
    member this.perimeter = (width + length) * 2. 

(questo è quasi identica alla mia Square tipo originale - questo Square2 salva solo seveal this. prefisso a 01.231., this.length.)

Ancora una volta, il The F# Component Design Guidelines è abbastanza utile.

+1

Più funzionale non è necessariamente più F # idiomatico! F # è un linguaggio oo/funzionale, e l'uso appropriato dei metodi va bene. La citazione riguarda davvero * apprendimento * programmazione funzionale quando già conosci oo, e in tal caso cercare di essere il più funzionale possibile è un buon esercizio di apprendimento. – JacquesB

+1

Per quanto riguarda le due risposte attuali e l'appello contro i "metodi", usa un'unione discriminata quando conosci tutti i tuoi casi in anticipo. Questo è più funzionale e mappa veramente meglio il tuo dominio - queste N opzioni sono i miei unici casi - e imparerai più F # da esso. Usa un'interfaccia (e metodi) quando vuoi essere in grado di definire nuove implementazioni ad-hoc attraverso il tuo programma. –

risposta

8

Un modo più funzionale per fare ciò sarebbe creare un unione discriminata Shape, dove Square e Circle sarebbero i suoi casi. Quindi creare funzioni area e perimeter, prendendo Shape e l'utilizzo di pattern matching:

type Shape = 
    | Square of Width: float * Length: float 
    | Circle of R: float 

let area = function 
    | Square (width, length) -> width * length 
    | Circle r -> System.Math.PI * r * r 

let perimeter = function 
    | Square (width, length) -> (width + length) * 2. 
    | Circle r -> 2. * System.Math.PI * r 

let s1 = Square(Width = 3., Length = 4.) 
let c1 = Circle(R = 8.3) 

printfn "%A" s1 
printfn "The area of s1 is: %A" (area s1) 
printfn "The perimeter of s1 is: %A" (perimeter s1) 


printfn "%A" c1 
printfn "The area of c1 is: %A" (area c1) 
printfn "The perimeter of c1 is: %A" (perimeter c1) 
+3

Perché è più funzionale? Si prega di elaborare. Entrambi gli approcci mancano di effetti collaterali. – rightfold

+0

@rightfold Si può solo assumere l'intento della domanda, dato che la sua implementazione manca anche di effetti collaterali, è "come si scriverebbe usando le caratteristiche più comunemente presenti nei linguaggi funzionali rispetto alle lingue OO", che sembra essere una definizione accettata di " funzionale "in questi giorni. (Proprio come non-letteralmente è una definizione accettata di letteralmente. Non sparare al messaggero.) –

+1

@rightfold Questo è già spiegato nella domanda. Per esempio 'let f x = x.area' non verrà compilato, mentre' let f x = area x' lo farà. – svick

1

C'è un modo più funzionale che @svick descrive bene, ma prendere in considerazione anche "The F# Component Design Guidelines"

  • fare proprietà d'uso e metodi per operazioni intrinseche ai tipi.

Questo è chiamato in modo specifico perché alcune persone da un funzionale sfondo programmazione evitano l'uso di programmazione orientata agli oggetti insieme, preferendo un modulo che contiene un insieme di funzioni che definiscono le funzioni intrinseche legate a un tipo (ad esempio, lunghezza pippo invece di foo.Length). Ma vedi anche il prossimo proiettile.In generale, in F #, l'uso di di programmazione orientata agli oggetti è preferito come dispositivo di ingegneria del software . Questa strategia fornisce anche alcuni vantaggi degli strumenti come la funzione "Intellisense" di Visual Studio per scoprire i metodi su un tipo "puntando" su un oggetto.

  • considerare l'utilizzo di tipi di interfaccia per rappresentare gruppi correlati di operazioni che possono essere implementate in più modi.

In F # ci sono un certo numero di modi per rappresentare un dizionario di operazioni, come l'utilizzo di tuple funzioni o registrazioni di funzioni. In generale, si consiglia di utilizzare i tipi di interfaccia per questo scopo.

Quindi, in base a queste linee guida, un'interfaccia IShape con Area e Perimeter membri è un metodo consigliato per un componente # F, nonostante ci sia un modo "più funzionale" in generale.

+0

Non penso che questi due proiettili implichino la necessità di un'interfaccia. Entrambe possono essere eseguite estensioni di tipo in un modo più funzionale. In realtà, il secondo punto è che non si fanno registrazioni di funzioni, che possono essere difficili da capire. –

+0

@DaxFohl quando si confronta con la risposta di svick, IShape è ciò che io intendo come un "gruppo di operazioni correlate che può essere implementato in più modi" - Area e Perimetro. La corrispondenza del modello con una funzione separata per ogni caso non è così lontana da un record di funzioni. –

+0

Si noti inoltre che queste sono le linee guida di progettazione * componente *, ovvero descrive come dovrebbe essere la superficie pubblica delle librerie, non influisce sull'implementazione. – svick

Problemi correlati