Создание приложения «Учебник по 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; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!