Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lab02_2010

.pdf
Скачиваний:
30
Добавлен:
07.06.2015
Размер:
382.44 Кб
Скачать

Лабораторная работа 2. Одномерные массивы

В предыдущей лабораторной работе решались задачи, не требующие хранения (последовательности) вводимых данных. Часто в задачах требуется сохранять входные данные. Однако далеко не всегда можно сразу, «на лету», провести их обработку. При этом бывает достаточно даже незначительного изменения условия задачи. Так, например, подсчитать, сколько чисел в последовательности совпадает с первым ее числом или с максимальным ее элементом, можно, не сохраняя эту последовательность. Но если потребуется вычислить, сколько чисел в последовательности совпадает с ее последним числом, то всю последовательность придется хранить до тех пор, пока это последнее число не будет введено.

Проект № 2. Работа с сохраненной последовательностью чисел.

Приложение, которое мы сейчас разработаем, будет решать следующую задачу.

С клавиатуры вводится последовательность N (вещественных) чисел, обозначающих точки на числовой оси. Требуется найти:

а) пару (указать номера) наиболее удаленных друг от друга точек б) точку (указать номер), ближайшую к середине отрезка, концами которого

являются наиболее удаленные друг от друга точки.

в) пару (указать номера) наиболее близко расположенных друг к другу точек

Обсудим сначала план действий.

Чтобы выполнить задание пункта а), хранить данные нет необходимости. Вполне достаточно найти минимум и максимум из N введенных чисел и вывести номера этих чисел в качестве результата.

А вот при выполнении пунктов б) и в) без хранения всех точек уже не обойтись. Искать точку, ближайшую к середине отрезка, концами которого являются наиболее удаленные друг от друга точки, можно только зная координаты концов отрезка. Но чтобы определить их, потребуется просмотреть всю последовательность до конца. После этого, вычислив середину отрезка с известными концами, можно выяснить, какая из точек ближе всего находится к этой середине, еще раз просмотрев все элементы последовательности.

Чтобы найти две наиболее близко расположенные друг к другу точки, необходимо либо просмотреть все возможные пары точек, сравнивая расстояния между ними, либо сначала упорядочить их (по возрастанию или убыванию) и просмотреть все соседние пары. Эти действия также требуют неоднократного возвращения к одним и тем же элементам последовательности.

Поэтому при вводе элементов последовательности мы будем помещать их в одномерный массив.

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

Создайте (как было описано в Лабораторной работе № 1) новый проект вида Java Application, назовите его lab_02_gp_01 (первый общий проект во второй лабораторной работе), а его главный класс – TestArray (lab_02_gp_01.TestArray) и сохраните в папке W:\JavaProjects\LR02_01). Все остальные настройки при создании проекта менять не нужно. У Вас должно получиться следующее:

Рис. 1. Класс TestArray

Метод main() класса TestArray будет служить лишь для «запуска» программы. Все действия с массивом будут содержаться в другом классе, который мы сейчас опишем.

§ 1. Создание нового класса

Когда планируется использовать класс в других пакетах, его необходимо объявлять общедоступным (public). В этом случае он обязан находиться в отдельном одноименном файле. Вы уже сталкивались с такими классами: Scanner, Math.

Мы же собираемся ограничиться использованием создаваемого класса внутри пакета, поэтому уровня «видимости пакета» будет вполне достаточно (и даже с лихвой). А это значит, что можно поместить его в файл TestArray.java.

Примечание.

В один файл обычно также помещают «логически связанные» классы, если один из них «основной», а все остальные носят вспомогательный характер, и используются только этим «основным» классом.

Назовем новый класс MyArray. Напечатайте начало его объявления в окне Редактора (рис. 2):

Рис. 2. Начало объявления нового класса

Нажмите клавишу Enter и «ошибка» исчезнет: среда автоматически завершит объявление класса (рис. 3):

Рис. 3. Автоматическое завершение объявления класса

Опишем в классе единственное пока поле типа массив, в котором будут храниться введенные вещественные числа (рис. 4):

Рис. 4. Поле с именем array – одномерный массив вещественных чисел

Класс без заполненного массива не имеет смысла: никакие его методы (описывающие действия по решению задачи) нельзя будет использовать, если массив не существует. Поэтому поместим код, обеспечивающий заполнение массива, в конструктор.

§ 2. Описание конструктора

Напомним, что конструктор имеет имя, совпадающее с именем класса. Напечатайте заголовок конструктора и фигурные скобки для его тела (не забудьте, что закрывающая фигурная скобка будет поставлена автоматически, когда Вы нажмете Enter.

Кроме того, полезно снабжать закрывающие скобки методов и блоков операторов хотя бы простыми комментариями – какой именно метод или блок завершен. Среда позволяет «сворачивать» блоки кода, но этого не всегда достаточно.

После написания заголовка конструктора код в окне редактора примет следующий вид (рис. 5):

Рис. 5. Конструктор MyArray()

Теперь нужно организовать чтение с клавиатуры сначала (целого) количества точек на числовой оси, а затем – их (вещественных) координат. Чтобы это сделать, опишем в конструкторе объект класса Scanner, который будет способен читать данные из стандартного входного потока (см. лабораторную работу № 1). Когда такой объект будет описан, потребуется добавить предложение импорта (можете напечатать его сами – до всех объявлений классов, или можете выбрать предлагаемый средой NetBeans вариант) (рис. 6А).

Рис. 6А. Добавленное предложение импорта

Рис. 6B. Чтение размера массива и выделение для него памяти

Также в конструкторе опишем целую переменную N, значением которой станет введенное пользователем с клавиатуры количество чисел. Ради краткости описание этой переменной можно совместить с ее инициализацией этим значением, предварительно сформулировав соответствующий запрос пользователю.

После этого можно выделить память для массива (рис. 6B).

§ 3. Особенности ввода вещественных чисел

Теперь необходимо прочитать координаты точек. Поскольку координаты – вещественные числа, то следует использовать метод nextDouble(). Однако распознавание вещественных чисел по умолчанию зависит от региональных настроек операционной системы. Так, в «русскоязычных» операционных системах дробная часть числа отделяется от целой с помощью запятой, а в «англоязычных» для этих целей используется точка. Поскольку в программировании также обычно используют точку в качестве разделителя, то потребуется явным образом указать объекту класса Scanner использовать соответствующие региональные настройки при чтении вещественных чисел. Для этого в его методе useLocale достаточно указать в качестве параметра Locale.US (или Locale.UK; класс Locale нужно будет импортировать – дописать соответствующее предложение импорта сразу после предложения импорта класса Scanner можно как самостоятельно, так и следуя «подсказкам» среды). Затем можно организовать цикл по заполнению массива с помощью метода nextDouble() объекта Scanner (рис. 7):

Рис. 7. Заполнение массива вещественными числами

Запись (i + 1) в приглашении для ввода числа позволяет запрашивать у пользователя числа от 1 до N, а не от 0 до (N–1) ( что не-программисту может показаться непривычным).

Написанный метод можно «протестировать» на работоспособность. Для этого в методе main() класса TestArray следует создать объект класса MyArray (рис. 8):

Рис. 8. Создание объекта класса MyArray

Сохраните проект и запустите его на выполнение. На рис. 9 приведен пример работы приложения:

Рис. 9. Вывод приложения при N = 4

Конечно, так нельзя увидеть заполненный массив, но пока приложение делает то, что и было задумано: запрашивает у пользователя количество вводимых чисел, а затем и собственно эти числа. Метод nextDouble() успешно прочитает и целые числа, а вот попытка ввести числа с запятой вместо точки приведет к ошибке (можете попробовать).

Примечание.

Если строчку с вызовом метода useLocale закомментировать, то как раз будут прочитываться числа с запятой в качестве разделителя целой и дробной части, а числа с точкой будут считаться ошибочными – конечно, если Вы работаете в версии операционной системы, имеющей русскую локализацию.

§ 4. Разработка методов отыскания максимума и минимума в массиве

Следующий метод, который мы разработаем, будет отыскивать позицию максимального элемента в массиве. Логика поиска максимума (равно как и минимума) была описана в лабораторной работе № 1. Сделаем лишь необходимые замечания относительно того, как оформить поиск индекса максимального элемента в массиве в виде метода.

Назовем этот метод getMax(). Он не будет иметь параметров – поскольку будет обращаться только к массиву, являющемуся полем того же класса. Тип возвращаемого значения – целый (это будет номер элемента массива). Для задания верхней границы просмотра элемента будет использовано свойство массива length (оно есть у каждого массива и после выделения памяти для массива содержит количество его элементов).

Когда Вы напишете заголовок метода, система сообщит, что в нем не хватает оператора return (рис. 10). Это полезное напоминание, среда не позволит Вам забыть, что найденный индекс максимального элемента необходимо сделать результатом метода. Полная запись метода приведена на рис. 11.

Рис. 10. Метод, имеющий тип, отличный от void, должен содержать в своем теле оператор return, возвращающий значение этого типа

Примечание.

Использовать для определения верхней границы массива переменную N нельзя: она объявлена в конструкторе, и область ее действия ограничена телом конструктора. Поэтому никакие другие методы не могут к ней обратиться. При этом любой другой метод может использовать в своем теле переменную с именем N такого типа и так, как нужно в этом методе.

Рис. 11. Метод getMax() – окончательная запись

Аналогичным образом можно написать метод getMin(), который отыскивает в массиве минимальный элемент (рис. 12):

Рис. 12. Метод getMin() – окончательный вариант

Эти методы в нашей задаче носят вспомогательный характер, однако хорошим правилом является тестирование методов сразу же после их написания. Именно это мы и сделаем, дополнив метод main() (рис. 13):

Рис. 13. Тестирование методов getMax() и getMin()

Сохраните и запустите приложение. Пример вывода для N = 5 приведен на рис. 14.

Рис. 14. Результат работы приложения при N = 5

Как можно видеть, на этом примере методы выдают правильные результаты. Вообще же, чтобы удостовериться в правильности методов, отыскивающих в массиве элемент по какому-либо критерию, имеет смысл использовать, в частности, следующие тесты:

массив из одного элемента (граничный тест N = 1)

упорядоченный массив (по возрастанию и по убыванию)

массив, в котором все или некоторые элементы одинаковы (в том числе и те, которые удовлетворяют критерию; полезно, например, рассмотреть массив, в котором каждый элемент принимает только одно из двух возможных значений)

«случайно заполненные» массивы с различным количеством элементов (четным и

нечетным)

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

§ 5. Разработка метода отыскания наиболее удаленных друг от друга точек

Следующий метод, который будет разработан, – метод, выполняющий задание а). Фактически он просто объединяет в себе методы getMax() и getMin().

Этот метод будет печатать номера наиболее удаленных друг от друга точек, а также расстояние между ними. Номера, как и в случае тестирования методов getMax() и getMin(), печатаются «с коррекцией на 1».

Поскольку никакого значения этот метод не возвращает, он имеет тип void (и оператор return в нем присутствовать не обязан).

Отметим еще то, что номера точек выводятся «по возрастанию» – сначала меньший, затем больший номер. Расстояние же вычисляется до «упорядочения номеров»

– как разность между максимальным и минимальным значением (и, таким образом, гарантированно получается положительным). Если бы мы вычисляли его после упорядочения номеров точек, оно могло бы получиться и отрицательным – тогда следовало бы дополнительно использовать метод Math.abs(), чтобы получить его абсолютное значение.

Полная запись метода приведена на рис. 15

Рис. 15. Запись метода theFarthestPoints()

Чтобы протестировать работу метода theFarthestPoints(), запишем его вызов в методе main() класса TestArray. Поскольку проверка работы поиска максимального и минимального элемента сейчас не нужна, закомментируем ее (рис. 16)

Примечание.

Конечно, «ненужные» методы можно и удалить, по крайней мере, в такой простой задаче, как эта. Но такая запись может пригодиться, чтобы видеть, что уже протестировано, а что еще нет. Многострочные комментарии как раз использованы для того, чтобы было удобно их снять при необходимости.

Рис. 16. Тестирование метода theFarthestPoints()

Сохраните и запустите приложение. Выполните проверку на тех же входных данных, которые были приведены на рис. 14 (вывод приложения приведен на рис. 17). Так же проверьте правильность вычислений с помощью ранее придуманных Вами тестов (рекомендованных в § 4).

Рис. 17. Работа приложения при N = 5

§ 6. Отыскание ближайшей к середине отрезка точки

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

Рис. 18. Метод отыскания точки, ближайшей к середине отрезка

Идейно решение очень похоже на отыскание минимума в массиве. Сначала, определив с помощью вспомогательных методов getMax() и getMin() координаты наиболее удаленных друг от друга точек, вычисляем середину этого отрезка. Затем, выбрав в качестве «пробной» первую точку (элемент массива с индексом 0), записываем

Соседние файлы в предмете Программирование на Java