F Sharp

Материал из Википедии ? свободной энциклопедии
Перейти к навигации Перейти к поиску
F#
Изображение логотипа
Класс языка
Появился в 2005
Автор Microsoft Research
Разработчик Майкрософт и F Sharp Software Foundation [вд]
Расширение файлов .fs , .fsi , .fsx или .fsscript
Выпуск 8.0 ( 14 ноября 2023 )
Система типов строгая
Испытал влияние Objective Caml , C# , Haskell
Лицензия Apache Software License
Сайт fsharp.org
ОС Кроссплатформенное программное обеспечение ( .NET Framework , Mono )
Логотип Викисклада  Медиафайлы на Викискладе

F# (произносится эф-шарп ) ? мультипарадигмальный язык программирования из семейства языков .NET , поддерживающий функциональное программирование в дополнение к императивному ( процедурному ) и объектно-ориентированному программированию . Структура F# во многом схожа со структурой OCaml с той лишь разницей, что F# реализован поверх библиотек и среды исполнения .NET . Язык был разработан Доном Саймом ( англ.   Don Syme ) в Microsoft Research в Кембридже , в настоящее время его разработку ведёт Microsoft Developer Division. F# достаточно тесно интегрируется со средой разработки Visual Studio и включён в поставку Visual Studio 2010/2012/2013/2015/2017/2019/2022; разработаны также компиляторы для Mac и Linux [1] .

Microsoft интегрировала среду разработки F# в Visual Studio 2010 и более новые версии.

4 ноября 2010 года код компилятора F# и основных библиотек к нему опубликован под Apache License 2.0 [2] .

Особенности

[ править | править код ]

Код на языке F# является безопасным в отношении типов , часто бывает более компактным, чем аналогичный код C# , за счёт вывода типов . В F# действует строгая типизация, неявные преобразования типов полностью отсутствуют, что полностью исключает ошибки, связанные с приведением типов.

Такие возможности, как обобщённое программирование и функции высших порядков позволяют писать абстрактные обобщённые алгоритмы , которые управляют параметризованными структурами данных (например, массивами , списками , графами , деревьями ).

Во многих языках большинство значений являются переменными. Например, в результате исполнения следующего кода на языке C++ в переменной x будет храниться значение 3:

int
 x
 =
 2
;

x
++
;

В F#, напротив, по умолчанию все значения являются константами. F# допускает переменные, для чего требуется специально помечать значения как изменяемые при помощи слова mutable:

let
 x
 =
 2
 // неизменяемое значение

let
 mutable
 y
 =
 2
 // переменная

x
 <-
 3
 // ошибка

y
 <-
 3
 // Ok. y = 3

Также в F# есть ссылочные типы и объекты, которые также могут содержать изменяемые значения. Тем не менее, бо?льшая часть кода является чистыми функциями , что позволяет избежать многих ошибок и упростить отладку. Кроме того, упрощается распараллеливание программ. При всём этом код редко становится сложнее, чем аналогичный код на императивном языке.

Одна из основных идей F# заключается в том, чтобы удостовериться, что имеющийся код и типы в функциональном языке программирования могут быть легко доступны из других .NET-языков. Программы на F# компилируются в сборки CLR (файлы с расширениями .exe и .dll), однако, для их запуска необходима установка пакета среды исполнения дополнительно к .NET Framework.

Интересной особенностью (и отличием от OCaml ) является управление логической вложенностью конструкций кода за счёт отступов в виде произвольного количества пробелов (и только лишь пробелов). Знаки табуляции для этой цели не поддерживаются. Это приводит к постоянным дискуссиям на форумах опытных разработчиков, которые привыкли пользоваться знаками табуляции в других языках программирования.

Компилятор и интерпретатор

[ править | править код ]

F# ? компилируемый язык программирования, при этом в качестве промежуточного языка используется язык Common Intermediate Language (CIL), так же как и в программах, написанных на языках C# или VB.NET .

Наряду с F#- компилятором (fsc) присутствует и F#- интерпретатор (fsi), который исполняет F#-код интерактивно.

Отличительной чертой F#- компилятора и F#- интерпретатора является возможность воспринимать код двумя разными способами ? немедленно (по умолчанию) и отложенно (программисту требуется явно указать это в исходном коде). В случае немедленной интерпретации, выражения вычисляются заранее в момент запуска программы на выполнение, независимо от того, вызываются ли они в процессе выполнения программы или нет. В этом случае, зачастую снижается производительность выполнения программы, а также происходит неэкономное расходование ресурсов системы (например, памяти). В случае ленивой интерпретации кода, выражения вычисляются только в тот момент, когда к ним происходит непосредственное обращение в процессе выполнения программы. Это избавляет программу от перечисленных выше недостатков, но снижает предсказуемость в плане объёма и последовательности использования ресурсов (процессорного времени, памяти, устройств ввода-вывода и т. п.) на различных этапах выполнения программы.

Синтаксис F# построен на математической нотации, а программирование чем-то похоже на алгебру , что делает F# похожим на Haskell . Например, когда вы определяете новый тип, то можете указать, что переменными этого типа будут ≪ целые или строки ≫. Вот как это выглядит:

type
 myType
 =
 IntVal
 of
 int
 |
 StringVal
 of
 string

Важным примером таких типов является Option, который содержит либо значение некоторого типа, либо ничего.

type
 Option
<
a
>
 =
 None
 |
 Some
 of
 a

Он является стандартным типом F# и часто используется в ситуациях, когда результатом работы какого-то кода (например, поиска в структуре данных) является значение, которое может и не быть получено.

Код также представляет собой математическую нотацию. Следующая конструкция эквивалентна f(x) = x + 1 в алгебре:

let
 f
 x
 =
 x
 +
 1

F# работает следующим образом: тип ≪ f ≫ представляет собой ≪ int -> int ≫, то есть функция получает на вход целое и выдаёт на выход целое.

F# позволяет получить доступ абсолютно ко всему, что есть в FCL . Синтаксис для работы с библиотеками .NET в этом смысле максимально близок к синтаксису C# . Особенности языка заметны при использовании всего спектра возможностей F#. К примеру, следующий код применяет функцию к элементам списка :

 let
 rec
 map
 func
 lst
 =

     match
 lst
 with

        |
 []
 ->
 []

        |
 head
 ::
 tail
 ->
 func
 head
 ::
 map
 func
 tail


 let
 myList
 =
 [
1
;
3
;
5
]

 let
 newList
 =
 map
 (
fun
 x
 ->
 x
 +
 1
)
 myList

В ≪ newList ≫ теперь находится ≪ [2;4;6] ≫.

Разбор списка в этой функции ведётся с помощью ещё одной мощной возможности сопоставления с образцом . Она позволяет задавать образцы при совпадении с которыми вычисляются соответствующие вхождения оператора match. Первый образец ≪[]≫ означает пустой список. Второй ? список состоящий из первого элемента и хвоста (который может быть произвольным списком, в том числе и пустым). Во втором образце значение головы связывается с переменной head, а хвоста с tail (имена могут быть произвольные). Таким образом кроме основной задачи образец ещё позволяет производить декомпозицию сложных структур данных. Например, в случае с типом Option сопоставление с образцом выглядит так:

match
 x
 with

|
 Some
 v
 ->
 printfn
 "Найдено значение %d."
 v

|
 None
 ->
 printfn
 "Ничего не найдено."

|
 None
 ->
 printfn
 "Привет"

Язык поддерживает генераторные выражения, определенные для множеств { … } , списков [ … ] и массивов [| … |] Например:

let
 test
 n
 =
 [
 for
 i
 in
 0
 ..
 n
 do
                            
                 if
 i
 %
 2
 =
 0
 then
 
                     yield
 i
 ]

Функция map является одной из стандартных функций над списками, которые содержатся в модуле List. Также существуют функции для других структур данных, объявленные в модулях Array, Set, Option.

Полезным инструментом является оператор pipe-forward (|>), который позволяет писать цепочки вызовов функций в обратном порядке. В результате имеет место такой код (в комментариях указаны промежуточные значения):

[
1
;
 2
;
 5
]

|>
 List
.
map
 ((+)
 1
)
 // [2; 3; 6]

|>
 List
.
filter
 (
fun
 x
 ->
 x
 %
 2
 =
 0
)
 // [2; 6]

|>
 List
.
sum
 // 8

Использование оператора |> исключает необходимость использования большого числа скобок, а также изменяет визуальное восприятие кода. И теперь данный код читается так: взять такой-то список, прибавить к каждому элементу 1, затем оставить только четные элементы, вернуть их сумму. То есть, описывается последовательность действий, выполняемая над изначальным объектом, в том порядке, в котором она происходит и на компьютере.

Далее небольшая демонстрация того, насколько функции .NET расширяют возможности F#. Одним из примеров являются оконные приложения и событийная обработка. Событийная обработка означает ? какие-то действия в программе происходят только как реакция на определенные события ? действия пользователей, подключение устройств и т. д. Проект можно создать как в Visual Studio, так и в любом текстовом документе, который затем подается на вход компилятору F# (fsc).

 // open - подключение модулей и пространств имен для использования содержащихся в них

 //    значений, классов и других модулей.

 open
 System.Windows.Forms
 // - классы Form (окно), Button (кнопка)и т. д.

 // Beep - звуковой сигнал

 // В качестве аргументов beep передаются еще некоторые параметры, которые мы не используем

 let
 beep
 _
 =
 System
.
Console
.
Beep
()

 // создание окна с программным именем окно !необходимо вызывать слово-функцию отображения - к примеру Application.Run(окно)!

 // Visible - булевское значение, является ли окно видимым

 // TopMost - отображается ли окно на переднем плане (очерёдность окон с одинаковым значением в обратном порядке вызова)

 // Text - текст заголовка окна

 let
 window
 =
 new
 Form
(
Visible
=
true
,
TopMost
=
true
,
Text
=
""
,

                       Top
 =
 0
,
 Left
 =
 0
,
 Height
 =
 512
,
 Width
 =
 768
)

 window
.
WindowState
 <-
 FormWindowState
.
Normal
 // Нормальное (, Свёрнутое, Развёрнутое) окно. Просто для примера не внесено в конструктор

 window
.
ClientSizeChanged
.
Add
 beep

 window
.
KeyDown
.
Add
 beep

 window
.
KeyPress
.
Add
 beep

 window
.
KeyUp
.
Add
 beep

 Application
.
Run
 window
 // отображение окна

Факториал

[ править | править код ]

Рекурсивная функция вычисления факториала нативным способом:

let
 rec
 fac
 n
 =

    if
 n
 <
 2
 then
 1

    else
 n
 *
 fac
(
n
 -
 1
)

Рекурсивная функция вычисления факториала, оптимизированная под хвостовую рекурсию.

let
 factorial
 num
 =

    let
 rec
 fac
 num
 acc
 =

        match
 num
 with

        |
x
 when
 x
 <
 2
 ->
 acc

        |_
 ->
 fac
 (
num
 -
 1
)
 (
acc
 *
 num
)

    fac
 num
 1

Функция вычисления факториала, в императивном стиле с использованием изменяемого состояния.

let
 factorial
 num
 =

    if
 num
 <
 2
 then
 1

    else

        let
 mutable
 fac
 =
 1

        for
 i
 in
 [
2
..
num
]
 do

            fac
 <-
 fac
 *
 i

        fac

Функция вычисления факториала с использованием свертки списка и каррированой операции умножения:

let
 fac
 n
 =
 List
.
fold
 (*)
 1
 [
1
..
n
]

Рекурсивная функция вычисления чисел Фибоначчи с использованием метода сопоставления с образцом:

let
 rec
 fib
 n
 a
 b
 =

    match
 n
 with

        |
 0
 ->
 a

        |
 1
 ->
 b

        |
 _
 ->
 fib
 (
n
 -
 1
)
 b
 (
a
 +
 b
)

Примечания

[ править | править код ]
  1. Ссылки для загрузки F# на сайте Microsoft Research . Дата обращения: 24 июня 2011. Архивировано 24 июня 2011 года.
  2. Announcing the F# Compiler + Library Source Code Drop . Дата обращения: 5 ноября 2010. Архивировано 17 января 2013 года.

Литература

[ править | править код ]
  • Крис Смит (Smith, Chris). Программирование на F# = Programming F#. ? O'Reilly, 2011. ? 448 с. ? ISBN 978-5-93286-199-8 .
  • Дмитрий Сошников. Функциональное программирование на F#. ? Москва: ДМК Пресс, 2011. ? 192 с. ? ISBN 978-5-94074-689-8 .
  • Syme, Don; Granicz, Adam; Cisternino, Antonio. Expert F#. ? Apress, 2007.   (англ.)