Пример выполнения простого задания: Begin3
На данной странице приводится пошаговое описание процесса решения на языках
C#, VB.NET и F# следующего простого учебного задания из группы Begin.
Begin3°. Даны стороны прямоугольника a и b.
Найти его площадь S = ab и
периметр P = 2(a + b).
В качестве среды программирования используется среда Visual Studio 2017,
однако аналогичные результаты будут получены и при выполнении приведенных
программ в среде Visual Studio других версий, поддерживаемых задачником.
Начиная с версии 4.20, для языков C# и F# можно также использовать среду SharpDevelop 5.1,
а начиная с версии 4.21, для языка C# можно использовать среду Visual Studio Code.
В версии 4.20 был существенно доработан вариант задачника для языка F#; именно этот
вариант используется при описании процесса выполнения задания.
В этом и последующих пунктах, в целях краткости и единообразия, процедурами
называются не только обычные процедуры Sub языка VB.NET, но и функции языков
C# и F#, которые не возвращают значения (у таких функций в качестве типа
возвращаемого значения в языке C# указывается ключевое слово void).
Создание программы-заготовки и знакомство с заданием
Для создания проекта, содержащего заготовку для требуемого задания, следует
воспользоваться программным модулем PT4Load. Для этого достаточно
перейти в рабочий каталог задачника и с помощью ярлыка Load.lnk запустить
программу PT4Load.exe (начиная с версии 4.22, для быстрого запуска любых модулей задачника
можно использовать программу PT4Panel, ярлык которой располагается
на рабочем столе и в любом рабочем каталоге).
В результате на экране появится окно модуля PT4Load, в котором
следует указать имя нужного задания. Отметим, что в заголовке этого окна указывается краткое
имя той программной среды, для которой будет создана заготовка. Например, для языка C# и среды
Visual Studio 2017 в заголовке окна модуля PT4Load будет указано
[Microsoft Visual C# 2017].
Созданный проект-заготовка состоит из нескольких файлов, однако для решения
задания нам потребуется только один файл: Begin3.cs для языка C#, Begin3.vb для
языка VB.NET и Begin3.fs для языка F#. Именно этот файл будет загружен в редактор среды Visual Studio.
Приведем содержимое файлов Begin3.cs, Begin3.vb и Begin3.fs:
[Begin3.cs]
// File: "Begin3"
using PT4;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PT4Tasks
{
public class MyTask : PT
{
public static void Solve()
{
Task("Begin3");
}
}
}
[Begin3.vb]
Option Strict On
Option Infer On
Imports PT4.PTVB
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Module MyTasks
Sub Solve()
Task("Begin3")
End Sub
End Module
[Begin3.fs]
module MyTask
open PT4
open System
open System.Collections.Generic
open System.Linq
open System.Text
let Solve = pt.Task "Begin3"
Файл Begin3.cs содержит описание класса MyTask (этот класс является
потомком класса PT, описанного в пространстве имен PT4).
Процедура Solve метод класса MyTask содержит вызов
процедуры Task, инициализирующей задание Begin3. Файл Begin3.vb содержит описание
«обычной» процедуры Solve, не являющейся методом какого-либо
класса; в процедуре Solve уже содержится вызов процедуры Task. Решение задачи
необходимо запрограммировать в процедуре Solve (хотя, разумеется, в решении
могут использоваться другие процедуры и функции, описанные в данном проекте).
Файл Begin3.fs содержит описание переменной Solve, в которое входит вызов процедуры Task
(при вызове процедур и функций задачника в программах на языке F# необходимо указывать перед ними
имя класса pt, в котором они определены). Решение задачи должно располагаться после строки с описанием переменной Solve.
Процедура Task (наряду с функциями группы Get
и процедурой Put) реализована для языков C# и VB.NET в виде статического
метода вспомогательного класса с именем PT для языка C# и PTVB для языка VB.NET. Однако перед ее именем можно не указывать имя
соответствующего класса. Для языка C# это возможно, поскольку Task вызывается из метода Solve
класса MyTask потомка класса PT; для языка VB.NET это возможно
благодаря директиве Imports PT4.PTVB.
Заметим, что в файле Begin3 отсутствует описание процедуры Main,
которая является стартовой при выполнении программы на языках C# и VB.NET
(для языка F# стартовая процедура помечается атрибутом [<EntryPoint>]
и тоже обычно имеет имя main).
Стартовая процедура описана в файле pt4main, также созданном при генерации
проекта-заготовки для данного задания (для языка C# данный файл имеет
расширение .cs, для языка VB.NET расширение .vb, для языка F# расширение .fs).
Именно в стартовой процедуре происходит вызов процедуры Solve, выполняющей задание для языков C# и VB.NET
(в случае языка F# в стартовой процедуре выполняются все операторы, указанные в файле с решением задачи).
Поскольку содержимое файла pt4main не требует редактирования, данный файл не загружается в редактор
среды Visual Studio. Однако он входит в проект, в чем можно убедиться, посмотрев на
окно Solution Explorer (Обозреватель решений).
Данное окно обычно располагается в правой части окна среды
Visual Studio; на рисунке слева направо приведены окна для проектов на языке C#, VB.NET и F#:
Запустим программу, нажав клавишу [F5], чтобы увидеть на экране
окно задачника с формулировкой задания и примером исходных данных.
Эту же клавишу можно использовать для запуска программы в средах SharpDevelop и Visual Studio Code.
Ниже приводится
вид окна для проекта на C# (окно для VB.NET и F# отличается только заголовком, в
котором вместо текста [C#] содержится текст [Visual Basic .NET] и [F#]).
Окно задачника может отображаться в двух режимах:
с динамической компоновкой, при которой каждый раздел «подстраивается» под свое содержимое,
и с фиксированной компоновкой, при которой каждый раздел имеет фиксированные размеры.
Режим с динамической компоновкой особенно удобен для заданий с большими формулировками и большими наборами данных.
Ниже приведены оба режима окна для данного задания; вначале указан режим с динамической компоновкой
(для переключения режимов предназначена клавиша [F4]).
Запуск нашей программы был признан ознакомительным (и поэтому
правильность решения не анализировалась), так как в ходе ее выполнения не было
выполнено ни одной операции ввода-вывода.
Ввод исходных данных
Приступая к решению задачи, надо прежде всего организовать ввод в
программу исходных данных. В нашем случае исходными данными являются
вещественные числа a и b, определяющие стороны прямоугольника (в области
исходных данных эти числа выделены желтым цветом и снабжены комментариями).
При вводе исходных данных важно правильно указывать их тип, в противном
случае задачник зафиксирует ошибку. Продемонстрируем это на примере нашей
программы, организовав считывание исходных данные в переменные целого типа,
используя функцию GetInt. Для этого закроем окно задачника (нажав кнопку
«Выход», клавишу [Esc] или клавишу [F5]) и дополним текст процедуры
Solve следующим образом:
[C#]
public static void Solve()
{
Task("Begin3");
int a = GetInt(), b = GetInt();
}
[VB.NET]
Sub Solve()
Task("Begin3")
Dim a = GetInt(), b = GetInt()
End Sub
[F#]
let Solve = pt.Task "Begin3"
let a = pt.GetInt()
let b = pt.GetInt()
Повторно запустив программу, мы увидим, что исходные данные изменились.
При каждом запуске генерируется новый набор исходных данных, поэтому для
успешного решения задания необходимо запрограммировать алгоритм, правильно
обрабатывающий любой допустимый набор исходных данных.
Поскольку мы попытались ввести исходные данные (т. е.
продемонстрировали намерение выполнить задание), данный запуск программы уже
не считается ознакомительным. Однако тип исходных данных был выбран нами
неправильно, поэтому на информационной панели будет выведена следующая
информация об ошибке (на фиолетовом фоне): «Неверно указан тип при вводе
исходных данных. Для ввода 1-го элемента (вещественного типа) использована
переменная целого типа»:
Следует обратить внимание на индикаторы, появившиеся в окне задачника.
В окне с динамической компоновкой для индикаторов отводится
специальная панель, которая располагается под информационной панелью; в качестве индикаторов
используются цветные горизонтальные полосы, а также цветные квадратные маркеры.
Полосы серого цвета показывают, какая часть исходных данных была введена
в ходе выполнения задания и какая часть результатов была выведена.
В нашем случае задачник зафиксировал ввод только одного элемента исходных данных (из двух
имеющихся).
Это объясняется тем, что уже при вводе первого элемента было обнаружено несоответствие типов,
и поэтому оставшаяся часть решения не анализировалась.
Полосы и маркеры различных оттенков красного цвета определяют характер обнаруженной ошибки,
а также указывают, к какой группе данных (исходных или результирующих) эта ошибка относится.
В нашем случае ошибка несоответствия типа была выявлена для исходных данных, поэтому
заголовок раздела с исходными данными был выделен фиолетовым цветом и, кроме того, на панели индикаторов
в области «Ввод», появился фиолетовый маркер.
Для правильного ввода исходных данных в нашем случае надо использовать
функцию GetDouble,
обеспечивающую ввод данных вещественного типа. Исправим
соответствующим образом процедуру Solve:
[C#]
public static void Solve()
{
Task("Begin3");
double a = GetDouble(), b = GetDouble();
}
[VB.NET]
Sub Solve()
Task("Begin3")
Dim a = GetDouble(), b = GetDouble()
End Sub
[F#]
let Solve = pt.Task "Begin3"
let a = pt.GetDouble()
let b = pt.GetDouble()
Теперь ввод данных выполнен правильно. Однако наша программа не выводит
результатов. В данной ситуации выводится сообщение
на светло-синем фоне: «Запуск с правильным вводом данных: все требуемые
исходные данные введены, результаты не выведены».
Светло-синяя индикация означает, что успешно пройден первый этап решения:
ввод исходных данных. Заметим, что в ранних версиях задачника в аналогичной ситуации
выводилось сообщение об ошибке на оранжевом фоне
«Выведены не все результирующие данные. Количество выведенных данных: 0
(из 2)».
Вычисления и вывод полученных данных
Выполним необходимые вычисления и выведем результаты, используя
процедуру Put
(в этой процедуре можно указывать произвольное количество параметров):
[C#]
public static void Solve()
{
Task("Begin3");
double a = GetDouble(), b = GetDouble(), S, P;
S = a * b;
P = 2 * (a + b);
Put(P, S);
}
[VB.NET]
Sub Solve()
Task("Begin3")
Dim a = GetDouble(), b = GetDouble(), S, P As Double
S = a * b
P = 2 * (a + b)
Put(P, S)
End Sub
[F#]
let Solve = pt.Task "Begin3"
let a = pt.GetDouble()
let b = pt.GetDouble()
let S = a * b
let P = 2. * (a + b)
pt.Put(P, S)
В данном фрагменте программы для языков C# и VB.NET демонстрируются два варианта описания
переменных: с инициализацией и без нее. Переменные a и b, предназначенные для
хранения исходных данных, инициализируются в момент описания; при этом
используется функция GetDouble. Переменные S и P,
предназначенные для хранения результатов, не инициализируются при описании; они
получают требуемые значения при последующем выполнении двух операторов присваивания.
Заметим, что вполне возможно было бы инициализировать при описании все переменные
(поскольку в момент описания переменных S и P значения a и b уже известны) или,
наоборот, описать все четыре переменные без инициализации, а затем задать их значения,
используя операторы присваивания. В языке VB.NET все локальные переменные, не
инициализированные явно, по умолчанию инициализируются нулевыми значениями. В
языке C# неинициализированные локальные переменные не получают значения по
умолчанию, а попытка обратиться к содержимому неинициализированных
переменных приводит к ошибке компиляции.
В языке F# переменные по умолчанию считаются
неизменяемыми, поэтому их начальные значения задаются в момент описания
(хотя можно использовать и изменяемые переменные, описываемые с помощью ключевого
слова mutable).
После запуска программы мы можем убедиться, что найденные значения
выведены в разделе результирующих данных. Но из-за того, что вначале мы вывели
значение периметра P, а затем значение площади S, решение по-прежнему
признано неверным, а сообщение об ошибке имеет вид «Ошибочное
решение». Подобное сообщение означает, что все исходные данные были
введены, все результирующие данные выведены, но значения полученных
результатов не совпадают с контрольными:
Индикаторы показывают, что были введены и выведены все требуемые данные.
Обратившись к примеру верного решения,
приведенному в окне задачника, мы видим, что площадь и периметр
найдены верно и нарушен только порядок их вывода. В окне с динамической компоновкой
пример верного решения выводится ниже раздела результатов.
Таким образом, важно не только найти правильные значения результирующих
данных, но и вывести их в нужном порядке. Для того чтобы указать этот порядок, в
разделе результатов используется поясняющий текст (см. приведенный выше рисунок
окна задачника). Заметим, что исходные данные тоже важно вводить именно в том
порядке, в котором они указаны в окне задачника. Общее правило здесь следующее:
ввод и вывод данных производится по строкам (слева направо), а строки
просматриваются сверху вниз. Иными словами, данные, отображаемые в окне
задачника, вводятся и выводятся в том порядке, в котором читается обычный текст на
русском языке.
Правильное решение и его тестирование
Исправим допущенную ошибку, изменив порядок вывода результирующих
данных в процедуре Put:
[C#]
Put(S, P);
[VB.NET]
Put(S, P)
[F#]
pt.Put(S, P)
После запуска исправленной программы на экране появится окно прогресса тестирования:
Данное окно отображается на экране, если программа успешно обработала хотя бы один
набор исходных данных. Полоса прогресса показывает, сколько тестов было успешно пройдено
к текущему моменту, а текст над полосой дублирует эту информацию и позволяет определить количество тестов,
которое должна успешно пройти программа, чтобы задание считалось выполненным.
Тестирование программы завершается в двух случаях: когда будет успешно пройдено требуемое число
тестов или когда при прохождении очередного теста будет выявлена ошибка.
Если при очередном тестовом испытании произойдет «зависание» программы
(например, из-за зацикливания алгоритма), то для завершения работы программы следует
закрыть окно прогресса тестирования, нажав на кнопку с крестиком в его правом верхнем углу.
В нашем случае решение не содержит ошибок, поэтому после прохождения трех успешных тестов
на экране появится окно задачника с сообщением «Задание выполнено!».
Выполнение задания Begin3 завершено:
Заметим, что в ранних версиях задачника
для тестирования на различных наборах данных программу требовалось запускать несколько раз;
при этом на информационной панели выводилась информация о количестве успешно пройденных тестов.
Возможность подобного «пошагового» тестирования сохранена и в новых версиях; для этого
надо дополнить имя задания в процедуре Task символом «!» (восклицательный знак).
С помощью модуля PT4Results можно просмотреть информацию о ходе
выполнения задания (этот модуль можно вызывать непосредственно из окна задачника;
для этого достаточно нажать клавишу [F2] или щелкнуть мышью на метке с описанием этой клавиши,
размещенной в правом верхнем углу окна задачника):
Begin3 S04/02 14:57 Ознакомительный запуск.
Begin3 S04/02 15:07 Неверно указан тип при вводе исходных данных.
Begin3 S04/02 15:09 Запуск с правильным вводом данных.
Begin3 S04/02 15:12 Ошибочное решение.
Begin3 S04/02 15:15 Задание выполнено!
Буква «S», указанная перед датой, означает, что задание выполнялось на языке C#.
[C#]
public static void Solve()
{
Task("Begin3");
double a = GetDouble(), b = GetDouble();
Put(a * b, 2 * (a + b));
}
[VB.NET]
Sub Solve()
Task("Begin3")
Dim a = GetDouble(), b = GetDouble()
Put(a * b, 2 * (a + b))
End Sub
[F#]
let Solve = pt.Task "Begin3"
let a = pt.GetDouble()
let b = pt.GetDouble()
pt.Put(a * b, 2. * (a + b))
Кроме того, в решении на языке F# можно использовать функцию GetDouble2,
позволяющую сразу ввести два исходных вещественных числа (в виде кортежа):
[F#]
let Solve = pt.Task "Begin3"
let a, b = pt.GetDouble2()
pt.Put(a * b, 2. * (a + b))
Начиная с версии 4.22, функции для ввода кортежей можно использовать
для языков C# и VB.NET (за исключением среды SharpDevelop, использующей версию 5.0 языка C#,
в которой кортежи не поддерживаются):
[C#]
public static void Solve()
{
Task("Begin3");
var (a, b) = GetDouble2();
Put(a * b, 2 * (a + b));
}
Для введенного кортежа сразу выполняется операция деконструкции, позволяющая
сохранить значения его полей в переменных a и b. В данном случае при описании переменных a и b
удобно использовать ключевое слово var.
Возможен и другой вариант: (double a, double b) = GetDouble2();
В языке VB.NET отсутствует операция деконструкции кортежа,
поэтому применение в нем функций для ввода кортежей обычно не приводит
к упрощению кода программы:
[VB.NET]
Sub Solve()
Task("Begin3")
Dim ab = GetDouble2()
Dim a = ab.Item1, b = ab.Item2
Put(a * b, 2 * (a + b))
End Sub
|