Краткое введение в FSharp

Введение
F# (эф-шарп) разработан в Microsoft Corporation в 2005 году. Информация о языке и необходимое програмное обеспечение доступно по адресу http://fsharp.org. Имеется поддержка языка в популярном пакете разработчика Visual Studio, он также доступен для Linux и Mac OS X.
Особенности программирования на FSharp
- основное назначение языка: программирование в функциональном стиле, но есть поддержка императивного и объектно-ориентированного стилей;
- отступы и пробелы имеют значение при форматировании кода;
- доступен режим интерпретатора, в котором можно выполнить команды (REPL), но можно использовать традиционный компилятор для получения исполняемого файла;
- язык F# многое взял от OCaml;
- доступны типы и библиотека .NET;
- язык имеет строгую типизацию.
Замечание Все дальнейшие примеры приведены для интерактивной оболочки. Символ '>' всегда обозначает приглашение интерпретатора и не набирается пользователем
REPL
При использовании MS Visual Studio можно открыть окно F# interactive и вводить команды. Можно выделить в программе текст и послать его в REPL комбинацией: Alt+Enter.
Вне Visual Studio запустить интерпретатор можно программой fsi.
Примеры использования .NET
> open System;;
> let now = DateTime.Now;;
val now : DateTime = 06.11.2016 15:29:42
>Math.PI;;
val it : float = 3.141592654Комментарии
// - однострочный комментарий
(*
Многострочный комментарий
(может занимать несколько строк)
*)Данные
Стандартные типы
Ниже перечислены основные типы с указанием суффиксов при связывании значений с именами:
- byte - uy - беззнаковый байт
- sbyte - y - знаковый байт
- int16 - s - короткое целое
- uint16 - us - беззнаковое короткое целое
- int32 - по-умолчанию - обычное целое
- uint32 - u - беззнаковое целое
- int64 - L - длинное целое
- uint64 - UL - беззнаковое длинное целое
- float32 - f - вещественное одинарной точности
- float - вещественное двойной точности
- decimal - M - вещественное фиксированной точности (28 цифр после запятой)
Присвоение значений
Поскольку F# - функциональный язык, то переменные в классическом (императивном) смысле в нем не приветствуются. Просто вводится имя и этому имени присваивается значение, которое в дальнейшем не может быть изменено.
Примеры создания значений:
let a = 7uy
let b = -18s
let c = 0b1010101yПримеры с возведением в степень:
> let d = 2UL 56;;
val b : uint64 = 72057594037927936ULПреобразования типов
Явные преобразования типов необходимы, поскольку в языке практически отсутствуют неявные преобразования.
Функции преобразования имеют такие же названия, что и типы, например:
>let e = uint64 2;;
val e : uint64 = 2ULСверхдлинные числа
>open System.Numerics;;
>let f = 2I ** 200 ;;
val f : BigInteger =
16069380442589902755419620923411626.......Символы и строки
Все символы хранятся в юникоде:
>let ch = 'л';;
val ch : char = 'л'
> int ch;;
vat it : int = 1083Строки:
>let hello = "hello, world!";;
val hello : string = "hello, world!"
> hello.[1]
val it : char = 'e'Структуры данных
В качестве структур данных выступают:
- списки (lists);
- кортежи (tupples);
- массивы (arrays);
- последовательности (sequences);;
Списки
Списки являются основной структурой данных в функцинальных языках.
Задать список легко:
>let list = [1;2;3];;
val list : int list = [1; 2; 3]Можно указывать диапазоны значений:
> let list = [1..10];;
val list : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
> let list = ['a'..'z'];;
val list : char list =
['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm'; 'n'; 'o';
'p'; 'q'; 'r'; 's'; 't'; 'u'; 'v'; 'w'; 'x'; 'y'; 'z']Пример определения списка с шагом:
> let lst = [1 .. 2 .. 10];;
val lst : int list = [1; 3; 5; 7; 9]Мы можем использовать для создания списка генераторы, - специальные конструкции, заключенные в квадратные скобки:
> let evens = [
for i in 1..10 do
if i % 2 = 0 then
yield i
]
val evens : int list = [2; 4; 6; 8; 10]Вариант с использованием лямбда-функции:
let lst = [for i in 1..10 -> i*i] // [1, 4, 9,..]Кортежи
..
Массивы
..
Последовательности
..
Операторы и операции
В языке F# имеется набор операторов, поддерживающих управляющую логику и циклы.
..
let choice a b =
if a>b then 'a' else 'b'Обработка списка циклом for:
> let lst = [1..10];;
val lst : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
> for item in lst do printfn "%A" item;;
1
2
3
4
5
6
7
8
9
10
val it : unit = ()Оператор yield используется для возвращения элемента из функции или цикла, например, для формирования списка (см. пример в разделе "списки").
Функции
Для функционального языка программирования функция - центральное понятие.
>let square = x * x;;
val square : int -> int
>square 2;;
val it : int = 4Функция square принимает параметр int и возвращает результат типа int.
Другая функция использует в работе ранее определенную:
> let sum_of_squares x y = square x + square y;;
val sum_of_squares : x:int -> y:int -> int
> sum_of_squares 3 4;;
val it : int = 25Странный тип выражения становится понятным, если рассмотреть следующий пример:
> let add x y = x + y;;
val add : int -> int -> intВ данном случае функция add принимает два параметра и возвращает функцию, принимающую один параметр, возвращая, в свою очередь, целое число. Этот пример позволяет понять свойство карриуемости:
Функции могут быть каррированными, то есть в них не передается весь набор параметров сразу:
> let add x y = x + y;;
val add : x:int -> y:int -> int
> let inc = add 1;;
val inc : (int -> int)
> inc 5;;
val it : int = 6Можно явно указать типы параметров:
>let add (x : float) y = x + y;;
val add : float -> float -> floatПараметр обобщенного типа:
>let prn (x : 'a) = x;;
val prn : 'a -> 'aВ качестве обобщенного типа может использоваться любой тип.
Безымянные функции (лямбда-функции) задаются без имени:
> fun x -> x * x;;
val it : x:int -> int = <fun:clo@9>
> it 6;;
val it : int = 36Лямбда-функции являются очень удобным инструментом при обработках элементов структур данных.
О важности пробелов (отступов)
В теле функции мы используем отступы, чтобы отделить внутренние определения от их использования:
> let mid x y =
- let sum a b =
- a + b
- (sum x y) / 2;;
val mid : x:int -> y:int -> int
> mid 4 2;;
val it : int = 3Рекурсия
Рекурсивные функции используются для реализации циклических алгоритмов:
> let rec factor n =
if n<=1 then
1
else
n * factor (n - 1);;
val factor : n:int -> int
> factor 10;;
val it : int = 3628800Генераторы списков
Очень полезны генераторы (специальные функции для генерации значений):
..
Литература
- Крис Смит. Программирование на F#, Символ-Плюс, 2011.
- Chris Smith. Programming F#, O'Reilly Media, 2009.
- Jon Harrop, Don Syme. F# for Scientists, Wiley-Interscience, 2008.
- Robert Pickering, Kit Eason. Beginning F# 4.0, Apress, 2016.
- Dave Fancher. The Book of F#: Breaking Free With Managed Functional Programming, No Starch Press, 2014.
- Adnan Masood. Learning F# Functional Data Structures and Algorithms, Packt, 2015.
- Tomas Petricek, Phillip Trelford. F# Deep Dives, Manning Publications, 2014.
- swlaschin. F# for Fun and Profit, GITBOOK
- James Graff. The Fsharp Handbook, CreateSpace, 2016.
- Tomas Petricek, Jon Skeet. Real-World Functional Programming: With Examples in F# and C#, Manning Publications, 2010.
- Robert Pickering. Foundations of F#, Apress, 2007.
- Robert Pickering. Beginning F#, Apress, 2009.
- Don Syme. Expert F#, Apress, 2007.
- Michael R. Hansen, Hans Rischel. Functional Programming Using F#, Cambridge Press, 2013.