Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторный_практикум.doc
Скачиваний:
74
Добавлен:
15.11.2019
Размер:
45.35 Mб
Скачать

2. Основные положения при работе с f#

Исполняемый файл Fsi.exe, входящий в комплект поставки F#, представляет собой интерактивную консоль, в которой можно быстро проверить работоспособность отдельных фрагментов кода на F#. После установки среды разработки расположен по адресу (для Windows 7 64-бит): C:\Program Files (x86)\Microsoft F#\v4.0\Fsi.exe

Рис. 2. 1. Fsi.exe

В состав современных инсталляционных пакетов F# входят также модули интеграции в Visual Studio 2008 и свободно распространяемую Visual Studio 2008 Shell, которые позволяют компилировать участки кода прямо из редактора кода. Открыв текст программы во встроенном редакторе кода, можно отправлять выделенные участки на исполнение простым нажатием комбинации клавиш Alt+Enter.

Исполняемый файл Fsc.exe — непосредственно компилятор исходного кода F#, который можно использовать совместно со своим любимым текстовым редактором. Расположен в той же директории что и Fsi.exe.

Утилиты fsc.exe и fsi.exe отлично работают и под Mono, открытой реализацией .NET Framework.

Файлы, содержащие код на F#, обычно имеют следующие расширения:

*.fs — обычный файл с кодом, который может быть скомпилирован;

*.fsi — файл описания публичного интерфейса модуля. Обычно генерируется компилятором на основе кода, а затем редактируется вручную;

*.fsx — исполняемый скрипт. Может быть запущен прямо из проводника Windows при помощи соответствующего пункта всплывающего меню или передан на исполнение в интерактивную консоль Fsi.exe.

Иногда можно встретить в начале F# кода директиву #light on. Эта директива отключает режим совместимости синтаксиса с OCaml, делая отступы в коде значимыми (как, например в Python или Haskell). В последних версиях языка облегчённый режим включен по умолчанию, поэтому необходимости в указании директивы #light больше нет.

Также для F# не поддерживается режим конструктора страниц ASP.NET. Это не означает, что F# нельзя использовать вместе с ASP.NET — отнюдь. Просто в Visual Studio, работая с F#, нельзя без дополнительных средств перетаскивать элементы управления, как при работе с C# или Visual Basic.

3. Создание приложения «Учебник по f#»

После запуска Visual Studio 2010, откроется Начальная страница:

Рис. 3. 1. Начальная страница Visual Studio 2010 Professional (русская версия)

Для начала, надо создать пустой проект, для этого выполним последовательно: Файл -> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N или пункт «Создать проект…» на Начальной странице):

Рис. 3. 2. Создание нового проекта

Откроется окно создания проекта и выбора необходимых нам параметров.

Выберем слева в пункте Установленные шаблоны -> Другие языки -> Visual F#, далее найдём в списке Учебник по F#. Также здесь можно выбрать, какой использовать «фреймворк» (набора компонентов для написания программ). В нашем случае выберем .NET Framework 4.

Рис. 3. 3. Окно создания нового проекта

В поле Имя вводим LWP21-Tutorial это название программы (выбрано по названию лабораторного практикума, номеру и названию работы). В поле Расположение указана конечная директория, где будет находиться весь проект (значение «по умолчанию» можно поменять, выполнив действия: Сервис -> Параметры… -> Проекты и решения -> меняем путь в поле Размещение проектов). Выберем расположение удобное для быстрого поиска. В поле Имя решения вводится либо название программы «по умолчанию» из поля Имя автоматически, либо можно ввести своё собственное. Под этим именем будет создана конечная папка проекта (если Имя и Имя решения разные).

Рис. 3. 4. Вводим данные нового проекта «Учебник по F#»

После нажатия клавиши ОК мы увидим сформированный проект и исходный код консольного приложения (не пустого изначально).

Рис. 3. 5. Исходный код консольного приложения сформированного средой разработки

Как видим, среда разработки сформировала один файл Tutorial.fs с исходным кодом. В самом конце кода вставим следующую строчку:

let c = Console.ReadKey()

Без точки с запятой как в C#... Код служит «паузой» для окна консоли после компиляции.

Теперь, можно откомпилировать созданную программу, нажав клавишу F5 или Отладка -> Начать отладку. Если программист хочет запустить программу без отладки и уверен что программа не нуждается в поиске ошибок и оптимизации кода, то можно нажать Отладка -> Запуск без отладки.

По умолчанию клавиша отладки вынесена на панель инструментов вверху. Запускаем приложение в режиме отладки (и компиляции debug-версии программы) нажав на иконку (Debug выбрано изначально).

Рис. 3. 5. Запуск приложения «Учебник по F#» по конфигурации Debug

Но компиляция в случае данного приложения не нужна. Весь файл содержит примеры для изучения F#. В файле собрано достаточное количество различных примеров с использование оператора let. Более подробно о том, что делает та или иная строчка кода можно прочитать из русскоязычных комментариев (если их нет, взять код с комментариями из Приложения № 1 к данной лабораторной работе).

Для получения промежуточных результатов работы кода можно воспользоваться комбинацией: выделение участка кода + Alt+Enter.

Найдём и выделим мышкой следующий код:

/// Очень простое константное целое

let int1 = 1

/// Другое очень простое константное целое

let int2 = 2

/// Добавление двух целых

let int3 = int1 + int2

После выделения жмём «альт» + «энтер» и...:

Рис 3. 6. Результат выполнения выделенного участка кода в окне F# Interactive

Как видим, здесь сработала встроенная интерактивная консоль Fsi.exe. Выделяем и выполняем другие участки кода и смотрим на результаты. Код в принципе, как уже был сказано, достаточно хорошо прокомментирован, потому в пояснениях не нуждается.

Наполним наш «учебник» новыми «уроками».

Перед вызовом функции «паузы» окна консоли добавим код:

let results = [ for i in 0 .. 100 -> (i, i*i) ]

printfn "\n\tРезультаты работы цикла и операции с шагом цикла i: i*i = \n\n%A" results

На первый взгляд этот цикл for кажется совершенно безобидным, но в действительности простым его никак не назовешь. Официально это создаваемый список (так своеобразно именуется часть кода, результатом работы которого является список).

Список — это примитив, часто встречающийся в функциональных языках. Во многих отношениях список схож с массивом. Разница состоит в том, что список не позволяет получать доступ к отдельному элементу на основании его позиции в списке (как традиционное выражение a[i] в C#). В функциональном программировании списки можно встретить в самых различных ситуациях. По большей части их можно считать эквивалентом List<T> в .NET Framework с несколько расширенными возможностями.

Список всегда относится к какому-то определенному типу. В нашем примере идентификатор порождает список кортежей, а точнее кортежей, относимых языком F# к типу (int * int). Список кортежей можно представить в виде пары столбцов, возвращаемых оператором SELECT в SQL. Наш код создаёт список, содержащий 100 пар целых чисел.

Ни одно введение в язык программирования не обходится без программы «Hello, World». F# не станет исключением.

После цикла добавим код:

printf "Hello World!"

open System.Windows.Forms

let mb = MessageBox.Show("Привет мир и F#!", "Учебник по F# (F#) :: Работа с MessageBox")

F# полностью поддерживает связь и взаимодействие с нижележащей платформой CLR — в том числе и библиотеки Windows Forms. Но для работы кода выше потребуется добавить в приложение библиотеку. В обозревателе решений выделим Ссылки и нажмём правую кнопку мыши, далее в открывшемся окне на вкладке .NET найдём System.Windows.Forms, далее ОК.

Рис. 3. 7. Добавление ссылки: System.Windows.Forms

Компилируем приложение (Debug) и запускаем. Видим следующее:

Рис. 3. 8. Результат выполнения новых участков кода в приложении «Учебник по F#»

Оператор let. Это вообще самый важный оператор. Формально let присваивает идентификатору значение. «Определяет переменную», но это было бы неверно. Идентификаторы в F# имеют природу двойственную. Во-первых, однажды определенный идентификатор может так никогда и не измениться. Именно этот момент позволяет писать программы, не нарушающие параллельность обработки: изменение состояния язык F# не приветствует. Во-вторых, идентификатор может относиться не только к примитивному или объектному типу, как в C# и Visual Basic, но и к функциональному типу, подобному тем, что встречаются в LINQ.

Обратим внимание ещё и на то, что явно тип идентификатора никогда не указывается. Идентификатор результата, к примеру, никогда не определяется — он выводится из правой части выражения, следующего за ним. Эта функция известна как вывод типа. Она свидетельствует о способности компилятора проанализировать код, определить возвращаемое значение и автоматически его использовать. Аналогичным образом действуют новые выражения с выводимым типом в C#, содержащие ключевое слово var.

Оператор let работает не только с данными. Его можно применять для определения функций, которые в F# являются понятиями первостепенными:

let add a b =

a + b

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

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

let add a b = a + b

let return10 _ = add 5 5

let ten = return10 12

printf "\n\nДесять = %d\n" ten

Результат:

Как и в большинстве функциональных языков, в F# функцию можно определить частично — в расчёте на то, что недостающие параметры будут переданы при вызове:

let add5 a =

add a 5

В определённой степени это напоминает создание перегруженного метода, который получает другой набор параметров и вызывает ещё один метод (код C#):

public class Adders {

public static int add(int a, int b) { return a + b; }

public static int add5(int a) { return add(a, 5); }

}

Но есть небольшая разница. Обратим внимание на то, что в F# типы явным образом не определяются. Это означает, что компилятор проделывает всю работу по выводу типов, определяет, совместим ли параметр функции add5 по типу с возможностью сложения с целочисленным литералом 5, и либо проводит компиляцию, либо сообщает об ошибке. В действительности в F# чаще всего имеет место неявная параметризация по типам (то есть используются общие типы).

Если в Visual Studio навести указатель мыши на определение числа десять, приведенное выше, мы увидим, что его тип объявляется следующим образом:

На языке F# это означает, что десять — это значение, функция, принимающая один параметр любого типа, и возвращающая целочисленный результат. Синатксис с «галочкой» — это грубый эквивалент синтаксиса <T> в C#, так что в наилучшем переводе на язык функций C# можно было бы сказать, что десять — это экземпляр делегата метода с параметризацией типов, тип которого в действительности лучше просто игнорировать (только по правилам C# это невозможно):

delegate int Transformer<T>(T ignored);

public class App

{

public static int return10(object ignored) { return 5 + 5; }

static void Main()

{

Transformer<object> ten = return10;

System.Console.WriteLine("\n\nДесять = {0}", return10(0));

}

}

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

let compute2 x = (x, x*x)

let compute3 x = (x, x*x, x*x*x)

let results2 = [ for i in 0 .. 10 -> compute2 i ]

let results3 = [ for i in 0 .. 10 -> compute3 i ]

printfn "\n\tРезультаты работы цикла и операции с шагом цикла i: i*i = \n\n%A" results2

printfn "\n\tРезультаты работы цикла и операции с шагом цикла i: i*i, i*i*i = \n\n%A" results3

Поэлементный просмотр списка (или массива, или любой другой схожей конструкции) настолько распространен в функциональных языках, что его свели к единому базовому методу List.iter. Он просто вызывает функцию для каждого элемента списка. Весьма полезны и другие схожие библиотечные функции. Например, метод List.map принимает в качестве аргумента функцию и применяет её к каждому элементу списка, создавая, таким образом, новый список.

Немного об асинхронном выполнении функций. Вставим следующий код:

open System.Threading

printf "\n"

let printWithThread str =

printfn "[ID потока = %d] %s" Thread.CurrentThread.ManagedThreadId str

let evals =

let z = 1.0

[ async { do printWithThread "Вычисляем z*z\n"

return z + z };

async { do printWithThread "Вычисляем sin(z)\n"

return (sin z) };

async { do printWithThread "Вычисляем log(z)\n"

return (log z) } ]

let awr =

async { let! vs = Async.Parallel evals

do printWithThread "Вычисляем v1 + v2 + v3\n"

return (Array.fold (fun a b -> a + b) 0.0 vs) }

let R = Async.RunSynchronously awr

printf "Результат вычислений = %f\n" R

В нём показана работа асинхронных рабочих потоков в упрощенном, удобоваримом виде. Если не вдаваться в подробности, evals является массивом функций, которые должны быть выполнены. Каждая из них помещается в очередь на выполнение путем вызова Async.Parallel. При выполнении становится ясно, что функции, входящие в массив evals, фактически находятся в отдельных потоках, идущих от функции в awr. Хотя, по причине природы пула потоков .NET, часть функций из массива evals или даже все функции могут выполняться в одном потоке, что заметно при выводе результата: части некоторых строк при печати окажутся не в том месте.

Факт выполнения функций в пуле потоков .NET ещё раз подтверждает отличную приспособленность языка F# ко взаимодействию с нижележащей средой выполнения. То, что он опирается на библиотеку классов .NET там, где другие функциональные языки используют специальные методы (например, многопоточность), означает, что в программах на C# можно использовать библиотеки и модули F# — и наоборот.