Wydawca Wioleta Szczygielska-Dybciak

Redaktor prowadzący Jolanta Kowalczuk

Redaktor Jolanta Sierakowska

Koordynator produkcji Anna Bączkowska

Zastrzeżonych nazw firm i produktów użyto w książce wyłącznie w celu identyfikacji.

Skład wersji elektronicznej na zlecenie Wydawnictwa Naukowego PWN

Monika Lipiec /Woblink

Książka, którą nabyłeś, jest dziełem twórcy i wydawcy. Prosimy, abyś przestrzegał praw, jakie im przysługują. Jej zawartość możesz udostępnić nieodpłatnie osobom bliskim lub osobiście znanym. Ale nie publikuj jej w internecie. Jeśli cytujesz jej fragmenty, nie zmieniaj ich treści i koniecznie zaznacz, czyje to dzieło. A kopiując jej część, rób to jedynie na użytek osobisty.

Szanujmy cudzą własność i prawo.

Więcej na www.legalnakultura.pl

Polska Izba Książki

Copyright © by Wydawnictwo Naukowe PWN SA

Warszawa 2020

ISBN 978-83-01-20992-6

eBook został przygotowany na podstawie wydania papierowego z 2020 r., (wyd. I)

Warszawa 2020

Wydawnictwo Naukowe PWN SA

02-460 Warszawa, ul. Gottlieba Daimlera 2

tel. 22 69 54 321, faks 22 69 54 288

infolinia 801 33 33 88

e-mail: pwn@pwn.com.pl; reklama@pwn.pl

www.pwn.pl

Od autora

F#. Zadania z funkcyjnego¹ i imperatywnego² programowania z przykładowymi rozwiązaniami to książka zawierająca krótkie i o różnym stopniu trudności zadania wraz z rozwiązaniami, których dokładne przeanalizowanie pozwoli szybko poznać gruntowne podstawy języka F#.

Na polskim rynku wydawniczym jest to najprawdopodobniej pierwszy zbiór zadań z programowania w języku F#, oferujący w jednej książce zadania obejmujące swoją tematyką programowanie funkcyjne, obiektowe oraz imperatywne³.

Programowanie funkcyjne staje się w Polsce coraz bardziej popularne i zyskuje ciągle nowych zwolenników wśród zawodowych programistów, miłośników i pasjonatów programowania. Ten nowy paradygmat programowania z powodzeniem zaczyna zapełniać obszerną przestrzeń nowoczesnych wyzwań programistycznych.

Dla wszystkich zainteresowanych programowaniem funkcyjnym publikacja F#. Zadania z funkcyjnego i imperatywnego programowania z przykładowymi rozwiązaniami może stać się pierwszym bardzo stabilnym pomostem w nauce nowego paradygmatu i języka programowania.

Książkę zaadresowałem do szerokiego grona osób, które dobrze znają paradygmat programowania imperatywnego oraz obiektowego i które pragną bardzo szybko nauczyć się niezbędnych podstaw dotyczących programowania funkcyjnego.

W tym opracowaniu zamieściłem ponad 90 krótkich i typowych zadań z funkcyjnego, imperatywnego oraz obiektowego programowania wraz z przykładowymi rozwiązaniami w postaci gotowych listingów programów spośród wybranych typowych zagadnień dotyczących języka F#. Tam, gdzie było to konieczne (dla prostoty i przejrzystości), wzbogaciłem zadania w niezbędny materiał uzupełniający, np. w postaci wskazówek.

Wszystkie programy, o których mowa w tej książce, zostały skompilowane w bardzo nowoczesnym środowisku programistycznym firmy Microsoft Visual Studio Community 2019⁴. Jest to bezpłatne, w pełni funkcjonalne i rozszerzalne środowisko IDE przeznaczone do tworzenia nowoczesnych aplikacji dla systemów Windows, Android i iOS, a także aplikacji w sieci Web i usług w chmurze.

W trakcie pisania tej książki, dla prostoty i przejrzystości kodu programu, korzystałem wyłącznie z tzw. aplikacji konsolowych dla języka F# (console application) dla wymienionego środowiska programistycznego.

Wyjściowy wzorzec kodu konsolowego programu dla kompilatora firmy Microsoft (Visual F#) jest następujący:

// Learn more about F# at http://fsharp.org

open System

[<EntryPoint>]

let main argv =

    printfn "Hello World from F#!"

    0 // return an integer exit code

Uzyskamy go, klikając Plik -> Nowy Projekt ->, w oknie Utwórz nowy projekt wybieramy Język F#, a następnie wybieramy Aplikacja konsoli .NET Core -> klikamy Dalej. W oknie Konfiguruj nowy projekt wpisujemy nazwę i jego lokalizację. Następnie klikamy Utwórz i nowy projekt zostanie utworzony w określonej przez nas lokalizacji.

Zakładam, że Szanowny Czytelnik zna podstawowe elementy języka F# i elementarne zasady programowania w tym języku oraz że potrafi posługiwać się wspomnianym środowiskiem programistycznym na poziomie elementarnym, tzn. umożliwiającym napisanie nowego lub wczytanie gotowego projektu do aplikacji konsolowych.

Mirosław J. Kubiak

Instrukcja dla Czytelników,

którzy nie chcą korzystać z wymienionego środowiska

IDEONE.COM – http://ideone.com/ – to bardzo ciekawe narzędzie adresowane do osób zainteresowanych programowaniem.

Ten darmowy kompilator online i narzędzie do debugowania pozwala kompilować i uruchamiać kod źródłowy w ponad 60 językach programowania, w tym również w języku F#.

Wzorzec wyjściowy kodu programu dla kompilatora online jest następujący:

open System

// your code goes here

Obsługa tego kompilatora, bardzo prosta i intuicyjna, nie wymaga specjalnych objaśnień, jest dostępna w języku polskim. Inny niż w kompilatorze Microsoft Visual Studio Community 2019 jest mechanizm wprowadzania danych do programu. Czytelnicy, analizując przykładowy program, nie powinni mieć z tym problemu.

Poniżej znajduje się kod programu Zadanie 1.1, przystosowany do kompilatora online.

// Zadanie 1.1

open System

Console.WriteLine("Prosty program ilustrujący wykorzystanie let.")

let ą = 2 // Identyfikatorowi ą nadajemy stałą wartość 2

let b = 3 // Identyfikatorowi b nadajemy stałą wartość 3

let ć = ą + b // Identyfikator ć oblicza sumę dwóch identyfikatorów a + b

Console.Write("ą = {0}, ", ą) // Wyświetlenie wartości identyfikatora ą

Console.Write("b = {0}, ", b) // Wyświetlenie wartości identyfikatora b

Console.Write("ć = ą + b = {0}.", ć) // Wyświetlenie wartości identyfikatora ć

Console.WriteLine(); // Wyświetlenie pustej linii

Życząc skutecznego kompilowania, zachęcam Szanownego Czytelnika do owocnego eksperymentowania również za pomocą tego kompilatora. Średnio zaawansowanemu programiście napisanie programu dla tego kompilatora nie powinno sprawić żadnych trudności.

ROZDZIAŁ 1 .
Nadawanie wartości i komunikacja z użytkownikiem

W rozdziale tym omówię, w jaki sposób nadajemy wartości w języku F#. Przedstawię również sposoby komunikowania się z użytkownikiem.

1.1.
Programowanie funkcyjne – informacje ogólne

Języki programowania, takie jak np. C#, C/C++, Java, Pascal, są nazywane imperatywnymi językami programowania, ponieważ zawierają sekwencje zadań do wykonania. Mechanizm programowania imperatywnego jest następujący: programista jawnie, krok po kroku, definiuje, jakie instrukcje należy wykonać na danych programu, aby otrzymać pożądany efekt.

Programowanie funkcyjne działa inaczej. Zamiast wykonywać zadania sekwencyjnie, języki funkcyjne wyznaczają jedynie wartości poszczególnych wyrażeń. Mechanizm tego programowania wygląda następująco: programista składa odpowiednie wyrażenia w celu uzyskania korzystnego wyniku.

Programy funkcyjne składają się jedynie z funkcji, które są podstawowymi elementami tego języka. Główny program jest funkcją, do której podajemy argumenty, a w zamian otrzymujemy wyznaczoną wartość – wynik działania programu. Główna funkcja składa się z innych funkcji, które z kolei składają się z jeszcze innych funkcji. Funkcje takie dokładnie odpowiadają funkcjom w czysto matematycznym znaczeniu – przyjmują pewną liczbę parametrów i zwracają wynik. Każda operacja wykonywana podczas działania funkcji, ale niemająca związku z wartością zwracaną przez funkcję to tzw. efekt uboczny (np. operacje wejścia-wyjścia, modyfikowanie zmiennych globalnych).

Funkcje, które nie mają efektów ubocznych, nazywane są funkcjami czystymi (pure function).

Język F# jest:

• wieloparadygmatowym językiem programowania zawierającym w sobie głównie cechy języka funkcyjnego, ale umożliwiającym także pisanie kodu imperatywnego oraz obiektowego;

• językiem silnie typowanym⁵ zaprojektowanym w celu pisania prostego, solidnego i wydajnego kodu, służącego do rozwiązywania złożonych problemów informatycznych;

• nowoczesnym językiem programowania typu open source;

• częścią platformy .NET firmy Microsoft;

• wzorowany na językach OCaml i Haskell⁶;

• językiem, który łączy w sobie różne cechy, takie jak nowoczesność, zwięzłość, wydajność i ekspresywność [3].

W języku F# wszystko jest wartością, w przeciwieństwie do języków obiektowych, w przypadku których wszystko jest obiektem (zob. rozdział 5).

Programiści języka F# rezerwują określenie „obiekt” dla wartości specjalnego rodzaju:

• wartości, których obserwowalne właściwości zmieniają się w trakcie wykonywania programu;

• wartości, które wskazują dane lub stany określające tożsamość (takie jak unikatowe identyfikatory całkowitoliczbowe) albo które wyznaczają ogólną tożsamość obiektu, służącą do odróżniania obiektów od innych wartości identycznych w pozostałych aspektach;

• wartości, które można sprawdzić, by ustalić dodatkowe mechanizmy za pomocą rzutowania, konwersji i interfejsów [3].

Szanowny Czytelniku, jeżeli do tej pory sądziłeś, że programowanie imperatywne i obiektowe to jedyny sposób myślenia o programowaniu, to pisanie programów w języku F# może wymagać od Ciebie zupełnie innego podejścia.

1.2.
Identyfikator wartości

Do deklarowania identyfikatorów w języku F# używamy słowa kluczowego let⁷. Jest to najważniejsza instrukcja, jaką będziemy stosowali, programując w tym języku. Służy ono do definiowania danych, obliczanych wartości i funkcji. Po lewej stronie tej instrukcji często znajduje się prosty identyfikator, można tam jednak umieszczać także wzorce lub nazwę funkcji z listą nazw argumentów. Po prawej stronie instrukcji let, czyli po znaku =, znajduje się wartość lub wyrażenie, np.

let ą = 2 // Identyfikatorowi ą nadajemy wartość 2

Oznacza to, że do zadeklarowania identyfikatora o nazwie ą użyliśmy słowa kluczowego let i nadaliśmy mu wartość równą 2.

UWAGA!
Język programowania Visual F# zawiera w sobie standard Unicode, co oznacza, że identyfikatory wartości zawierają również polskie znaki.

W innych językach (np. C++, Java, C#) wartość lokalna (local value) jest nazywana zmienną lokalną (local variable). Programiści języka F# wolą posługiwać się pojęciem wartość, zamiast zmienna.

W języku F# nie można modyfikować wartości lokalnych po ich zainicjowaniu, chyba że dana wartość lokalna jest jawnie opatrzona modyfikatorem mutable (zob. rozdział 4). W języku F# istnieją wartości (dane) całkowicie niemodyfikowalne (immutable). Oznacza to, że ani określona wartość lokalna, ani dane, które ona wskazuje, nie mogą zostać zmienione za pomocą zewnętrznych modyfikacji. Takie dane nazywane są wartościami niemodyfikowalnymi. Na przykład wszystkie podstawowe typy⁸ (basic types) z platformy .NET⁹ (m.in. liczby całkowite, łańcuchy znaków i wartości typu System.DateTime) są niemodyfikowalne.

Oto prosty przykład zadania demonstrującego zastosowanie słowa kluczowego let w programie.

Zadanie 1.1

Napisz program, który ilustruje proste zastosowanie słowa kluczowego let. Wynik działania programu wyprowadź na ekran komputera.

Przykładowe rozwiązanie – listing 1.1

// Zadanie 1.1

open System

Console.WriteLine("Prosty program ilustrujący wykorzystanie słowa kluczowego let.")

let ą = 2 // Identyfikatorowi ą nadajemy stałą wartość 2

let b = 3 // Identyfikatorowi b nadajemy stałą wartość 3

let ć = ą + b // Identyfikator ć oblicza sumę dwóch identyfikatorów ą + b

Console.Write("ą = {0}, ", ą) // Wyświetlenie wartości identyfikatora ą

Console.Write("b = {0}, ", b) // Wyświetlenie wartości identyfikatora b

Console.Write("ć = ą + b = {0}.", ć) // Wyświetlenie wartości identyfikatora ć

Console.WriteLine(); // Wyświetlenie pustej linii

// Zatrzymanie konsoli

Console.WriteLine("Naciśnij dowolny klawisz.")

Console.ReadKey(true) |> ignore

Wcięcie w programie ilustrowane linijką kodu…

let ć =

ą + b // Identyfikator ć oblicza sumę dwóch identyfikatorów ą + b

…pełni w języku F# rolę nawiasów klamrowych {}, tak jak to można spotkać np. w języku C++.

Wcięcie to jest obowiązkowe. Brak jego jest sygnalizowane przez kompilator.

Wcięcie kończy się w miejscu, gdzie kolejny wiersz nie jest już przesunięty.

Linijka kodu…

open System

…informuje nas, że do programu została dołączona przestrzeń nazw¹⁰ System. Pozwoli to nam m.in. na skorzystanie z konsolowych operacji wejścia-wyjścia (tj. np. metod klasy Console) na platformie .NET.

Następna linijka…

Console.WriteLine("Prosty program ilustrujący wykorzystanie słowa kluczowego let.")

…wyświetli na ekranie komputera następujący tekst:

Prosty program ilustrujący wykorzystanie słowa kluczowego let.

W kolejnych linijkach kodu…

let ą = 2 // Identyfikatorowi ą nadajemy wartość 2

let b = 3 // Identyfikatorowi b nadajemy wartość 3

…zostaną nadane określone wartości poszczególnym identyfikatorom: ą = 2 i b = 3.

Większość czytelników, programujących w językach imperatywnych, takich jak C/C++, Java itd., może taki zapis traktować jako przypisanie do zmiennej. Istnieje tutaj pewne podobieństwo, ale w czystych funkcjonalnych językach nadana raz wartość identyfikatorowi nie może ulec zmianie w trakcie działania programu. Dlatego będziemy używali określenia identyfikator wartości, zamiast zmienna.

Linijka kodu:

Console.Write("ć = ą + b = {0}.", ć) // Wyświetlenie wartości identyfikatora ć

gdzie:

ć = ą + b – oblicza i wyświetla na ekranie monitora wartość tego identyfikatora;

{0} – oznaczona tak sekwencja wyznacza miejsce na dane.

Linijka kodu…

Console.ReadKey(true) |> ignore

…wstrzymuje zamknięcie konsoli do momentu naciśnięcia dowolnego klawisza. Operator |> to tzw. pipe-forward operator (zob. rozdział 2), który nakazuje zignorowanie zwracanego wyniku.

Komentarze w języku F# oznaczamy dwoma ukośnikami //. Służą one do opisu linijek programu i nie są kompilowane.

Rezultat działania programu można zobaczyć na rysunku 1.1.

Prosty program ilustrujący wykorzystanie słowa kluczowego let.

ą = 2, b = 3, ć = ą + b = 5.

Naciśnij dowolny klawisz.

Rysunek 1.1. Efekt działania programu Zadanie 1.1

UWAGA!
Napis Naciśnij dowolny klawisz będzie pojawiał się w dalszej części książki tylko w listingach programów.

Program z zadania 1.1 można również przedstawić w innej formie:

open System

let main() =

    Console.WriteLine("Prosty program ilustrujący wykorzystanie słowa kluczowego let.")

    let ą = 2 // Identyfikatorowi ą nadajemy stałą wartość 2

    let b = 3 // Identyfikatorowi b nadajemy stałą wartość 3

    let ć = ą + b // Identyfikator ć oblicza sumę dwóch identyfikatorów a + b

    Console.Write("ą = {0}, ", ą) // Wyświetlenie wartości identyfikatora ą

    Console.Write("b = {0}, ", b) // Wyświetlenie wartości identyfikatora b

    Console.Write("ć = ą + b = {0}.", ć) // Wyświetlenie wartości identyfikatora ć

    Console.WriteLine(); // Wyświetlenie pustej linii

main()

// Zatrzymanie konsoli

Console.WriteLine("Naciśnij dowolny klawisz.")

Console.ReadKey(true) |> ignore

Skorzystaliśmy tutaj z funkcji main(), odgrywającej rolę funkcji głównej.

W dalszej części tej książki nie będziemy korzystali z funkcji głównej main(), zachowując strukturę programu, jak w zadaniu 1.1.

Nie jest to do końca poprawne, ale… program się kompiluje!

W ramach ćwiczeń utrwalających wymagający czytelnicy mogą przepisać wszystkie listingi programów zawartych w tej książce do wspomnianej postaci, wykorzystując funkcję główną main() oraz punkt wejścia do programu [<EntryPoint>] (zob. Od Autora).

Zadanie 1.2

Napisz program, który wyświetla na ekranie komputera wartość predefiniowanej stałej π = 3,14... Należy przyjąć format wyświetlania tej stałej z dokładnością do pięciu miejsc po przecinku.

Przykładowe rozwiązanie – listing 1.2

// Zadanie 1.2

open System

Console.WriteLine("Program wyświetla stałą pi z zadaną dokładnością.")

Console.WriteLine("Pi = {0:##.#####}.", Math.PI)

// Zatrzymanie konsoli

Console.WriteLine("Naciśnij dowolny klawisz.")

Console.ReadKey(true) |> ignore

Linijki kodu w programie…

Console.WriteLine("Pi = {0:##.#####}.", Math.PI);

…oznaczają, że do wyświetlenia na ekranie liczby π przeznaczono 7 pól, w tym 5 na część ułamkową {0:##.#####}. Klasa Math jest standardową klasą języka F#, umożliwiającą wykonanie różnych matematycznych obliczeń.

Rezultat działania programu można zobaczyć na rysunku 1.2.

Program wyświetla stałą pi z zadaną dokładnością.

Pi = 3,14159.

Rysunek 1.2. Efekt działania programu Zadanie 1.2

Do wyprowadzania wyników na ekran monitora można również skorzystać z funkcji printf i printfn. Stosując łańcuchy formatujące w postaci…

ciąg_formatujący wartości formatowane

…możemy otrzymać wyniki w wygodnej dla nas formie. Pokazuje to następujący fragment programu…

let pi = Math.PI

printfn "%f" pi

printfn "%1.1f" pi

printfn "%2.2f" pi

printfn "%2.8f" pi

…oraz rezultat jego działania:

3.141593

3.1

3.14

3.14159265

Więcej informacji na ten temat Czytelnik może znaleźć w publikacji [1] lub w Core.Printf Module (F#)¹¹ na stronie internetowej https://msdn.microsoft.com/en-us/.

Zadanie 1.3

Napisz program, który wyświetla na ekranie komputera pierwiastek kwadratowy z wartości predefiniowanej π = 3,14... z dokładnością do dwóch miejsc po przecinku.

Przykładowe rozwiązanie – listing 1.3

// Zadanie 1.3

open System

Console.WriteLine("Program wyświetla pierwiastek kwadratowy z liczby pi")

Console.WriteLine("z dokładnością do dwóch miejsc po przecinku.")

Console.WriteLine()

Console.WriteLine("Sqrt(pi) = {0:##.##}.", Math.Sqrt(Math.PI))

// Zatrzymanie konsoli

Console.WriteLine("Naciśnij dowolny klawisz.")

Console.ReadKey(true) |> ignore

Metoda Sqrt() pozwala na obliczenie pierwiastka kwadratowego. Jest ona metodą standardowej klasy Math.

Rezultat działania programu można zobaczyć na rysunku 1.3.

Program wyświetla pierwiastek kwadratowy z liczby pi

z dokładnością do dwóch miejsc po przecinku.

Sqrt(pi) = 1,77.

Rysunek 1.3. Efekt działania programu Zadanie 1.3

Oto przykłady zadań zawierające podstawowe działania arytmetyczne.

Zadanie 1.4

Napisz program, który oblicza wynik dzielenia całkowitego bez reszty dla dwóch liczb całkowitych: a = 37 i b = 11.

Wskazówka:
w języku F# w przypadku zastosowania operatora dzielenia / dla liczb całkowitych reszta wyniku jest pomijana. Tak samo jest w językach imperatywnych: C/C++ i Java.

Przykładowe rozwiązanie – listing 1.4

// Zadanie 1.4

open System

let a = 37

let b = 11

Console.WriteLine("Program wyświetla wynik dzielenia całkowitego")

Console.WriteLine("bez reszty dwóch liczb całkowitych a i b.")

Console.WriteLine("Dla liczb: a = {0} i b = {1}", a, b)

Console.WriteLine("{0}/{1} = {2}.", a, b, a/b)

// Zatrzymanie konsoli

Console.WriteLine("Naciśnij dowolny klawisz.")

Console.ReadKey(true) |> ignore

Rezultat działania programu można zobaczyć na rysunku 1.4.

Program wyświetla wynik dzielenia całkowitego

bez reszty dwóch liczb całkowitych a i b.

Dla liczb: a = 37 i b = 11

37/11 = 3.

Rysunek 1.4. Efekt działania programu Zadanie 1.4

Zadanie 1.5

Napisz program, który oblicza resztę z dzielenia całkowitego dwóch liczb całkowitych: a = 37 i b = 11.

Wskazówka:
należy zastosować operator reszty z całkowitego dzielenia całkowitego modulo, który oznaczamy w języku F# jako %. Podobnie jak w językach imperatywnych, takich jak C/C++ i Java, operator ten umożliwia uzyskanie tylko reszty z dzielenia, a wartość całkowita jest odrzucana.

Przykładowe rozwiązanie – listing 1.5

// Zadanie 1.5

open System

let a = 37

let b = 11

Console.WriteLine("Program oblicza resztę z dzielenia całkowitego")

Console.WriteLine("dwóch liczb całkowitych a i b.");

Console.WriteLine()

Console.WriteLine("Dla liczb: a = {0} i b = {1}", a, b)

Console.WriteLine("{0}%{1} = {2}.", a, b, a%b)

// Zatrzymanie konsoli

Console.WriteLine("Naciśnij dowolny klawisz.")

Console.ReadKey(true) |> ignore

Rezultat działania programu można zobaczyć na rysunku 1.5.

Program oblicza resztę z dzielenia całkowitego

dwóch liczb całkowitych a i b.

Dla liczb: a = 37 i b = 11

37%11 = 4.

Rysunek 1.5. Efekt działania programu Zadanie 1.5

Na koniec tego rozdziału została nam do omówienia jedna kwestia. Zajmiemy się nią w podrozdziale 1.3.

Przypisy

1 Programowanie funkcyjne to taki paradygmat programowania, którego funkcje należą do wartości podstawowych. Nacisk kładzie się na wartościowanie funkcji, a nie wykonywanie poleceń.

2 Programowanie imperatywne to taki paradygmat programowania, który opisuje proces wykonywania programu jako sekwencję ciągu wykonywanych instrukcji zmieniających jego stan.

3 Język F# zawiera w sobie czwarty paradygmat – programowanie zorientowane na język (language-oriented programming) [3].

4 Środowisko programistyczne Microsoft Visual Studio Community 2019 można bezpłatnie pobrać ze strony https://www.visualstudio.com/pl/.

5 Silna typizacja to system typów w języku programowania, w którym każde wyrażenie ma ustalony typ i nie można go używać w kontekście przeznaczonym dla innych typów.

6 O funkcyjnym języku Haskell można przeczytać także w znakomitej książce Bruce’a A. Tate, Siedem języków w siedem tygodni. Praktyczny przewodnik nauki języków programowania, wydanej przez Wydawnictwo Helion.

7 Słowo kluczowe let służy do związania (binding) wartości z identyfikatorem lub z funkcją. W języku F# identyfikator wartości oraz funkcję definiujemy, używając tego samego słowa kluczowego let. Identyfikator wartości jest szczególnym przypadkiem funkcji – bez parametrów (czyli stałej).

8 Zob. Typy podstawowe, https://docs.microsoft.com/pl-pl/dotnet/fsharp/language-reference/basic-types (dostęp: 15.06.2019).

9 Pełną dokumentację platformy .NET można znaleźć pod adresem: https://docs.microsoft.com/pl-pl/dotnet/.

10 Przestrzenie nazw definiują rejon deklaracji, umożliwiając oddzielenie jednego zestawu nazw od drugiego. Nazwy zadeklarowane w jednej przestrzeni nazw nie wchodzą w konflikt z takimi samymi nazwami zadeklarowanymi w innej przestrzeni nazw.

11 Więcej na ten temat zob. Core.Printf Module (F#), https://msdn.microsoft.com/visualfsharpdocs/conceptual/core.printf-module-%5bfsharp%5d (dostęp: 18.05.2019).