2. Visual F# .NET
Transkrypt
2. Visual F# .NET
Język F# Języki Programowania na Platformie .NET (część 2) Tomasz Goluch http://www.kaims.pl/∼goluch/ [email protected] Słowa kluczowe języka abstract, and, as, assert, base, begin, class, default, delegate, do, done, downcast, downto, elif, else, end, exception, extern, false, finally, for, fun, function, global, if, in, inherit, inline, interface, internal, lazy, let, let!, match, member, module, mutable, namespace, new, not, null, of, open, or, override, private, public, rec, return, return!, select, static, struct, then, to, true, try, type, upcast, use, use!, val, void, when, while, with, yield, yield! Tomasz Goluch Język F# Składnia Język F#posiada dwie składnie: verbose rzadziej stosowana, mniej wrażliwa na wcięcia. light krótsza, mniej tokenów (np. nie są wymagane: done, in i ;), zamiast bloków: begin – end, class – end, struct – end, interface – end, with – end stosowane są wcięcia, zabronione są znaki tabulacji. Tomasz Goluch Język F# Składnia Różnica: #light let add a b c = let ab = a + b printfn "%d" ab c - ab let add a b c = let ab = a + b in //’in’ specyfikuje zakres wiązania ab printfn "%d" ab; //’;’ rozdziela wyrażenia c - ab;; //’;;’ koniec funkcji Tomasz Goluch Język F# Dyrektywy preprocesora #if #else #endif (Dyrektywy kompilacji warunkowej) – inne zachowanie niż w typowych językach. Przykładowo, nie można stosować wyrażeń logicznych zawierających symbole. Słowa true i false nie mają specjalnego znaczenia. Nie ma dyrektywy #define należy użyć opcji kompilatora bądź ustawień projektu do definiowania symboli. Tomasz Goluch Język F# Dyrektywy preprocesora #[line] int #[line] int string #[line] int verbatim-string (dyrektywy wiersza) – Jeżeli kod F#jest generowany na podstawie innych źródeł to można przekazać informacje o numerach i nazwach plików z których został on wygenerowany w celu łatwiejszego raportowanie błędów. # 25 #line 25 #line 25 "C:\\Projects\\MyProject\\MyProject\\Script1" #line 25 @"C:\Projects\MyProject\MyProject\Script1" # 25 @"C:\Projects\MyProject\MyProject\Script1" Tomasz Goluch Język F# Dyrektywy kompilatora #light ["on"|"off"] #indent ["on"|"off"] – Włącza/wyłącza składnię lekką light dla zgodności z innymi wersjami ML (ang. Meta Language). Dyrektywy #light i #indent są wymienne i mają taką samą składnię i skutek. Domyślnie składnia lekka jest włączona. Składnia verbose zawsze jest dostępna. Tomasz Goluch Język F# Wartości Wartości w F#mają przyjmować typ liczb całkowitych lub zmiennoprzecinkowych, znaków, tekstu, list, sekwencji, tablic, krotek, unii, rekordów, klas typów i wartości funkcji. Do wiązania identyfikatora wartości z jej definicją służy słowo kluczowe let: let a = 1 let b = 100u let str = "text" // A function value binding. let f x = x + 1 Tomasz Goluch Język F# Wartości Dlaczego „immutable” ? Domyślnie wszystkie wartości w F#nie mogą ulec zmianie w trakcie wykonywania programu. Takie zmienne są łatwe do zarządzania w środowisku wielowątkowym. Nie ma problemu z dylematem czy zmienna może ulec zmianie jeśli została przekazana do innej funkcji. Funkcje zachowują się jak typowe funkcje matematyczne. Pozwala to na bardziej restrykcyjny tryb pracy kompilatorów i lepszy stopień optymalizacji, a programistom na łatwiejsze zrozumienie i pisanie poprawnego kodu. Kod języków funkcyjnych jest prostszy do debuggowania. Tomasz Goluch Język F# Zmienne F# jednak nie jest czystym językiem programowania. Słowo kluczowe mutable pozwala na definicję zmiennej. Przeważnie mają one ograniczony zakres jako pole typu albo zmienna lokalna, co pozwala na łatwiejszą kontrolę i mniejsze prawdopodobieństwo zmiany wartości w niespodziewany sposób. Przypisanie wartości początkowej wygląda identycznie a do zmiany wartości służy operator przypisania: <- : let mutable x = 1 x <- x + 1 Tomasz Goluch Język F# Słowo kluczowe let Słowo kluczowe let służy do wiązania identyfikatora z wartością lub funkcją: // Wiązanie wartości: let identifier-or-pattern [: type] = expression body-expression // Wiązanie funkcji: let identifier parameter-list [: return-type ] = expression body-expression Tomasz Goluch Język F# Słowo kluczowe let Wiązanie większej liczby identyfikatorów: let i, j, k = (1, 2, 3) Wiązane identyfikatory dostępne są dopiero po definicji, nie można ich użyć w kodzie wcześniej: // Error: printfn "%d" x let x = 100 // OK: printfn "%d" x Tomasz Goluch Język F# Słowo kluczowe let Wiązanie funkcji: let function1 a = a + 1 let function2 (a, b) = a + b let result = let function3 (a, b) = a + b 100 * function3 (1, 2) Tomasz Goluch Język F# Słowo kluczowe let Przypisywanie typu: let function1 (a: int) : int = a + 1 Tomasz Goluch Język F# Funkcje Definicja funkcji: // Bez rekurencji let [inline] function-name parameter-list [ : return-type ] = function-body // Rekurencyjna let rec function-name parameter-list = recursive-function-body Tomasz Goluch Język F# Operatory arytmetyczne + - * / % ** (operatory binarne) dodawanie, odejmowanie, mnożenie, dzielenie, dzielenie modulo i potęgowanie. Dzielenie dla typów całkowitych rzuca wyjątek DivideByZeroException a dla zmiennoprzecinkowych specjalne wartości (+/-)Infinity. Dla wyniku dzielenia modulo znak jest taki sam jak znak pierwszego operandu. + - (operatory unarne) wartość dodatnia, ujemna. Tomasz Goluch Język F# Operatory arytmetyczne + - * / % ** (operatory binarne) dodawanie, odejmowanie, mnożenie, dzielenie, dzielenie modulo i potęgowanie. Dzielenie dla typów całkowitych rzuca wyjątek DivideByZeroException a dla zmiennoprzecinkowych specjalne wartości (+/-)Infinity. Dla wyniku dzielenia modulo znak jest taki sam jak znak pierwszego operandu. + - (operatory unarne) wartość dodatnia, ujemna. = > < >= <= <> (binarne operatory porównania) równe, większe niż, mniejsze niż, większe lub równe, mniejsze lub równe, nierówne. Tomasz Goluch Język F# Operatory arytmetyczne + - * / % ** (operatory binarne) dodawanie, odejmowanie, mnożenie, dzielenie, dzielenie modulo i potęgowanie. Dzielenie dla typów całkowitych rzuca wyjątek DivideByZeroException a dla zmiennoprzecinkowych specjalne wartości (+/-)Infinity. Dla wyniku dzielenia modulo znak jest taki sam jak znak pierwszego operandu. + - (operatory unarne) wartość dodatnia, ujemna. = > < >= <= <> (binarne operatory porównania) równe, większe niż, mniejsze niż, większe lub równe, mniejsze lub równe, nierówne. Tomasz Goluch Język F# Operatory logiczne && || ^^^ (binarne operatory logiczne) koniunkcja (iloczyn logiczny), alternatywa (suma logiczna). Wartości składowych wyrażenia logicznego są wyliczane jedynie gdy wpływają na wynik (short-circuit evaluation). Drugie wyrażenie nie jest wyliczane jeśli (dla koniunkcji) pierwsze jest fałszywe a (dla alternatywy) prawdziwe. not (unarny operator logiczny) negacja. Tomasz Goluch Język F# Operatory logiczne && || ^^^ (binarne operatory logiczne) koniunkcja (iloczyn logiczny), alternatywa (suma logiczna). Wartości składowych wyrażenia logicznego są wyliczane jedynie gdy wpływają na wynik (short-circuit evaluation). Drugie wyrażenie nie jest wyliczane jeśli (dla koniunkcji) pierwsze jest fałszywe a (dla alternatywy) prawdziwe. not (unarny operator logiczny) negacja. Tomasz Goluch Język F# Operatory bitowe &&& ||| ^^^ <<< >>> (binarne operatory bitowe) koniunkcja (iloczyn logiczny), alternatywa (suma logiczna), alternatywa wykluczająca (różnica symetryczna lub suma modulo 2), przesunięcie bitowe w lewo i prawo. Drugi operator przesunięcia bitowego jest typu int32. ~~~ (unarny operator bitowy) negacja. Tomasz Goluch Język F# Operatory bitowe &&& ||| ^^^ <<< >>> (binarne operatory bitowe) koniunkcja (iloczyn logiczny), alternatywa (suma logiczna), alternatywa wykluczająca (różnica symetryczna lub suma modulo 2), przesunięcie bitowe w lewo i prawo. Drugi operator przesunięcia bitowego jest typu int32. ~~~ (unarny operator bitowy) negacja. Tomasz Goluch Język F# Przeciążanie operatorów // Przeciążenie operatora klasy lub rekordu. static member (operator-symbols) (parameter-list) = method-body // Przeciążenie operatora globalnego let [inline] (operator-symbols) parameter-list = function-body Przeciążając operatory unarne należy zaznaczyć ten fakt przy pomocy tyldy: static member (~-) (v : Vector) Tomasz Goluch Język F# Przeciążanie operatorów type Vector(x: float, y : float) = member this.x = x member this.y = y static member (~-) (v : Vector) = Vector(-1.0 * v.x, -1.0 * v.y) static member (*) (v : Vector, a) = Vector(a * v.x, a * v.y) static member (*) (a, v: Vector) = Vector(a * v.x, a * v.y) let v1 = Vector(1.0, 2.0) let v2 = v1 * 2.0 let v3 = 2.0 * v1 let v4 = - v2 Tomasz Goluch Język F# Typy nullable – ? Można konwertować typu nullable do „zwykłych” przy pomocy operatorów takich jak: int, float... Możliwa jest również konwersja pomiędzy różnymi typami nullable przy pomocy prefiksu Nullable z przestrzeni nazw Microsoft.FSharp.Linq. open Microsoft.Fsharp.Linq let nullableInt = new System.Nullable<int>(10) // Konwersja na inny typ nullable let nullableFloat = Nullable.float nullableInt // Konwersja na typ zwykły printfn "%f" (float nullableFloat) Wynik: 10.000000. Tomasz Goluch Język F# Operator nullable – ? Pozwala na pracę innych operatorów z wartościami null. ?>= ?> ?<= ?< ?= ?<> ?+ ?- ?* ?/ ?% (dopuszczalne wartości null po lewej stronie) ?>= >? <=? <? =? <>? +? -? *? /? %? (dopuszczalne wartości null po prawej stronie) Tomasz Goluch Język F# Operator nullable – ? Pozwala na pracę innych operatorów z wartościami null. ?>= ?> ?<= ?< ?= ?<> ?+ ?- ?* ?/ ?% (dopuszczalne wartości null po lewej stronie) ?>= >? <=? <? =? <>? +? -? *? /? %? (dopuszczalne wartości null po prawej stronie) ?>= ?>? ?<=? ?<? ?=? ?<>? ?+? ?-? ?*? ?/? ?%? (dopuszczalne wartości null po obydwu stronach) Tomasz Goluch Język F# Operator nullable – ? Pozwala na pracę innych operatorów z wartościami null. ?>= ?> ?<= ?< ?= ?<> ?+ ?- ?* ?/ ?% (dopuszczalne wartości null po lewej stronie) ?>= >? <=? <? =? <>? +? -? *? /? %? (dopuszczalne wartości null po prawej stronie) ?>= ?>? ?<=? ?<? ?=? ?<>? ?+? ?-? ?*? ?/? ?%? (dopuszczalne wartości null po obydwu stronach) Tomasz Goluch Język F# Przykłady Tomasz Goluch Język F# Przykłady Liczby całkowite i podstawowe funkcje: let integer = 176 // Prosta funkcja wykonująca działania arytmrtyczne let integer2 = (integer/4 + 5 - 7) * 4 // Lista liczb z przedziału od 0 do 99 let numbers = [ 0 .. 99 ] // Lista krotek zawierających liczby od 0 to 99 // oraz ich kwadraty let tableOfSquares = [ for i in 0 .. 99 -> (i, i*i) ] // Drukowanie zawartości listy (Generic Pretty-Printing %A) printfn "Squares from 0 to 99 are:\n%A" tableOfSquares Tomasz Goluch Język F# Przykłady Liczby całkowite i podstawowe funkcje: // Funkcja przyjmująca i zwracająca liczbę całkowitą let func1 (x) = x*x + 3 // Nawiasy dla argumentów funkcji są opcjonalne let func1 x = x*x + 3 // Wywołanie funkcji z przypisaniem wyniku do zmiennej // Typ zmiennej wynika z typu zwracanego przez funkcję let result1 = func1 4573 printfn "Square of 4573 + 3 is %d" result1 Tomasz Goluch Język F# Przykłady Liczby całkowite i podstawowe funkcje: // Opisanie typ parametru za pomocą:’(argument:typ)’ let func2 (x:int) = 2*x*x - x/5 + 3 let result2 = func2 (7 + 4) // 243 printfn "The result of func2 (7 + 4) is %d" result2 let func3 x = if x < 100.0 then 2.0*x*x - x/5.0 + 3.0 else 2.0*x*x + x/5.0 - 37.0 let result3 = func3 (6.5 + 4.5) // 242.800000 printfn "The result of func3 (6.5 + 4.5) is %f" result3 Tomasz Goluch Język F# Przykłady Wartości logiczne let boolean1 = true let boolean2 = false let boolean3 = not boolean1 && (boolean2 || false) printfn "The expression value is %b" boolean3 // The expression value is false Tomasz Goluch Język F# Przykłady Napisy let string1 = "Hello" let string2 = "world" // Znak @ ignoruje kody specjalne (literał dosłowny) let string3 = @"c:\Program Files\" // Użycie potójnego cytatu let string4 = """He said "hello world" after you did""" // Połączenie dwóch napisów ze spacą pomiędzy let helloWorld = string1 + " " + string2 printfn "%s" helloWorld //Hello world // Napis składjący się z pierwszych 7 znaków let substring = helloWorld.[0..6] printfn "%s" substring //Hello w Tomasz Goluch Język F# Przykłady Krotki (Tuples) // Krotka liczb całkowitych let tuple1 = (1, 2, 3) // Funkcja zamieniająca kolejność dwóch elementów w krotce // Jak pokazuje QuickInfo, jest to funkcja ogólna let swapElems (a, b) = (b, a) printfn "Swapping elements 1 i 2 = %A" (swapElems (1,2)) // Swapping elements 1 i 2 = (2, 1) // Krotka składająca się z liczby całkowitej, // napisu oraz liczby zmiennorzecinkowej podwójnej precyzji let tuple2 = (1, "fred", 3.1415) printfn "tuple1: %A // tuple1: (1, 2, 3) tuple2: %A" tuple1 tuple2 tuple2: (1, "fred", 3.1415) Tomasz Goluch Język F# Przykłady Listy let list1 = [ ] // Pusta lista let list2 = [ 1; 2; 3 ] // Lista 3 elementów // Nowa lista z ’42’ dodanym na początku let list3 = 42 :: list2 // Lista liczb całkowitch od 1 do 1000 let numberList = [ 1 .. 1000 ] Tomasz Goluch Język F# Przykłady Listy // Lista zawierająca wszystkie dni roku let daysList = [ for month in 1 .. 12 do for day in 1 .. DateTime.DaysInMonth(2016, month) do yield DateTime(2016, month, day) ] // Lista zawierająca krotki reprezentujące // Współrzędne czarnych pól szachownicy let blackSquares = [ for i in 0 .. 7 do for j in 0 .. 7 do if (i+j) % 2 = 1 then yield (i, j) ] Tomasz Goluch Język F# Przykłady Przetwarzanie list // Podnosi liczby w liście do kwardatu, // przekazując argument do List.map // przy pomocy operatora pipeline: |> let squares = numberList |> List.map (fun x -> x*x) // Oblicza sumę kwadratów liczb podzielnych przez 3 let sumOfSquaresUpTo n = numberList |> List.filter (fun x -> x % 3 = 0) |> List.sumBy (fun x -> x * x) Tomasz Goluch Język F# Przykłady Klasy // Konstruktor klasy przyjmuje dwa argumenty: dx i dy, // obydwa typu zmiennoprzecinkowego type Vector2D(dx : float, dy : float) = // Długość wektora obliczana jest podczas tworzenia obiektu let length = sqrt (dx*dx + dy*dy) // ’this’ określa nazwę identyfikatora dla samego siebie // W metodach instancji, musi poprzedzać nazwy składowych member this.DX = dx member this.DY = dy member this.Length = length member this.Scale(k) = Vector2D(k * this.DX, k * this.DY) Tomasz Goluch Język F# Przykłady Klasy // Instancja klasy Vector2D let vector1 = Vector2D(3.0, 4.0) // Pobranie nowego obieku przeskalowanego wektora, // bez modyfikowania oryginalnego obiektu let vector2 = vector1.Scale(10.0) printfn "Length of vector1: %f Length of vector2: %f" vector1.Length vector2.Length // Length of vector1: 5.000000 Length of vector2: 50.000000 Tomasz Goluch Język F# Przykłady Klasy generyczne // ’T jest parametrem typu dla klasy type StateTracker<’T>(initialElement: ’T) = // Stany przechowywane w tablicy let mutable states = [ initialElement ] // Dodanie nowego elementu do listy stanów member this.UpdateState newState = // Użycie operatora <- w celu zmiany wartości states <- newState :: states // Pobranie całej historii stanów member this.History = states // Pobranie najnowszego stanu member this.Current = states.Head Tomasz Goluch Język F# Przykłady Klasy generyczne // Instancja typu ’int’ klasy StateTracker // Parametr typu został wywnioskowany przez kompilator let tracker = StateTracker 10 // Dodanie stanu tracker.UpdateState 17 Tomasz Goluch Język F# Przykłady Implementacja interfejsów // Typ implementujący IDisposable type ReadFile() = let file = new System.IO.StreamReader("readme.txt") member this.ReadLine() = file.ReadLine() // Implementacja elementu interfejsu IDisposable interface System.IDisposable with member this.Dispose() = file.Close() Tomasz Goluch Język F# Przykłady Tablice let array1 = [| |] // Pusta tablica let array2 = [| "hello"; "world"; "and"; "hello"; "world"; "again" |] let array3 = [| 1 .. 1000 |] // Tablica zawiera jedynie słowa "hello" i "world" let array4 = [| for word in array2 do if word.Contains("l") then yield word |] // Modyfikacja elementu tablicy operatorem przypisania: <array2.[1] <- "WORLD!" // Oblicza sumę długości słów rozpoczynających się od ’h’ let sumOfLengthsOfWords = array2 |> Array.filter (fun x -> x.StartsWith "h") |> Array.sumBy (fun x -> x.Length) Tomasz Goluch Język F# Przykłady Tablice // // // // for word in array4 do printfn "word: %s" word word: hello word: world word: hello word: world // Tablica zainicjowana poprzez indeks, // zawiera parzyste liczby od 0 do 2000 let evenNumbers = Array.init 1001 (fun n -> n * 2) // Wycinek tablicy uzyskany przy użyciu "slicing notation" let evenNumbersSlice = evenNumbers.[0..500] Tomasz Goluch Język F# Przykłady Sekwencje są wyliczane na żądanie oraz wyliczane ponownie przy każdej iteracji. W F# sekwencja jest instancją System.Collections.Generic.IEnumerable <’T>, co pozwala na stosowanie funkcji Seq do list i tablic. // Pusta sekwencja let seq1 = Seq.empty let seq2 = seq { yield "hello"; yield "world"; yield "and"; yield "hello"; yield "world"; yield "again" } let numbersSeq = seq { 1 .. 1000 } // Kolejna tablica zawierająca jedynie słowa "hello" i "world" let seq3 = seq { for word in seq2 do if word.Contains("l") then yield word } Tomasz Goluch Język F# Przykłady Sekwencje let evenNumbers = Seq.init 1001 (fun n -> n * 2) let rnd = System.Random() // Nieskończony ciąg (losowy spacer) // Używaj yield! w celu zwrócenia każdego elementu podsekwencji, // podobnie jak w IEnumerable.SelectMany let rec randomWalk x = seq { yield x yield! randomWalk (x + rnd.NextDouble() - 0.5) } let first100ValuesOfRandomWalk = randomWalk 5.0 |> Seq.truncate 100 |> Seq.toList Tomasz Goluch Język F# Przykłady Rekurencja // Użycie ’let rec’ definiuje funkcję rekurencyjną let rec factorial n = if n = 0 then 1 else n * factorial (n-1) // Ponieważ wszystkie wywołania rekurencyjne to // tzw. rekurencja ogonowa, kompilator uruchomi je w pętli, // co zwiększa wydajność i zmniejsza zużycie pamięci. let rec greatestCommonFactor a b = if a = 0 then b elif a < b then greatestCommonFactor a (b - a) else greatestCommonFactor (a - b) b Tomasz Goluch Język F# Przykłady Rekurencja // Rekurencyjne obliczanie sumę listy liczb całkowitych let rec sumList xs = match xs with | [] -> 0 | y::ys -> y + sumList ys // Zmiana na rekurencję ogonową, // przy użyciu funkcji pomocniczej z akumulatorem wyniku let rec private sumListTailRecHelper accumulator xs = match xs with | [] -> accumulator | y::ys -> sumListTailRecHelper (accumulator+y) ys let sumListTailRecursive xs = sumListTailRecHelper 0 xs Tomasz Goluch Język F# Przykłady Rekordy – podobne do krotek ale posiadają nazwane wartości i opcjonalnie mogą posiadać składowe „member” // Definicja typu rekordu type ContactCard = { Name : string; Phone : string; Verified : bool } let contact1 = { Name = "Alf" ; Phone = "(206) 555-0157" ; Verified = false } // Utworzenie kopii rekordu contact1, posiadającej // inne wartości w polach ’Phone’ i ’Verified’ let contact2 = { contact1 with Phone = "(206) 555-0112"; Verified = true } // Konwersja obiektu ’ContactCard’ na napis let showCard c = c.Name + " Phone: " + c.Phone + (if not c.Verified then " (unverified)" else "") Tomasz Goluch Język F# Przykłady Unie dyskryminowane type Suit = // Kolory w kartach do gry | Hearts | Clubs | Diamonds | Spades type Rank = // Figury w kartach do gry | Value of int // Figury od 2 .. 10 | Ace | King | Queen | Jack static member GetAllRanks() = [ yield Ace for i in 2 .. 10 do yield Value i yield Jack yield Queen yield King ] Tomasz Goluch Język F# Przykłady Unie dyskryminowane // Konwersja obiektu ’Card’ na napis let showCard c = let rankString = match c.Rank with | Ace -> "Ace" | King -> "King" | Queen -> "Queen" | Jack -> "Jack" | Value n -> string n let suitString = match c.Suit with | Clubs -> "clubs" | Diamonds -> "diamonds" | Spades -> "spades" | Hearts -> "hearts" rankString + " of " + suitString Tomasz Goluch Język F# Przykłady Unie dyskryminowane type Card = { Suit: Suit; Rank: Rank } // Zwraca listę wszystkich karty z talii let fullDeck = [ for suit in [ Hearts; Diamonds; Clubs; Spades] do for rank in Rank.GetAllRanks() do yield { Suit=suit; Rank=rank } ] // Drukuje wszystkie karty z talii let printAllCards() = for card in fullDeck do printfn "%s" (showCard card) Tomasz Goluch Język F# Przykłady Opcje to wszelkiego rodzaju wartości z etykietą ’Some’ lub ’None’. Są one szeroko stosowane w F# do reprezentowania przypadków w których inne języki użyły by pustej referencji (null). type Customer = { zipCode : decimal option } Tomasz Goluch Język F# Przykłady Opcje // Klasa abstrakcyjna obliczająca strefę wysyłki dla podanego // kodu pocztowego klienta, podaje implementacje dla // abstrakcyjnych metod: ’getState’ i ’getShippingZone’. [<AbstractClass>] type ShippingCalculator = abstract getState : decimal -> string option abstract getShippingZone : string -> int // // // // Zwracana strefa wysyłki zależy od kodu pocztowego klienta. Klient może jeszcze nie posiadać kodu pocztowego lub może być on nieważny member this.customerShippingZone(customer : Customer) = customer.zipCode |> Option.bind this.getState |> Option.map this.getShippingZone Tomasz Goluch Język F# Przykłady Dopasowanie wzorca // Rekord zawierający imię i nazwisko osoby type Person = { First : string Last : string } // definicja dyskryminowanej unii // reprezentujacej 3 rodzaje pracowników type Employee = | Engineer of Person // Manager posiada listę raportów | Manager of Person * list<Employee> // Executive posiada również asystenta | Executive of Person * list<Employee> * Employee Tomasz Goluch Język F# Przykłady Dopasowanie wzorca // zlicza wszystkich podwładnych danego pracownika // w hierarchii zarządania wliczając jego samego let rec countReports(emp : Employee) = 1 + match emp with | Engineer(id) -> 0 | Manager(id, reports) -> reports |> List.sumBy countReports | Executive(id, reports, assistant) -> (reports |> List.sumBy countReports) + countReports assistant Tomasz Goluch Język F# Przykłady Dopasowanie wzorca // znajduje wszystkich managers/executives // o nazwisku "Dave" nie posiadających raportów let rec findDaveWithOpenPosition(emps : Employee list) = emps |> List.filter(function // [] matches the empty list | Manager({First = "Dave"}, []) -> true | Executive({First = "Dave"}, [], _) -> true // ’_’ is a wildcard pattern that matches anything // this handles the "or else" case | _ -> false) Tomasz Goluch Język F# Przykłady Jednostki miary – kod można oznaczyć jednostką miary dokonując arytmetyki na typach liczbowych open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames [<Measure>] type mile = // Współczynnik konwersji: mila/metr: // Metr jest zdefiniowany w SI.UnitNames static member asMeter = 1600.<meter/mile> // Odległość wyrażona za pomocą jednostek imperialnych let d = 50.<mile> // Odległość wyrażona za pomocą systemu metrycznego let d2 = d * mile.asMeter printfn "%A = %A" d d2 // 50.0 = 80000.0 // let error = d + d2 // Błąd kompilacji: // units of measure do not match Tomasz Goluch Język F# Przykłady Programowanie równoległe tablic let oneBigArray = [| 0 .. 100000 |] // intensywne obliczenia CPU let rec computeSomeFunction x = if x <= 2 then 1 else computeSomeFunction (x - 1) + computeSomeFunction (x - 2) // Równoległa mapa na dużej tablicy wejściowej let computeResults() = oneBigArray |> Array.Parallel.map (fun x -> computeSomeFunction (x % 20)) printfn "Parallel computation results: %A" (computeResults()) Tomasz Goluch Język F# Przykłady Programowanie równoległe tablic Parallel computation results: [|1; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377; 610; 987; 1597; 2584; 4181; 1; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377; 610; 987; 1597; 2584; 4181; 1; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377; 610; 987; 1597; 2584; 4181; 1; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377; 610; 987; 1597; 2584; 4181; 1; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377; 610; 987; 1597; 2584; 4181; ...|] Tomasz Goluch Język F# Przykłady Wykorzystanie zdarzeń // Utworzenie instancji Event w którego skład wchodzi // punkt subskrypcji (event.Publish) oraz wyzwalacz zdarzenia // (event.Trigger) let simpleEvent = new Event<int>() // dodanie uchwytu simpleEvent.Publish.Add( fun x -> printfn "handler added with Publish.Add: %d" x) // zdarzenie wyzwalające simpleEvent.Trigger(5) Tomasz Goluch Język F# Przykłady Wykorzystanie zdarzeń // Utworzenie instancji Event przy pomocy standardowej // konwencji .NET: (sender, EventArgs) let eventForDelegateType = new Event<EventHandler, EventArgs>() // dodanie uchwytu eventForDelegateType.Publish.AddHandler( EventHandler(fun _ _ -> printfn "handler added with Publish.AddHandler")) // zdarzenie wyzwalające (należy zauważyć, że argument // nadawcy powinien być zainicjalizowany) eventForDelegateType.Trigger(null, EventArgs.Empty) Tomasz Goluch Język F# Przykłady Dostęp do baz danych przy pomocy dostawcy typów // // // #r #r #r Jest to najprostszy sposób na dostęp dobazy SQL Należy dodać referencje do System.Data, System.Data.Linq, i FSharp.Data.TypeProviders.dll. "System.Data" "System.Data.Linq" "FSharp.Data.TypeProviders" open Microsoft.FSharp.Data.TypeProviders // ConnectionString można zbudować wykorzystując // Server Explorer. Tomasz Goluch Język F# Przykłady Dostęp do baz danych przy pomocy dostawcy typów type SqlConnection = SqlDataConnection<ConnectionString = @"Data Source=.\sqlexpress; Initial Catalog=tempdb; Integrated Security=True"> let db = SqlConnection.GetDataContext() let table = query { for r in db.Table do select r } // Zamiast SqlDataConnection można również użyć // SqlEntityConnection, pozwalającego na dostęp // do bazy przy użyciu Entity Framework. Tomasz Goluch Język F# Przykłady Dostęp do OData przy pomocy dostawcy typów open System.Data.Services.Client open Microsoft.FSharp.Data.TypeProviders // Dane o konsumpcji oraz dochodach społeczeństwa // z serwisu OData Azure Marketplace. type Demographics = Microsoft.FSharp.Data.TypeProvider .ODataService<ServiceUri = "https://api.datamarket.azure.com/Esri/KeyUSDemographicsTrial/"> Tomasz Goluch Język F# Przykłady Dostęp do OData przy pomocy dostawcy typów let ctx = Demographics.GetDataContext() // logowanie do konta Azure Marketplace na // https://datamarket.azure.com/account/info ctx.Credentials <- System.Net.NetworkCredential ("<your liveID>", "<your Azure Marketplace Key>") let cities = query { for c in ctx.demog1 do where (c.StateName = "Washington") } for c in cities do printfn "%A - %A" c.GeographyId c.PerCapitaIncome2010.Value Tomasz Goluch Język F# Dziękuje za uwagę Tomasz Goluch Język F#