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



 

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

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

let c = Console.ReadKey()

 

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

 

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

По умолчанию клавиша отладки вынесена на панель инструментов вверху. Запускаем приложение в режиме отладки (и компиляции 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# — и наоборот.

 


Дата добавления: 2019-09-13; просмотров: 180; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!