Этот пост будет не о том, как «перевести» код с C# на F#: различные парадигмы делают каждый из этих языков лучшим для своего круга задач. Однако вы сможете оценить все достоинства функционального программирования быстрее, если не будете думать о переводе кода из одной парадигмы в другую. Настало время любопытных, пытливых и готовых изучать совершенно новые вещи. Давайте начнем!

Ранее, в посте «Почему вам следует использовать F#», мы рассказали, почему F# стоит попробовать прямо сейчас. Теперь мы разберем основы, необходимые для его успешного применения. Пост предназначен для людей, знакомых с C#, Java или другими объектно-ориентированными языками. Если вы уже пишете на F#, эти понятия должны быть вам хорошо знакомы.
Сразу к различиям
Перед тем, как приступить к изучению понятий функционального программирования, давайте посмотрим на небольшой пример и определим, в чем F# отличается от C#. Это базовый пример с двумя функциями и выводом результата на экран:
01. Что такое функция в математике
let square x = x * x let sumOfSquares n = [1..n] // Создадим список с элементами от 1 до n |> List.map square // Возведем в квадрат каждый элемент |> List.sum // Просуммируем их! printfn «Сумма квадратов первых 5 натуральных чисел равна %d» (sumOfSquares 5)
Обратите внимание, что здесь нет явного указания типов, отсутствуют точки с запятой или фигурные скобки. Скобки используются в единственном месте: для вызова функции sumOfSquares с числом 5 в качестве входного значения и последующего вывода результата на экран. Конвейерный оператор |> (pipeline operator) используется так же, как конвейеры (каналы, pipes) в Unix. square — это функция, которая напрямую передается в функцию List.map как параметр (функции в F# рассматриваются как значения, first-class functions).
Хотя различий на самом деле еще много, сперва стоит разобраться с фундаментальными вещами, поскольку они — ключ к пониманию F#.
C# и F#: Соответствие ключевых понятий
Следующая таблица показывает соответствия между некоторыми ключевыми понятиями C# и F#. Это умышленно короткое и неполное описание, но так его проще запомнить в начале изучения F#.
| Переменные | Неизменяемые значения | 
| Инструкции | Выражения | 
| Объекты с методами | Типы и функции | 
Быстрая шпаргалка по некоторым терминам:
- Переменные — это значения, которые могут меняться. Это следует из их названия!
- Неизменяемые значения — это значения, которые не могут быть изменены после присваивания.
- Инструкции — это команды, исполняемые после запуска программы.
- Выражения — это фрагменты кода, которые можно вычислить и получить значения.
- Типы — это классификация данных в программе.
Графический метод решения задачи линейного программирования (ЗЛП)
Стоит отметить, что все указанное в столбце C# так же возможно в F# (и довольно легко реализуется). В столбце F# также есть вещи, которые можно сделать в C#, хотя и намного сложнее. Следует упомянуть, что элементы в левом столбце не являются «плохими» в F#, и наоборот. Объекты с методами отлично подходят для использования в F# и часто являются лучшим решением в зависимости от вашей ситуации.
Неизменяемые значения вместо переменных
Одним из наиболее непривычных понятий в функциональном программировании является неизменяемость (иммутабельность, immutability). Ему часто уделяют недостаточно внимания в сообществе любителей функционального программирования. Но если вы никогда не использовали язык, в котором значения неизменяемы по умолчанию, это часто является первым и наиболее значимым препятствием для дальнейшего изучения. Неизменяемость является фундаментальным понятием практически во всех функциональных языках.
let x = 1
В предыдущем выражении значение 1 связано с именем x . В течение всего времени существования имя x теперь ссылается на значение 1 и не может быть изменено. Например, следующий код не может переназначить значение x :
let x = 1 x = x + 1 // Это выражение ничего не присваивает!
Неизменяемость существенным образом преобразует ваши привычные подходы к решению задач. Например, циклы for и другие базовые операции императивного программирования не так часто используются в F#.
Рассмотрим более конкретный пример: вы хотите возвести в квадрат числа из входного списка. Вот как это можно сделать в F#:
// Определим функцию, которая вычисляет квадрат значения let square x = x * x let getSquares items = items |> List.map square let lst = [ 1; 2; 3; 4; 5 ] // Создать список в F# printfn «Квадрат числа %A равен %A» lst (getSquares lst)
Заметим, что в этом примере нет цикла for . На концептуальном уровне это сильно отличается от императивного кода. Мы не возводим в квадрат каждый элемент списка.
Мы применяем функцию square к входному списку и получаем значения, возведенные в квадрат. Это очень тонкое различие, но на практике оно может приводить к значительно отличающемуся коду. Прежде всего, функция getSquares на самом деле создает полностью новый список.
Неизменяемость — это гораздо более широкая концепция, чем просто иной способ управления данными в списках. Понятие ссылочной прозрачности (Referential Transparency) естественно для F#, и оказывает значительное влияние, как на разработку систем, так и на то, как части этих систем сочетаются. Функциональные характеристики системы становятся более предсказуемыми, когда значения не изменяются, если вы этого не ожидаете.
Более того, когда значения неизменяемы, конкурентное программирование становится проще. Некоторые сложные проблемы, возникающие в С# из-за изменяемого состояния, в F# не встречаются вообще. F# не может волшебным образом решить все ваши проблемы с многопоточностью и асинхронностью, однако он сделает многие вещи проще.
Выражения вместо инструкций
Как было упомянуто ранее, F# использует выражения (expressions). Это контрастирует с C#, где практически для всего используются инструкции (statements). Различие между ними может казаться на первый взгляд незначительным, однако есть одна вещь, о которой следует помнить: выражения производят значения. Инструкции — нет.
// ‘getMessage’ — это функция, и `name` — ее входной параметр. let getMessage name = if name = «Phillip» then // ‘if’ — это выражение. «Hello, Phillip!» // Эта строка тоже является выражением. Оно возвращает значение else «Hello, other person!» // То же самое с этой строкой. let phillipMessage = getMessage «Phillip» // getMessage, при вызове, является выражением. Его значение связано с именем ‘phillipMessage’. let alfMessage = getMessage «Alf» // Это выражение связано с именем ‘alfMessage’!
В предыдущем примере вы можете увидеть несколько моментов, которые отличают F# от императивных языков вроде C#:
- if. then. else — это выражение, а не инструкция.
- Каждая ветка выражения if возвращает значение, которое в данном случае будет являться возвращаемым значением функции getMessage .
- Каждый вызов функции getMessage — это выражение, которое принимает строку и возвращает строку.
Этот подход сильно отличается от C#, но скорее всего он покажется вам естественным при написании кода на F#.
Если копнуть немного глубже, в F# даже инструкции описываются с помощью выражений. Такие выражения возвращают значение типа unit . unit немного похож на void в C#:
let names = [ «Alf»; «Vasily»; «Shreyans»; «Jin Sun»; «Moulaye» ] // Цикл `for`. Ключевое слово ‘do’ указывает, что выражение их внутренней области видимости должно иметь тип `unit`. // Если это не так, то результат выражения неявно игнорируется. for name in names do printfn «My name is %s» name // printfn возвращает unit.
В предыдущем примере с циклом for всё имеет тип unit . Выражения типа unit — это выражения, которые не имеют возвращаемого значения.
F#: Массивы, списки и последовательности
Предыдущие примеры кода использовали массивы и списки F#. В данном разделе разъясняются некоторые подробности.
F# предоставляет несколько типов коллекций и самые распространенные из них — это массивы, списки и последовательности.
- Массивы в F# — это массивы .NET. Они изменяемы — хранимые значения могут быть перезаписаны на месте. Они вычисляются энергично (eagerly).
- Списки в F# — это неизменяемые односвязные списки. Они могут быть использованы в виде шаблонов списков для сопоставлением с образом (pattern matching) в F#. Они вычисляются энергично.
- Последовательности в F# является неизменяемыми IEnumerable . Они вычисляются лениво.
Массивы, списки и последовательности в F# также имеют особый синтаксис для выражений. Это очень удобно для различных задач, когда нужно генерировать данные программно.
// Создадим список квадратов первых 100 натуральных чисел let first100Squares = [ for x in 1..100 -> x * x ] // То же самое, но массив! let first100SquaresArray = [| for x in 1..100 -> x * x |] // Функция, которая генерирует бесконечную последовательность нечетных чисел // // Вызывать вместе с Seq.take! let odds = let rec loop x = // Использует рекурсивную локальную функцию seq < yield x yield! loop (x + 2) >loop 1 printfn «Первые 3 нечетных числа: %A» (Seq.take 3 odds) // Вывод: «Первые 3 нечетных числа: seq [1; 3; 5]
Соответствие между функциями F# и методами LINQ
Если вы знакомы с методами LINQ, следующая таблица поможет вам понять аналогичные функции в F#.
| Where | filter | 
| Select | map | 
| GroupBy | groupBy | 
| SelectMany | collect | 
| Aggregate | fold или reduce | 
| Sum | sum | 
Вы также можете заметить, что такой же набор функций существует для модулей Seq , List и Array . Функции модуля Seq могут быть использованы для последовательностей, списков или массивов. Функции для массивов и списков могут быть использованы только для массивов и списков в F# соответственно. Также последовательности в F# ленивые, а списки и массивы — энергичные. Использование функций модуля Seq на списках или массивах влечет за собой ленивое вычисление, а тип возвращаемого значения будет последовательностью.
Предыдущий раздел содержит в себе довольно много информации, но по мере написания программ на F# она станет интуитивно понятной.
Функциональные конвейеры
Вы могли заметить, что оператор |> используется в предыдущих примерах кода. Он очень похож на конвейеры в unix: принимает что-то слева от себя и передает на вход чему-то справа. Этот оператор (называется «pipe» или «pipeline») используется для создания функциональных конвейеров. Вот пример:
let square x = x * x let isOdd x = x % 2 <> 0 let getOddSquares items = items |> Seq.filter isOdd |> Seq.map square
В данном примере сначала items передается на вход функции Seq.filter . Затем возвращаемое значение Seq.filter (последовательность) передается на вход функции Seq.map . Результат выполнения Seq.map является выходным значением функции getOddSquares .
Конвейерный оператор очень удобно использовать, поэтому редко кто обходится без него. Возможно, это одна из самых любимых возможностей F#!
F#: типы
Поскольку F# — язык платформы .NET, в нем существуют те же примитивные типы, что и C#: string , int и так далее. Он использует объекты .NET и поддерживает четыре основных столпа объектно-ориентированного программирования. F# предоставляет кортежи (tuples), а также два основных типа, которые отсутствуют в C#: записи (records) и размеченные объединения (discriminated unions).
Запись — это группа упорядоченных именованных значений, которая автоматически реализует операцию сравнения — в самом буквальном смысле. Не нужно задумываться о том, как происходит сравнение: через равенство ссылок или с помощью пользовательского определения равенства между двумя объектами. Записи — это значения, а значения можно сравнивать. Они являются типами-произведениями, если говорить на языке теории категорий. У них есть множество применений, однако одно из самых очевидных — их можно использовать в качестве POCO или POJO.
open System // Вот так вы можете определить тип-запись. // Можно располагать метки на новых строках type Person = < Name: string Age: int Birth: DateTime >// Создать новую запись `Person` можно примерно так. // Если метки расположены на одной строке, они разделяются точкой с запятой let p1 = < Name=»Charles»; Age=27; Birth=DateTime(1990, 1, 1) >// Или же можно располагать метки на новых строках let p2 = < Name=»Moulaye» Age=22 Birth=DateTime(1995, 1, 1) >// Записи можно сравнивать на равенство. Не нужно определять метод Equals() и GetHasCode(). printfn «Они равны? %b» (p1 = p2) // Это выведет `false`, потому что они не равны.
Другой основной тип в F# — это размеченные объединения, или РО, или DU в англоязычной литературе. РО — это типы, представляющие некоторое количество именованных вариантов. На языке теории категорий это называется типом-суммой. Они также могут быть определены рекурсивно, что значительно упрощает описание иерархических данных.
// Определим обобщенное бинарное дерево поиска. // // Заметим, что обобщенный тип-параметр имеет ‘ в начале. type BST = | Empty | Node of ‘T * BST * BST // Каждый узел имеет левый и правый BST // Развернем BST с помощью сопоставления с образцом! let rec flip bst = match bst with | Empty -> bst | Node(item, left, right) -> Node(item, flip right, flip left) // Определим пример BST let tree = Node(10, Node(3, Empty, Node(6, Empty, Empty)), Node(55, Node(16, Empty, Empty), Empty)) // Развернем его! printfn «%A» (flip tree)
Тадам! Вооружившись мощью размеченных объединений и F#, вы можете пройти любое собеседование, в котором требуется развернуть бинарное дерево поиска.
Наверняка вы увидели странный синтаксис в определении варианта Node . Это на самом деле сигнатура кортежа. Это означает, что определенное нами BST может быть или пустым, или являться кортежем (значение, левое поддерево, правое поддерево) . Более подробно про это написано в разделе о сигнатурах.
Собираем всё вместе: синтаксис F# за 60 секунд
Следующий пример кода представлен с разрешения Скотта Влашина, героя сообщества F#, написавшего этот прекрасный обзор F# синтаксиса. Вы прочтете его примерно за минуту. Пример был немного отредактирован.
В дополнение, в нашей официальной документации для .NET и поддерживаемых языков есть материал «Тур по F#».
Что делать дальше
Всё описанное в данном посте — лишь поверхностные возможности F#. Мы надеемся, что после прочтения этой статьи вы сможете погрузиться в F# и функциональное программирование. Вот несколько примеров того, что можно написать в качестве упражнения для дальнейшего изучения F#:
- Используйте F#, чтобы построить прекрасные фрактальные деревья.
- Используйте F# для исследования и анализа данных о вселенной Симпсонов в Azure Notebooks.
- Используйте F# и Suave для разработки веб-приложений.
- Используйте F# при разработке serverless-приложений и компонентов с помощью Azure Functions.
Есть очень много других задач, для которых можно использовать F#; предыдущий список ни в коем случае не является исчерпывающим. F# используется в различных приложениях: от простых скриптов для сборки до бэкенда интернет-магазинов с миллиардной выручкой. Нет никаких ограничений по проектам, для которых вы можете использовать F#.
Дополнительные ресурсы
Для F# существует множество самоучителей, включая материалы для тех, кто пришел с опытом C# или Java. Следующие ссылки могут быть полезными по мере того, как вы будете глубже изучать F#:
Также описаны еще несколько способов, как начать изучение F#.
И наконец, сообщество F# очень дружелюбно к начинающим. Есть очень активный чат в Slack, поддерживаемый F# Software Foundation, с комнатами для начинающих, к которым вы можете свободно присоединиться. Рекомендуем вам это сделать!
Не забудьте посетить сайт русскоязычного сообщества F#! Если у вас возникнут вопросы по изучению языка, мы будем рады обсудить их в чатах:
- комната #ru_general в Slack-чате F# Software Foundation
- чат в Telegram
- чат в Gitter
Об авторах перевода
Источник: habr.com
Погружение в F#. Пособие для C#-разработчиков

2017-08-14 в 7:54, admin , рубрики: .net, C#, F#, fsharplangru, microsoft, microsoft research, Mono и Moonlight, Блог компании Microsoft, Программирование
Этот пост будет не о том, как «перевести» код с C# на F#: различные парадигмы делают каждый из этих языков лучшим для своего круга задач. Однако вы сможете оценить все достоинства функционального программирования быстрее, если не будете думать о переводе кода из одной парадигмы в другую. Настало время любопытных, пытливых и готовых изучать совершенно новые вещи. Давайте начнем!
 
Ранее, в посте «Почему вам следует использовать F#», мы рассказали, почему F# стоит попробовать прямо сейчас. Теперь мы разберем основы, необходимые для его успешного применения. Пост предназначен для людей, знакомых с C#, Java или другими объектно-ориентированными языками. Если вы уже пишете на F#, эти понятия должны быть вам хорошо знакомы.
Сразу к различиям
Перед тем, как приступить к изучению понятий функционального программирования, давайте посмотрим на небольшой пример и определим, в чем F# отличается от C#. Это базовый пример с двумя функциями и выводом результата на экран:
let square x = x * x let sumOfSquares n = [1..n] // Создадим список с элементами от 1 до n |> List.map square // Возведем в квадрат каждый элемент |> List.sum // Просуммируем их! printfn «Сумма квадратов первых 5 натуральных чисел равна %d» (sumOfSquares 5)
Обратите внимание, что здесь нет явного указания типов, отсутствуют точки с запятой или фигурные скобки. Скобки используются в единственном месте: для вызова функции sumOfSquares с числом 5 в качестве входного значения и последующего вывода результата на экран. Конвейерный оператор |> (pipeline operator) используется так же, как конвейеры (каналы, pipes) в Unix. square — это функция, которая напрямую передается в функцию List.map как параметр (функции в F# рассматриваются как значения, first-class functions).
Хотя различий на самом деле еще много, сперва стоит разобраться с фундаментальными вещами, поскольку они — ключ к пониманию F#.
C# и F#: Соответствие ключевых понятий
Следующая таблица показывает соответствия между некоторыми ключевыми понятиями C# и F#. Это умышленно короткое и неполное описание, но так его проще запомнить в начале изучения F#.
| Переменные | Неизменяемые значения | 
| Инструкции | Выражения | 
| Объекты с методами | Типы и функции | 
Быстрая шпаргалка по некоторым терминам:
- Переменные — это значения, которые могут меняться. Это следует из их названия!
- Неизменяемые значения — это значения, которые не могут быть изменены после присваивания.
- Инструкции — это команды, исполняемые после запуска программы.
- Выражения — это фрагменты кода, которые можно вычислить и получить значения.
- Типы — это классификация данных в программе.
Стоит отметить, что все указанное в столбце C# так же возможно в F# (и довольно легко реализуется). В столбце F# также есть вещи, которые можно сделать в C#, хотя и намного сложнее. Следует упомянуть, что элементы в левом столбце не являются «плохими» в F#, и наоборот. Объекты с методами отлично подходят для использования в F# и часто являются лучшим решением в зависимости от вашей ситуации.
Неизменяемые значения вместо переменных
Одним из наиболее непривычных понятий в функциональном программировании является неизменяемость (иммутабельность, immutability). Ему часто уделяют недостаточно внимания в сообществе любителей функционального программирования. Но если вы никогда не использовали язык, в котором значения неизменяемы по умолчанию, это часто является первым и наиболее значимым препятствием для дальнейшего изучения. Неизменяемость является фундаментальным понятием практически во всех функциональных языках.
let x = 1
В предыдущем выражении значение 1 связано с именем x . В течение всего времени существования имя x теперь ссылается на значение 1 и не может быть изменено. Например, следующий код не может переназначить значение x :
let x = 1 x = x + 1 // Это выражение ничего не присваивает!
Неизменяемость существенным образом преобразует ваши привычные подходы к решению задач. Например, циклы for и другие базовые операции императивного программирования не так часто используются в F#.
Рассмотрим более конкретный пример: вы хотите возвести в квадрат числа из входного списка. Вот как это можно сделать в F#:
// Определим функцию, которая вычисляет квадрат значения let square x = x * x let getSquares items = items |> List.map square let lst = [ 1; 2; 3; 4; 5 ] // Создать список в F# printfn «Квадрат числа %A равен %A» lst (getSquares lst)
Заметим, что в этом примере нет цикла for . На концептуальном уровне это сильно отличается от императивного кода. Мы не возводим в квадрат каждый элемент списка.
Мы применяем функцию square к входному списку и получаем значения, возведенные в квадрат. Это очень тонкое различие, но на практике оно может приводить к значительно отличающемуся коду. Прежде всего, функция getSquares на самом деле создает полностью новый список.
Неизменяемость — это гораздо более широкая концепция, чем просто иной способ управления данными в списках. Понятие ссылочной прозрачности (Referential Transparency) естественно для F#, и оказывает значительное влияние, как на разработку систем, так и на то, как части этих систем сочетаются. Функциональные характеристики системы становятся более предсказуемыми, когда значения не изменяются, если вы этого не ожидаете.
Более того, когда значения неизменяемы, конкурентное программирование становится проще. Некоторые сложные проблемы, возникающие в С# из-за изменяемого состояния, в F# не встречаются вообще. F# не может волшебным образом решить все ваши проблемы с многопоточностью и асинхронностью, однако он сделает многие вещи проще.
Выражения вместо инструкций
Как было упомянуто ранее, F# использует выражения (expressions). Это контрастирует с C#, где практически для всего используются инструкции (statements). Различие между ними может казаться на первый взгляд незначительным, однако есть одна вещь, о которой следует помнить: выражения производят значения. Инструкции — нет.
// ‘getMessage’ — это функция, и `name` — ее входной параметр. let getMessage name = if name = «Phillip» then // ‘if’ — это выражение. «Hello, Phillip!» // Эта строка тоже является выражением. Оно возвращает значение else «Hello, other person!» // То же самое с этой строкой. let phillipMessage = getMessage «Phillip» // getMessage, при вызове, является выражением. Его значение связано с именем ‘phillipMessage’. let alfMessage = getMessage «Alf» // Это выражение связано с именем ‘alfMessage’!
В предыдущем примере вы можете увидеть несколько моментов, которые отличают F# от императивных языков вроде C#:
- if. then. else — это выражение, а не инструкция.
- Каждая ветка выражения if возвращает значение, которое в данном случае будет являться возвращаемым значением функции getMessage .
- Каждый вызов функции getMessage — это выражение, которое принимает строку и возвращает строку.
Этот подход сильно отличается от C#, но скорее всего он покажется вам естественным при написании кода на F#.
Если копнуть немного глубже, в F# даже инструкции описываются с помощью выражений. Такие выражения возвращают значение типа unit . unit немного похож на void в C#:
let names = [ «Alf»; «Vasily»; «Shreyans»; «Jin Sun»; «Moulaye» ] // Цикл `for`. Ключевое слово ‘do’ указывает, что выражение их внутренней области видимости должно иметь тип `unit`. // Если это не так, то результат выражения неявно игнорируется. for name in names do printfn «My name is %s» name // printfn возвращает unit.
В предыдущем примере с циклом for всё имеет тип unit . Выражения типа unit — это выражения, которые не имеют возвращаемого значения.
F#: Массивы, списки и последовательности
Предыдущие примеры кода использовали массивы и списки F#. В данном разделе разъясняются некоторые подробности.
F# предоставляет несколько типов коллекций и самые распространенные из них — это массивы, списки и последовательности.
- Массивы в F# — это массивы .NET. Они изменяемы — хранимые значения могут быть перезаписаны на месте. Они вычисляются энергично (eagerly).
- Списки в F# — это неизменяемые односвязные списки. Они могут быть использованы в виде шаблонов списков для сопоставлением с образом (pattern matching) в F#. Они вычисляются энергично.
- Последовательности в F# является неизменяемыми IEnumerable . Они вычисляются лениво.
Массивы, списки и последовательности в F# также имеют особый синтаксис для выражений. Это очень удобно для различных задач, когда нужно генерировать данные программно.
// Создадим список квадратов первых 100 натуральных чисел let first100Squares = [ for x in 1..100 -> x * x ] // То же самое, но массив! let first100SquaresArray = [| for x in 1..100 -> x * x |] // Функция, которая генерирует бесконечную последовательность нечетных чисел // // Вызывать вместе с Seq.take! let odds = let rec loop x = // Использует рекурсивную локальную функцию seq < yield x yield! loop (x + 2) >loop 1 printfn «Первые 3 нечетных числа: %A» (Seq.take 3 odds) // Вывод: «Первые 3 нечетных числа: seq [1; 3; 5]
Соответствие между функциями F# и методами LINQ
Если вы знакомы с методами LINQ, следующая таблица поможет вам понять аналогичные функции в F#.
| Where | filter | 
| Select | map | 
| GroupBy | groupBy | 
| SelectMany | collect | 
| Aggregate | fold или reduce | 
| Sum | sum | 
Вы также можете заметить, что такой же набор функций существует для модулей Seq , List и Array . Функции модуля Seq могут быть использованы для последовательностей, списков или массивов. Функции для массивов и списков могут быть использованы только для массивов и списков в F# соответственно. Также последовательности в F# ленивые, а списки и массивы — энергичные. Использование функций модуля Seq на списках или массивах влечет за собой ленивое вычисление, а тип возвращаемого значения будет последовательностью.
Предыдущий раздел содержит в себе довольно много информации, но по мере написания программ на F# она станет интуитивно понятной.
Функциональные конвейеры
Вы могли заметить, что оператор |> используется в предыдущих примерах кода. Он очень похож на конвейеры в unix: принимает что-то слева от себя и передает на вход чему-то справа. Этот оператор (называется «pipe» или «pipeline») используется для создания функциональных конвейеров. Вот пример:
let square x = x * x let isOdd x = x % 2 <> 0 let getOddSquares items = items |> Seq.filter isOdd |> Seq.map square
В данном примере сначала items передается на вход функции Seq.filter . Затем возвращаемое значение Seq.filter (последовательность) передается на вход функции Seq.map . Результат выполнения Seq.map является выходным значением функции getOddSquares .
Конвейерный оператор очень удобно использовать, поэтому редко кто обходится без него. Возможно, это одна из самых любимых возможностей F#!
F#: типы
Поскольку F# — язык платформы .NET, в нем существуют те же примитивные типы, что и C#: string , int и так далее. Он использует объекты .NET и поддерживает четыре основных столпа объектно-ориентированного программирования. F# предоставляет кортежи (tuples), а также два основных типа, которые отсутствуют в C#: записи (records) и размеченные объединения (discriminated unions).
Запись — это группа упорядоченных именованных значений, которая автоматически реализует операцию сравнения — в самом буквальном смысле. Не нужно задумываться о том, как происходит сравнение: через равенство ссылок или с помощью пользовательского определения равенства между двумя объектами. Записи — это значения, а значения можно сравнивать. Они являются типами-произведениями, если говорить на языке теории категорий. У них есть множество применений, однако одно из самых очевидных — их можно использовать в качестве POCO или POJO.
open System // Вот так вы можете определить тип-запись. // Можно располагать метки на новых строках type Person = < Name: string Age: int Birth: DateTime >// Создать новую запись `Person` можно примерно так. // Если метки расположены на одной строке, они разделяются точкой с запятой let p1 = < Name=»Charles»; Age=27; Birth=DateTime(1990, 1, 1) >// Или же можно располагать метки на новых строках let p2 = < Name=»Moulaye» Age=22 Birth=DateTime(1995, 1, 1) >// Записи можно сравнивать на равенство. Не нужно определять метод Equals() и GetHasCode(). printfn «Они равны? %b» (p1 = p2) // Это выведет `false`, потому что они не равны.
Другой основной тип в F# — это размеченные объединения, или РО, или DU в англоязычной литературе. РО — это типы, представляющие некоторое количество именованных вариантов. На языке теории категорий это называется типом-суммой. Они также могут быть определены рекурсивно, что значительно упрощает описание иерархических данных.
// Определим обобщенное бинарное дерево поиска. // // Заметим, что обобщенный тип-параметр имеет ‘ в начале. type BST = | Empty | Node of ‘T * BST * BST // Каждый узел имеет левый и правый BST // Развернем BST с помощью сопоставления с образцом! let rec flip bst = match bst with | Empty -> bst | Node(item, left, right) -> Node(item, flip right, flip left) // Определим пример BST let tree = Node(10, Node(3, Empty, Node(6, Empty, Empty)), Node(55, Node(16, Empty, Empty), Empty)) // Развернем его! printfn «%A» (flip tree)
Тадам! Вооружившись мощью размеченных объединений и F#, вы можете пройти любое собеседование, в котором требуется развернуть бинарное дерево поиска.
Наверняка вы увидели странный синтаксис в определении варианта Node . Это на самом деле сигнатура кортежа. Это означает, что определенное нами BST может быть или пустым, или являться кортежем (значение, левое поддерево, правое поддерево) . Более подробно про это написано в разделе о сигнатурах.
Собираем всё вместе: синтаксис F# за 60 секунд
Следующий пример кода представлен с разрешения Скотта Влашина, героя сообщества F#, написавшего этот прекрасный обзор F# синтаксиса. Вы прочтете его примерно за минуту. Пример был немного отредактирован.
В дополнение, в нашей официальной документации для .NET и поддерживаемых языков есть материал «Тур по F#».
Что делать дальше
Всё описанное в данном посте — лишь поверхностные возможности F#. Мы надеемся, что после прочтения этой статьи вы сможете погрузиться в F# и функциональное программирование. Вот несколько примеров того, что можно написать в качестве упражнения для дальнейшего изучения F#:
- Используйте F#, чтобы построить прекрасные фрактальные деревья.
- Используйте F# для исследования и анализа данных о вселенной Симпсонов в Azure Notebooks.
- Используйте F# и Suave для разработки веб-приложений.
- Используйте F# при разработке serverless-приложений и компонентов с помощью Azure Functions.
Есть очень много других задач, для которых можно использовать F#; предыдущий список ни в коем случае не является исчерпывающим. F# используется в различных приложениях: от простых скриптов для сборки до бэкенда интернет-магазинов с миллиардной выручкой. Нет никаких ограничений по проектам, для которых вы можете использовать F#.
Дополнительные ресурсы
Для F# существует множество самоучителей, включая материалы для тех, кто пришел с опытом C# или Java. Следующие ссылки могут быть полезными по мере того, как вы будете глубже изучать F#:
- F# Guide
- F# for Fun and Profit
- F# Wiki
- Learn X in Y Minutes: F#
Также описаны еще несколько способов, как начать изучение F#.
И наконец, сообщество F# очень дружелюбно к начинающим. Есть очень активный чат в Slack, поддерживаемый F# Software Foundation, с комнатами для начинающих, к которым вы можете свободно присоединиться. Рекомендуем вам это сделать!
Не забудьте посетить сайт русскоязычного сообщества F#! Если у вас возникнут вопросы по изучению языка, мы будем рады обсудить их в чатах:
- комната #ru_general в Slack-чате F# Software Foundation
- чат в Telegram
- чат в Gitter
Об авторах перевода
Источник: www.pvsm.ru
Язык программирования F#

Собственные идентификаторы необходимы для доступа к членам текущего экземпляра класса. В F# определено два вида собственных идентификаторов: для всего определения класса и только для одного члена класса. Само-идентификаторы класса неразрывно связаны с конструкторами классов.
Анимация движения символов в консоли
Все исходники / Язык программирования F#
Движок анимации построен на событиях таймера. На основе исходника можно писать забавные программы. Функция определения актуальной позиции символов класса Game позволяет создавать разнообразные по логике игры.
Классы F#. Свойства
Все исходники / Язык программирования F#

Свойства синтаксически похожи на открытое поле класса, но внутри свойства может быть программный код любой сложности. Свойства класса упрощают написание кода. Примеры кода явных, автоматических и статических свойств класса F#.
Игра Змейка на F#
Все исходники / Язык программирования F#

Исходный код консольной игры «Змейка». Простые текстовые символы копируют изящный стиль передвижения настоящей змеи. В исходнике уже всё готово для модификации игры под свои фантазии. Два класса и три модуля F#.
Классы F#. Конструкторы
Все исходники / Язык программирования F#

Первичные, вторичные и статические конструкторы классов F#. Первичный конструктор является обязательным и всегда вызывается другими локальными конструкторами. Обязательные и необязательные параметры конструкторов. Инициализация свойств класса в конструкторе экземпляра.
WinForms F# — интерактивное приложение
Все исходники / Язык программирования F#

Приложение Windows Forms на языке F#. Программный код разделён на логические модули. Для интерактивности добавлены простые функции на F#. На основе данного исходника можно создавать вполне работоспособные приложения на языке F#.
Источник: www.interestprograms.ru
