[PL] F# Wprowadzenie – listy i unie oraz wyszukiwanie wzorców

Kontynuując rozpoczęty artykuł o F#. Powracamy do interpretera FSI aby poeksperymentować dalej ze składnią.

Listy w F#:

Pusta lista:

> let emptyList = [];;
val emptyList : 'a list

Elementy listy:

> let listOneItem = "one" :: [];;
val listOneItem : string list = ["one"]
> let listTwoItems = "one" :: "two" :: [];;
val listTwoItems : string list = ["one"; "two"]

Listy wspierają tylko jeden typ składników:

> let badList = "one" :: 123 :: "three" :: [];; 
  let badList = "one" :: 123 :: "three" :: [];;
-----------------------^^^
stdin(5,24): error FS0001: This expression has type
int
but is here used with type
string

Oczywiście można listy zadeklarować w łatwiejszy sposób:

> let easyList = ["A"; "B"; "C"];;
val easyList : string list = ["A"; "B"; "C"]

A tak możemy łaczyć listy:

> let listLetters = easyList;;
val listLetters : string list = ["A"; "B"; "C"]
> let listNumbers = ["1";"2";"3"];;
val listNumbers : string list = ["1"; "2"; "3"]
> let listCombined = listLetters @ listNumbers;;
val listCombined : string list = ["A"; "B"; "C"; "1"; "2"; "3"]

Wyszukiwanie wzorców:

W zasadzie podobnie do C#, chociaż F# ma więcej możliwości wychodzących poza standardowe konstrukcje z C#. Poniżej przykłady:

> let patternMatching x = match x with | 0 -> printfn "Value is 0" | 1 -> printf
- n "Value is 1" | 2 -> printfn "Value is 2";;
val patternMatching : int –> unit
> let andTrue x = match x with | (true, true) -> true | _ -> false;;
val andTrue : bool * bool -> bool

Krótki test:

> andTrue(false,true);;
val it : bool = false
> andTrue(true,true);;
val it : bool = true

Spróbujmy się za pomocą powyższej składni pobawić ciągiem Fibonacci’ego:

> let rec fib x = match x with | 0 -> 0 | 1 -> 1 | _ -> fib(x-1) + fib(x-2);;
val fib : int -> int

I znajdzmy 42 liczbę:

> fib 42;;
val it : int = 267914296

Typy i unie:

Od tego momentu może zamiast fsi.exe proponuję utworzyć w Visual Studio 2010 nowy projekt F# (aplikacja konsolowa). Po jej utworzeniu dostaniemy plik program.fs zawierający jedną wdzięczną linijkę komentarza:

// Learn more about F# at https://fsharp.net

Krótki test zanim przejdziemy dalej, czyli Hello World zaczynamy jak na poniższym obrazku:

image 

Intellisense po przestrzeniach .NETowych jak widać działa tak jakbyśmy oczekiwali, konstrukcja jest trochę inna niż przy innych projektach konsolowych, i nasze “Hello World”, tak naprawdę może zawierać jedną linijkę:

System.Console.WriteLine("Hello World\n")

W takim razie spróbujmy zdefiniować własne typy na przykładzie poniższego prostego programiku:

//proste typy
type ItemName = string
type UnitPrice = float
type Percent = float
//definicja rekordu:
type OrderItem = {
 ItemOrdered : ItemName;
 Quantity : int;
 UnitPrice : UnitPrice;
 Discount : Percent;
}
//przykład rekordu
let myOrder = { ItemOrdered="Pants"; Quantity=1; UnitPrice=12.38; Discount=10.0}
//kontrolnie jaki typ zostanie zwrócony?
System.Console.WriteLine("myOrder type is: " + myOrder.ToString())
let key = System.Console.ReadKey()

Ten programik powinien zwrócić: myOrder type is: Program+OrderItem
Ciekawą konstrukcją są unie i ich wykorzystanie, oto przykład z temperaturą

//Unia, przykład konwersji temperatur
type temperature =
| Celcius of float
| Fahrenheit of float
| Kelvin of float

let t1 = Celcius 32.0
let t2 = Fahrenheit 98.3
let t3 = Kelvin 5.0

let conv2F x =
    match x with
        | Celcius x -> Fahrenheit (x * 9.0/5.0 + 32.0)
        | Fahrenheit x -> Fahrenheit x
        | Kelvin x -> Fahrenheit (x * 9.0/5.0 - 459.67);;       

let convResult = conv2F t1

Jak spojrzymy w wartość convResult to zobaczymy:
image 
Możliwości takich unii łatwo sobie wyobrazić. Przy okazji proszę zwrócić uwagę na składnię związaną z wyszukiwaniem wzorców (match x with), w tym przypadku wykorzystaną, aby rozpoznać w jakim formacie temperaturę ma nasz pojemnik.