
Lab03_2010
.pdf
Зеленая строка исполнения переместится на вызов этого оператора, все «промежуточные» действия при этом будут выполнены в автоматическом режиме: Вы можете убедиться в этом, проверив, что все строки массива заполнены (рис. 40).
Сессию отладки можно завершить, выполнив пункт меню Debug | Finish Debugging Session или просто нажав Shift + F5
§ 4. Отыскание суммы максимальных элементов строк
Теперь приступим к решению пункта а) задачи. Разработаем для этого три метода: первый будет отыскивать максимальный элемент в строке с заданным номером, второй – вычислять сумму таких элементов, а третий обеспечит вывод решения в файл.
Метод поиска максимального элемента в строке идейно повторяет метод отыскания максимального элемента в последовательности. Сначала предполагаем, что максимальным будет элемент нулевого столбца в строке, а затем, последовательно сравнивая с «текущим максимумом» остальные элементы, обновляем этот максимум по мере необходимости (рис. 41).
Рис. 41. Поиск максимального элемента в строке
Метод, вычисляющий сумму максимальных элементов строк, вызывает метод maxInRow в цикле для каждой строки (рис. 42):
Рис. 42. Вычисление суммы максимальных элементов строк
21

Наконец, третий метод должен создать файл и записать туда вычисленную сумму. Запись в файл выполним с помощью класса PrintWriter, для чего опишем метод printSumMaxInRow, а в нем – переменную этого класса.
Примечание.
Существует еще класс FileWriter, но он «умеет» выводить только строки или символы. Конечно, любое число можно преобразовать в строку, но пока мы хотим избежать этого преобразования. Отметим, что класс FileWriter может оказаться удобным, если требуется дописывать информацию в существующий файл.
Рис. 43. Опишем переменную класса PrintWriter
Среда предложит выполнить импорт этого класса из пакета java.io, что и следует сделать. Один из конструкторов класса PrintWriter может создавать объект класса по параметру – имени выходного файла. Создадим объект для файла a.txt (рис. 44).
Рис. 44. Вывод будет осуществляться в файл a.txt
Такой файл пока не существует, однако при обращении к конструктору PrintWriter он будет создан. Более того, если создать такой файл, то после вызова конструктора PrintWriter его размер будет обнулен (а информация, содержавшаяся в нем, уничтожена), и файл будет перезаписан (Вы можете прочитать об этом в справочном файле, установив текстовый курсор на PrintWriter, нажав Shift + F1 и отыскав на открывшейся странице соответствующий конструктор PrintWriter(String FileName)). Однако, поскольку этот конструктор объявлен пропускающим исключение FileNotFoundException («Файл не найден») (рис. 45), среда сообщит о том, что оно должно быть обработано или пропущено в методе printSumMaxInRow (как в вызывающем небезопасный метод).
Рис. 45. Пропускаемое конструктором исключение
22

Мы добавим в заголовок метода printSumMaxInRow предложение throws, содержащее, как и конструктор, пропуск исключений IOException (класса-предка по отношению к FileNotFound Exception) (рис. 46).
Рис. 46. Метод printSumMaxInRow()
Метод println выводит в файл вещественное число (он успешно справляется и с целыми числами, и со строками). Метод flush() выполняет очистку буфера. Метод close() закрывает файл.
Примечание.
Очистка буфера, вообще говоря, может понадобиться не всегда. Однако лучше выполнить ее «лишний раз», нежели не выполнить и обнаружить, что в файле оказались не все полученные результаты. Вывод в файл – медленная операция, и потому данные обычно сначала накапливаются в специальной области памяти – буфере, и затем уже «большой порцией» передаются в файл.
Принудительная очистка буфера перед закрытием файла приводит к тому, что даже если «большая порция» не сформирована, все содержимое буфера будет переписано в файл.
Теперь можно поместить вызов метода printSumMaxInRow в метод main() главного класса и запустить приложение на выполнение (рис. 47).
Рис. 47. Приложение успешно завершило свою работу
Если все было сделано верно, Вы получите сообщение о том, что приложение успешно завершило свою работу. Чтобы посмотреть созданный файл, следует переключиться на вкладку Files (и «раскрыть» проект, если он окажется «свернутым» (рис. 48). Среди прочих Вы увидите файл a.txt, который двойным щелчком можно открыть в главном окне среды (рис. 49).
23

Рис. 48. Файл a.txt создан в результате запуска приложения
Рис. 49. Содержимое файла a.txt
Несложно выполнить «ручную» проверку этого результата. Вообще говоря, выполнять тестирование следует после написания каждого нового метода (или группы связанных методов, как в нашем случае). Однако, поскольку метод поиска максимального элемента разрабатывается не первый раз, мы несколько отступим от этого правила и проведем тестирование несколько позже, когда будут разработаны и другие методы.
В завершение этого параграфа сформулируем «алгоритм» – как организовать вывод
в(текстовый) файл (пока «упрощенную» версию, в дальнейшем мы внесем некоторые усовершенствования).
1)импортировать классы: java.io.PrintWriter и java.io.Exception (если последний еще не был импортирован, например, для организации чтения данных из файла).
2)разрешить пропускать ошибки ввода / вывода методу (методам), прямо или косвенно использующим объект класса PrintWriter, дописав к его (их) заголовку (- ам) (после списка параметров) предложение throws IOException
3)создать объект типа PrintWriter, передав в его конструктор в качестве параметра имя (текстового) файла.
4)выполнить вывод результатов в файл с помощью методов print, println или printf (при необходимости установив нужную локаль) объекта типа PrintWriter.
5)по завершении работы вызвать метод flush() для сброса буфера и закрыть объект типа PrintWriter методом close().
24

§ 5. Получение одномерного массива из минимальных элементов столбцов
Для решения пункта б) задачи также будут написаны три метода: для отыскания минимального элемента в столбце, для формирования массива, для вывода этого массива на печать. Отыскание минимума в столбце идейно не отличается от отыскания максимума. Элемент, стоящий на пересечении исследуемого столбца и нулевой строки, предполагается минимальным, после чего с «текущим минимумом» сравниваются все остальные элементы, и при необходимости выполняется его обновление (рис. 50).
Рис. 50. Отыскание минимального элемента в столбце с номером numCol
Массив, который требуется сформировать в пункте б) задачи, можно, разумеется объявить как поле класса наряду с массивом array. Однако можно объявить такой массив результатом выполнения метода (рис. 51).
Рис. 51. Формирование массива из минимальных элементов столбцов
Метод, выводящий полученный массив на печать, будет создавать файл b.txt. Пока ограничимся тем, что выведем все элементы полученного массива через пробел. Приведенный на рис. 52 код имеет небольшой недостаток: пробел выводится и после последнего элемента массива. Лишних символов при выводе лучше избегать (особенно если предполагается автоматизированное тестирование приложения; это еще будет обсуждаться в дальнейшем). Пока заметим, что избежать вывода лишнего пробела можно, например, если первый элемент массива выводить отдельно, а все последующие – с
25

пробелом перед ними (это более простое решение проблемы, нежели пытаться контролировать при выводе, является ли элемент последним).
Рис. 52. Вывод массива на печать
Теперь остается добавить вызов последнего метода в метод main() главного класса (рис. 53), запустить приложение на выполнение – и можно просматривать результат, выведенный в файл b.txt (рис. 54).
Рис. 53. Добавление вызова метода printArrayMinInColumn() в метод main()
Рис. 54. Результаты работы метода printArrayMinInColumn()
Внесите необходимые правки в метод printArrayMinInColumn(), чтобы после последнего числа не выводился пробел. Проверьте работоспособность приложения.
§ 6. Чтение дополнительных входных данных
Напомним задачу, поставленную в пункте в). Требуется найти в массиве элементы, равные заданному числу, с точностью до некоторого ε, а также указать их координаты (если хотя бы один такой есть) или же сообщить об отсутствии таких элементов.
Для решения этой задачи потребуются дополнительные входные данные: вопервых, собственно число, с которым следует сравнивать элементы, во-вторых, точность,
26

с которой нужно выполнять это сравнение. Обе эти величины понадобится запросить у пользователя (прочитать из файла).
Мы можем договориться, что заданное число и точность, с которой производится сравнение, будут записаны в файле со входными данными после последней строки массива. Поэтому нужно добавить поля, в которых заданное число и точность сравнения будут храниться, и дополнить конструктор класса Matrix, чтобы прочитывать из файла и эти данные. Кроме того, понадобится изменить и сам текстовый файл, дописав в него еще одну строку.
Назовите поле для хранения искомого числа desiredNumber, а поле для хранения точности сравнения – eps (сокращение от epsilon) (рис. 55). Оба поля будут вещественного типа double.
Рис. 55. Дополним класс Matrix новыми полями
Переключитесь на вкладку input.txt, допишите в него еще одну строчку – с числами 2.2 (искомое число) и точностью сравнения 0.1 (рис. 56), сохраните его (обратите внимание, что имена измененных файлов на вкладках отображаются жирным шрифтом) и вновь переключитесь на вкладку WorkWithMatrix.java.
Рис. 56. Новая строка во входном файле input.txt
Наконец, в конструкторе после циклов, отвечающих за чтение массива, добавьте строки, прочитывающие новые данные. Заметим, что при наборе «длинных» названий полезно пользоваться подсказками среды. Как только Вы напечатаете первую букву имени поля desiredNumber (букву d), среда NetBeans уже будет готова предложить Вам несколько вариантов продолжения. Появление такой подсказки обеспечено настройками
27

по умолчанию. Однако сколь быстро она появится на экране, зависит и от Вашей скорости набора, и от «скорости» компьютера. Чтобы подсказка появилась тогда, когда Вы этого хотите, нужно одновременно нажать клавиши Ctrl и Пробел (Ctrl + Space).
Напечатайте «d» в конструкторе после вложенных циклов и нажмите указанную комбинацию клавиш (рис. 57):
Рис. 57. Подсказка среды – возможные продолжения
Конечно, Вы уже сейчас можете нажать клавишу Enter, и (уже подсвеченное, поскольку стоит первым) имя desiredNumber появится в тексте программы. Но Вы можете проследить, как сокращается количество вариантов, продолжая печатать – сначала «e» (останется всего две возможности), затем «s» – будет единственное продолжение.
Примечание.
Вообще говоря, Вы можете нажать Ctrl + Space «на пустом месте» – только вариантов может оказаться слишком много.
Поскольку данные – вещественного типа, читать их следует методом nextDouble (рис. 58):
Рис. 58. Чтение новых данных в конструкторе
С помощью отладчика можно удостовериться, что чтение данных происходит правильно. Поставьте точку останова на строчке чтения desiredNumber (предшествующий код мы не меняли, и можем полагать, что он продолжает работать правильно) и запустите отладку проекта (Ctrl + F5 или Debug | Debug Main Project в главном меню).
28

Если Вы не удаляли точку останова в методе main(), то Вы должны увидеть следующее (рис. 59):
Рис. 59. Первая точка останова
Точки останова в программе при отладке проходятся последовательно – в порядке исполнения операторов. Вторая (только что поставленная) точка останова находится внутри конструктора Matrix(), т.е. обращение к оператору, который ей помечен, при исполнении программы происходит после вызова конструктора.
Чтобы сразу перейти ко второй (только что поставленной) точке останова, нажмите F5 или выполните Debug | Continue в главном меню. Это приведет к автоматическому выполнению всех промежуточных шагов, и зеленая строчка – курсор отладки – окажется на операторе присваивания значения desiredNumber (рис. 60):
Рис. 60. Вторая точка останова
Раскройте во вкладке Variables переменную this (нажав на «+» рядом с ее именем) и, дважды нажимая F8 (выполняя Debug | Step Over из главного меню или пользуясь соответствующей пиктограммой на панели инструментов), проследите за появлением значений desiredNumber и eps (рис. 61).
Рис. 61. Поля desiredNumber и eps получили значения, прочитанные из файла
29
Поскольку дальнейший код не претерпел изменений, то сессию отладки можно завершить (Shift + F5).
Примечание.
Если Вы вновь нажмете F5, то, хотя выполнение программы завершится – ведь точек останова больше нет, консоль отладки останется открытой.
§ 7. Хранение последовательности неизвестной заранее длины
Организация просмотра массива с целью сравнения каждого его элемента с заданным числом никаких «подводных камней» не содержит. Наибольший интерес представляет хранение номеров строк и столбцов, на которых подходящие элементы находятся.
Можно, конечно, выводить эти номера сразу по мере получения, не сохраняя их в программе. Однако если с найденными элементами потребуется что-либо сделать впоследствии, такой подход окажется неприменимым. Поэтому нужно предусмотреть возможность хранения списка координат. Пара координат – т.е. пара целых чисел – может быть описана либо как (одномерный) массив из двух элементов, либо как класс с двумя целочисленными полями. В пользу второго представления есть два важных довода. Первый – большая наглядность такого представления (к элементам массива нужно обращаться по индексам, к полям объекта – по именам). Второй – более широкие возможности по использованию структур данных.
Пока единственной структурой данных, с которой мы познакомились, был массив – набор однородных индексированных элементов. При создании массива необходимо выделить для него память, и в дальнейшем в программе он будет иметь фиксированный размер. Это не слишком удобно в нашем случае: заранее неизвестно, сколько элементов исследуемого массива окажутся удовлетворяющими условию. Поскольку оценку придется выполнять по их максимально возможному количеству, то придется создавать массив пар координат длины n * m и, по мере его заполнения, отслеживать его фактический размер. Вместе с тем велика вероятность того, что этот массив крайне редко (или даже никогда) будет заполнен полностью.
Наилучшим образом для хранения пар координат подошла бы такая структура данных, размер которой можно было бы увеличивать при необходимости. Структуры, которые могут изменять свой размер в ходе выполнения программы, называются динамическими структурами данных.
Такую структуру можно разработать самостоятельно (как это сделать, будет обсуждаться и на лекциях, и на дальнейших практических занятиях), а можно воспользоваться «готовой»: в Java предусмотрен достаточно широкий набор таких структур, именуемых коллекциями.
Примечание.
Возможно, подход, при котором студенты сначала учатся разрабатывать динамические структуры данных вручную и лишь затем знакомятся с возможностями системы программирования, методически более традиционен. Однако обучающемуся, уже представляющему, зачем нужны такие структуры данных и как их можно использовать, возможно,
будет проще проектировать их при самостоятельной разработке.
30