Niech będzie stała
Pierwszą rzeczą jaką musimy zrozumieć w programowaniu funkcyjnym jest to, że nie przypisujemy wartości do zmiennej, ale raczej nadajemy alias pewnej wartości. Można to też rozumieć jako deklarowanie stałych.
Będziemy się posługiwać słowem kluczowym let
w celu zadeklarowania stałej.
1: 2: 3: 4: 5: |
|
Powyższe stałe mają następujące typy
1: 2: 3: 4: 5: |
|
W F# int
to System.Int32
, float
to System.Double
, char
to System.Char
, string
to System.String
, a unit
to specjalny typ, który ma jedną wartość ()
i używany jest jako odpowiednik void
z programów imperatywnych. W programowaniu funkcyjnym, każda funkcja musi zwracać jakąś wartość. Więc kiedy nie ma zbytnio co zwrócić to zwracamy właśnie unit
.
F# Interactive
Żeby pobawić się F# będziemy korzystać z narzędzia FSI, które dynamicznie kompiluje kod, który mu podajemy i dokonuje ewaluacji wyrażeń.
Poniżej będę pisał fsi
odwołując się do tego środowiska, natomiast pod Linuxem binarka nazywa się fsharpi
.
Możemy uruchomić FSI w pustym środowisku
1:
|
|
Możemy załadować nasz skrypt
1:
|
|
Ale możemy załadować skrypt również w trakcie pracy przez dyrektywę #l
1:
|
|
Funkcje
Stałe mogą określać dowolną wartość, więc również funkcje.
1:
|
|
Zadeklarowaliśmy właśnie stałą id
, której wartość to funkcja typu a -> a
(funkcja identyczności). W zasadzie jest to uproszczenie poniższczego zapisu:
1:
|
|
Ta postać to tzw. wyrażenie lambda lub funkcja anonimowa. W matematyce używa się symbolu λ. niech id = λx. x
. Nie będziemy zagłębiali się tutaj zbytnio w rachunek lambda, ale warto zwrócić uwagę na kilka rzeczy:
Każda lambda ma jeden argument. Funkcje wieloargumentowe to w zasadzie funkcje jednoargumentowe, które zwracają kolejną jednoargumentową funkcję.
1: 2: |
|
Aby obliczyć wyrażenie wewnątrz lambdy musimy zaaplikować do niej wartość, którą zwiążemy z nazwą argumentu.
1:
|
|
Aplikacja to inaczej wywołanie funkcji. Jednak aplikacja może być również częściowa. W takim przypadku funkcja zostanie wywołana dopiero w momencie aplikacji ostatniego argumentu.
1: 2: |
|
Lepiej zobrazujemy to przy zapisie lambda.
1: 2: 3: |
|
Wyrażenia warunkowe
W programowaniu funkcyjnym można bardzo łatwo określić gdzie wyrażenie się zaczyna, a gdzie kończy. W przypadku wyrażenia if
jego struktura to if bool-exp then 'a-exp else 'a-exp
, gdzie 'a
jest pewnym typem. To oznacza, że każdy If ma Else oraz, że obie gałęzie muszą zwracać wartość tego samego typu.
Kolejna rzecz, przez którą trzeba się przebić to to, że ciało funkcji to jedno wyrażenie. W przypadku imperatywnego F# nie będzie to prawdą, ale pisząc czysto funkcyjny kod jak najbardziej.
1:
|
|
W F# do porównania używa się znaku =
, ale jeśli jesteś bardzo przyzwyczajona do ==
to można sobie taki operator samemu zdefiniować.
1: 2: 3: 4: 5: 6: |
|
Powyżej można wstawić nawiasy wokół drugiego warunku, żeby precyzyjniej pokazać strukturę wyrażeń.
1: 2: 3: 4: |
|
Przy okazji poruszymy kwestię białych znaków i wcięć. W F# jeśli przerzucamy część wyrażenia do nowej linii to musi ono być wcięte tak żeby zaczynać się nie wcześniej niż początek tego wyrażenia. Wcięcia robimy spacjami, nie znakiem \t
.
Zadania
- Napisz funkcję
succ
, która dla znaku od 'a' do 'y' poda znak następny, a dla 'z' poda 'a'. - Użyj wyrażenia warunkowego do deklaracji stałej liczbowej.
Pytania? Jeśli wszystko jasne, to przechodzimy do następnego modułu
Full name: Lets.a
Full name: Lets.b
Full name: Lets.c
Full name: Lets.d
Full name: Lets.e
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
val char : value:'T -> char (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.char
--------------------
type char = System.Char
Full name: Microsoft.FSharp.Core.char
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
Full name: Microsoft.FSharp.Core.unit
Full name: Microsoft.FSharp.Compiler.Interactive.Settings.fsi
Full name: Lets.id
Full name: Lets.id'
Full name: Lets.multiArg
Full name: Lets.multiArg'
Full name: Lets.a'
Full name: Lets.dodaj
Full name: Lets.dodaj5
Full name: Lets.dodaj'
Full name: Lets.dodaj5'
Full name: Lets.dodaj5''
Full name: Lets.even
Full name: Lets.discrete
Full name: Lets.discrete'