Практическая работа по теме 3. Модульное программирование

Создание и инициализация массивов. Изменение размеров массива. Поиск в массиве. Тип коллекции – набор объектов (set). Использование динамического класса ArrayList. Использование хэш

таблицы – коллекции Hashtable. Использование коллекции SortedList. Использование очереди. Использование стека. Методы: процедуры и функции. Обработка даты и времени.

Практическая работа по теме 3. Модульное программирование. 1

Задание 1. Создание и инициализация массивов. 1

Задание 2. Изменение размеров массива. 6

Задание 3. Поиск в массиве. 8

Задание 4. Тип коллекции – набор объектов (set) 10

Задание 5. Использование динамического класса ArrayList 12

Задание 6. Использование хэш-таблицы – коллекции Hashtable. 15

Задание 7. Использование коллекции SortedList 18

Задание 8. Использование очереди (набора, работающего по принципу FIFO) 20

Задание 9. Использование стека (набора, работающего по принципу LIFO) 22

Задание 10. Методы: процедуры и функции. 23

Задание 11. Обработка даты и времени. 28

Задание 1. Создание и инициализация массивов

Задача

Создать обычный типизированный массив (класс System. Array), выполнить его инициализацию (заполнение начальными значениями) как при объявлении массива, так и в ходе выполнения программы, просмотреть массив поэлементно.

Решение

Рассмотреть все возможные способы описания и инициализации обычных типизированных массивов, создаваемых в. NET как экземпляры класса System. Array. Использовать операторы цикла для организации работы с элементами массивов.

НЕ нашли? Не то? Что вы ищете?

Обсуждение

До этого момента мы рассматривали простые типы данных. Теперь выясним, как можно хранить связанную, структурную информацию.

В платформе. NET для структурированных данных применяется понятие «коллекция». Термин «коллекция» (collection) относится к особому типу объектов, которые способны хранить внутри себя другие объекты и позволяет работать с ними или по отдельности, или как с группой.

Простейший тип коллекции – набор объектов, сгруппированный без какого-либо определенного порядка, его часто называют набором (set). Коллекция, в которой объекты хранятся в определенной последовательности, называется списком (list).

Среди различных типов коллекций, кроме списков и наборов, также различают: хэш-таблицы, стэки, очереди, сортированные списки. У них различная функциональность и каждый тип предназначен для решения определенных задач. В. NET коллекции описаны в пространстве имен System.Collection.

Начнем рассмотрение принципов работы с коллекциями с одной из форм коллекции – массивами данных. Массивы представляют собой простейшую форму структурированных данных, они являются коллекцией, хотя описаны в другом пространстве имен – System.Array.

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

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

Рассмотрим примеры описания массивов:

Dim B(19) As Integer – одномерный массив B из 20 целых чисел;

Dim SA(6) As String – одномерный массив SA на 7 строк;

Private SB(4, 1) As Integer – создание двумерного массива SB (5x2) целых чисел;

Public RA(1, 1, 1) As Double – трехмерный (2x2x2) массив RA вещественных чисел.

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

Например, был объявлен одномерный массив на две строки:

Dim SA(1) As String

Далее, в ходе программы выполняется инициализация элементов массива SA:

SA(0)="Пн"

SA(1)="Вт"

Не требуется помещать данные в каждый элемент массива, нет даже необходимости сохранять элементы последовательно.

Dim strHouse(4) As String

strHouse(1)=”Jones”

strHouse(4)=”Goldstein”

strHouse(3)=”Soprano”

В случае двумерного или любого n-мерного массивов необходимо четко соблюдать систему индексации элементов массива:

SB(0,0)=1: SB(0,1)=2

SB(1,0)=3: SB(1,1)=4

RA(0,0,0)=0.1: RA(0,0,1)=0.1

RA(0,1,0)=0.3: RA(0,1,1)=0.4

Инициировать (задавать начальные значения) массив можно непосредственно при его описании. В этом случае есть дополнительные синтаксические особенности для строк и n-мерных массивов. Рассмотрим эти особенности в примерах:

Dim SA() As String = {"Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"} – обратите внимание на фигурные скобки и кавычки;

Private SB(,) As Integer = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}} – обратите внимание на пары фигурных скобок;

public RA( , , ) as Double={{{0.1,0.2},{0.3,0.4}},{{0.5,0.6},{0.7,0.8}}}

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

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

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

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

Массивы особенно полезны, если нужно обрабатывать целый набор элементов, как если бы это был один элемент. Например, изменить нормы оплаты трем сотрудникам можно следующим образом:

Dim EmployeePay(2) as Integer

Dim ExtraPay As Integer=1000

EmployeePay(0)= EmployeePay(0)+ ExtraPay

EmployeePay(1)= EmployeePay(1)+ ExtraPay

EmployeePay(2)= EmployeePay(2)+ ExtraPay

Но, используя структуру массива, можно сделать то же самое гораздо короче:

Sub Main()

Dim EmployeePay() As Integer = {20000, 15000, 25000}

Dim ExtraPay As Integer = 1000

Dim i As Integer

For i = 0 To 2

Console. Write(EmployeePay(i).ToString & ControlChars. Tab)

EmployeePay(i) = EmployeePay(i) + ExtraPay

Console. WriteLine(EmployeePay(i).ToString)

Next

Console. ReadLine()

End Sub

Управляющая структура For..Next позволяет сделать запись обработки элементов массива значительно короче. Кроме For..Next для перебора элементов массива могут быть использованы любые другие конструкции циклов, например, For Each..Next.

Sub Main()

Dim EmployeePay() As Integer = {20000, 15000, 25000}

Dim ExtraPay As Integer = 1000

Dim i As Integer

For Each i In EmployeePay

Console. Write(i. ToString & ControlChars. Tab)

i = i + ExtraPay

Console. WriteLine(i. ToString)

Next

Console. ReadLine()

End Sub

Массивы реализованы в. NET в классе System.Array, то есть, создавая массив для хранения переменных любого типа, мы создаем объект класса System. Array. Класс имеет набор свойств и методов, которые можно вызывать с именем объекта. Например, AA. GetUpperBound(0) возвращает значение верхней границы одномерного массива AA (индекс 0 показывает размерность массива) или AA. Clear очищает весь массив от значений. Использование методов и свойств класса System. Array рассмотрено в примерах ниже.

Самостоятельно

Создайте приложение, в котором используются n-мерные массивы; организован ввод элементов в описанные структуры и вывод их на экран.

Ниже представлены примеры использования операторов цикла для организации перебора всех элементов массива:

Sub Main()

Dim List() As String = {"Это", "просто", "тест"}

Dim str As String

For Each str In List

Console. WriteLine(str)

Next

Console. ReadLine()

End Sub

А эта программа отобразит в консоли число “2”:

Sub Main()

Dim AA(,) As Integer = {{3, 2, 6}, {9, 4, 6}}

Console. WriteLine(AA(0, 1).ToString)

Console. ReadLine()

End Sub

Аналогично, следующая консоль покажет число “7”:

Sub Main()

Dim AA(,,) As Integer = {{{3, 5}, {2, 7}}, {{0, 1}, {9, 8}}}

Console. WriteLine(AA(0, 1, 1).ToString)

Console. ReadLine()

End Sub

Перебор элементов одномерного массива удобно выполнять в счетном цикле:

Sub Main()

Dim AA() As Integer = {1, 2, 3}

Dim k As Integer

For k = 0 To AA. GetUpperBound(0)

Console. WriteLine(AA(k).ToString)

Next

Console. ReadLine()

End Sub

В случае двумерного массива (2x3) вывод можно организовать следующим образом:

Dim AA(,) As Integer = {{1, 2, 3}, {1, 2, 3}}

Dim j, k As Integer

For j = 0 To AA. GetUpperBound(1)-1

For k = 0 To AA. GetLength

Console. Write(AA(j, k).ToString & ControlChars. Tab)

Next

Console. WriteLine()

Next

Console. ReadLine()

End Sub

Фактически, не ограничивает массивы одной или двумя размерностями; можно объявить массив с максимум 60 размерностями, если потребуется. Однако использовать массив более чем с тремя размерностями не имеет смысла, лучше поискать другое решение (например, базы данных).

Задание 2. Изменение размеров массива

Задача

Изменять значение размера массива в ходе программы с сохранением данных или без сохранения.

Решение

Использовать команду Visual . ReDim с ключевым словом Preserve при необходимости сохранения данных в массиве или ReDim без ключа Preserve при новой инициализации массива.

Обсуждение

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

Например, если массивы первоначально описаны как

Dim B(19) As Integer

Dim SA(6) As String

Private SB(4, 1) As Integer

Public RA(1, 1, 1) As Double,

то в дальнейшем можно переопределить границы этих массивов следующим образом:

Redim B(20)

ReDim SA(1)

После переопределения массив B будет состоять из 21 элемента типа Integer, массив SA будет состоять из двух элементов типа String (индекс первого элемента равен нулю, второго – единице).

При переопределении массива можно использовать ключевое слово Preserve – в этом случае будут сохранены значения элементов, существовавших до переопределения. Таким образом, достаточно будет задать только значения новых элементов, если размер массива увеличился.

ReDim Preserve SB(4,3)

ReDim Preserve RA(1,1,2)

После переопределения двумерный массив SB будет состоять из двадцати элементов (пять умножить на четыре) типа Integer, трехмерный массив RA будет состоять из двенадцати элементов (2*2*3) типа Double.

Если размерность одномерного массива заранее неизвестна, можно при первоначальном определении не указывать ее вообще, а указать в дальнейшем в операторе ReDim.

Например, это можно сделать так:

Dim SA() As String, N As Integer

N=1

ReDim SA(N)

SA(0)=”Пн”

SA(1)=”Вт”

Еще одной особенностью коллекций и массивов является то, что их можно использовать как источник данных для элементов управления форм. Например, если в Windows форме поместить элемент управления ComboBox (или Listbox), то можно отобразить массив значений в этом элементе управления:

Private Sub Form12_Load(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles MyBase. Load

Dim AA() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9}

ComboBox1.DataSource = AA

Label1.Text = ComboBox1.SelectedItem

End Sub

Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles ComboBox1.SelectedIndexChanged

Label1.Text = ComboBox1.SelectedItem

End Sub

Самостоятельно

1.  Определите пустой числовой одномерный массив. Выполните отображение массива на элемент управления ListBox (можно ComboBox) в Windows приложении.

2.  Организуйте ввод информации в массив в диалоговом режиме.

3.  В коде программы изменяйте размер массива с сохранением всех внесенных данных.

Объявленный массив будет обрабатываться несколькими процедурами (обработчиками событий), следовательно, массив должен быть описан с областью видимости уровня формы:

Dim AA() As Integer

Private Sub Form12_Load(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles MyBase. Load

Ваш код загрузки формы

End Sub

Private Sub Button1_Click(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles Button1.Click

Ваш код щелчка по командной кнопке

End Sub

Задание 3. Поиск в массиве

Задача

Найти элемент массива с нужным содержанием.

Решение

Использовать методы и свойства класса System. Array.

Обсуждение

Допустим, требуется выяснить, в каком из элементов массива содержится некоторое значение. Класс System.Array предоставляет совместно используемый метод (shared-метод) IndexOf, который возвращает позицию первого найденного включения в заданном массиве. Например, чтобы узнать, в каком из элементов массива содержится строка «Самолет», можно использовать выражение:

Array. IndexOf(A, “Самолет”), где A – имя массива.

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

Array. IndexOf(MyArray, “Самолет”,3) – поиск начинается с индекса 3.

Можно «пройтись» по всем элементам массива и найти весь перечень включений.

Sub Main()

Dim A() As String = {"Самолет", "вертолет", "Самолет", "пилот"}

Dim i, Index As Integer

For i = 0 To 3

Index = Array. IndexOf(A, "Самолет", i)

Console. WriteLine("Строка 'Самолет' встречается в :" & Index. ToString)

Next

Console. ReadLine()

End Sub

Самостоятельно

1.  Создайте строковый массив. Отобразите его содержание в элементе управления ListBox (можно и в ComboBox).

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

3.  Удалите эти слова как в первом ListBox, так и в массиве.

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

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

Опишем исходный массив:

Dim Words() As String = {"Это", "текст", "пробы", "поиска", "буквы", "т"}

Создадим пустой динамический массив для переноса в него найденных слов:

Dim NewWords() As String

Напишем обработчик события загрузки формы:

Private Sub Form10_Load(ByVal sender As Object, ByVal e As System. EventArgs) Handles MyBase. Load

ListBox1.Items. AddRange(Words)

End Sub

В качестве обработчика щелчка по кнопке «Найти и копировать» напишем процедуру поиска слов и записи их в Listbox2:

Private Sub Button1_Click(ByVal sender

As System. Object, ByVal e As System. EventArgs) Handles Button1.Click

Dim str As String

Dim i As Integer

For Each str In Words

If str. Substring(0, 1).ToUpper = "Т" Then

ReDim Preserve NewWords(i)

NewWords(i) = str

i += 1

End If

Next

ListBox2.Items. AddRange(NewWords)

End Sub

По щелчку на кнопке «Удалить» необходимо удалить найденные слова в ListBox1:

Private Sub Button2_Click(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles Button2.Click

Dim str As String

For Each str In NewWords

ListBox1.Items. Remove(str)

Next

End Sub

Алгоритм удаления слов из исходного массива попробуйте придумать самостоятельно. В качестве варианта можно предложить: переносить в новый массив не слова на букву “т”, а наоборот, слова не на букву “т”. Тогда вы получите требуемую строку в новом массиве.

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

Задание 4. Тип коллекции – набор объектов (set)

Задача

Рассмотреть реализацию простейшего типа коллекций – набора объектов в. NET.

Решение

Используйте коллекцию – класс System.Enum.

Обсуждение

Простейшим типом коллекций является набор объектов, сгруппированных без какого-либо определенного порядка; его часто называют набором (set). Набор, который состоит из {объект1, объект2, объект3}, эквивалентен по своим функциям набору, состоящему из {объект1, объект2, объект3}; порядок здесь не имеет значение.

В. NET наборы или перечисления реализованы в классе System. Enum.

В каких случаях удобно использовать перечислений?

Часто переменной приходится присваивать одно из нескольких связанных предопределенных значений (констант). В таких случаях можно создать перечисляемый тип данных, позволяющий группировать значения. Перечисления связывают набор целочисленных констант с одним именем, которое можно использоваться в коде. Например, в следующем коде создается тип Enum переменной ВременаГода, которая используется для определения четырех связанных между собой констант: лето, осень, весна, зима со значениями 0, 1, 2, 3 соответственно:

Imports System. Enum

Module Module1

При описании перечисления необходимо использовать следующий синтаксис:

Enum ВременаГода

лето

осень

весна

зима

End Enum

При использовании перечисления необходимо объявить экземпляр перечисления:

Sub Main()

Dim x As ВременаГода= ВременаГода. весна

Console. WriteLine(x)

Console. WriteLine(x. ToString)

Console. ReadLine()

End Sub

End Module

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

Самостоятельно

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

Imports System. Enum

Enum inst

Международный_банковский_институт

Политехнический_Университет

Технологический_Институт

End Enum

Private Sub Button1_Click(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles Button1.Click

Dim r As inst

Dim Stud() As String = {"Сидоров", "Волнушкин", "Сыроежкин"}

MsgBox(Stud(0) & ControlChars. Tab & r. Международный_банковский_институт. ToString)

MsgBox(Stud(1) & ControlChars. Tab & r. Политехнический_Университет. ToString)

MsgBox(Stud(2) & ControlChars. Tab & r. Технологический_Институт. ToString)

End Sub

Задание 5. Использование динамического класса ArrayList

Задача

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

Решение

Используйте коллекции – класс System.Collections.ArrayList.

Обсуждение

Обычные массивы в. NET основаны на классе Array. Как в этом убедиться? Создайте любой массив и попробуйте вызвать методы и свойства созданного вами объекта. Вы убедитесь в том, что функциональность присутствует. Например, ваш объект поддерживает свойства, содержащие длину массива, его границы, есть методы сортировки, реверса, очистки и многое другое.

Sub Main()

Dim AA() As Integer = {3, 23, 56, 1, 34, 76, 2}

Console. WriteLine(AA. GetLength(0))

AA. Sort(AA)

Dim i As Byte

For i = 0 To AA. GetUpperBound(0)

Console. WriteLine(AA(i))

Next

AA. Reverse(AA)

For i = 0 To AA. GetUpperBound(0)

Console. WriteLine(AA(i))

Next

Console. ReadLine()

End Sub

Но класс Array нельзя назвать динамическим, так как он не позволяет добавлять и удалять элементы так же, как это, например, реализовано в элементах управления ListBox или ComboBox. Рассмотрим другой класс – ArrayList, являющийся динамическим классом.

Класс ArrayList довольно прост. Для добавления элементов в него служит метод Add, а для удаления – Remove, RemoveRange и RemoveAt. Два последних метода удаляют элементы на основе номеров из индексов, тогда как Remove ищет и удаляет указанный элемент. Вместо метода GetLength, реализованного в классе System. Array, класс ArrayList включает эквивалентное свойство Count.

Sub Main()

Dim List As New ArrayList

List. Add("blue")

List. Add("green")

List. Add("yellow")

List. Add("red")

Dim Item As String

For Each Item In List

Console. WriteLine(Item)

Next

Console. ReadLine()

End Sub

Удаление элемента “blue” по номеру индекса выполняется методом RemoveAt:

List. RemoveAt(0)

For Each Item In List

Console. WriteLine(Item)

Next

Console. ReadLine()

End Sub

Удаление элемента “green” с использованием поиска выполняется методом Remove:

List. Remove("red")

For Each Item In List

Console. WriteLine(Item)

Next

Console. ReadLine()

Console. WriteLine("Количество строк в масиве List: " & List. Count. ToString)

Система определения емкости класса ArrayList заключается в следующем: при создании объекта ArrayList для него выделяется внутренний буфер на 16 элементов. Если требуется хранить большее число элементов, размер буфера удваивается до 32, 64 и так далее. Если вам заранее известно, сколько элементов будет содержать объект ArrayList, вы можете зарезервировать требуемое пространство сразу, указав его в конструкторе класса ArrayList. Это несколько повысит быстродействие вашей программы.

Резервирование места для 50 элементов:

Dim List As New ArrayList(50)

Класс ArrayList слабо типизирован, то есть, все свои элементы он хранит как базовые типы Object. Поэтому при извлечении элемента его нужно привести к соответствующему типу:

Dim str As String = CType(List(0), String)

Такое поведение может вызывать проблемы, потому что нет способа гарантировать, что в массив не добавлен объект неверного типа.

Для быстрого добавления ряда элементов в объект ArrayList может служить метод AddRange:

Sub Main()

Dim List As New ArrayList

Dim Colors() As String = {"green", "blue", "red", "yellow"}

List. AddRange(Colors)

Dim Item As String

For Each Item In List

Console. WriteLine(Item)

Next

Console. ReadLine()

End Sub

Преобразование ArrayList в обычный типизированный массив можно выполнить методом ToArray. Это преобразование полезно выполнять, так как использовать объект класса ArrayList удобно для быстрой работы с массивом значений, но для контроля типов хранимых значений, затем можно выполнить обратное преобразование в обычный массив.

Рассмотрим код функции, которая ищет в предложении определенные слова и добавляет их в ArrayList. По завершении этой операции ArrayList преобразуется в массив строк. Метод ToArray возвращает объект Array, который нужно преобразовать в строго типизированный массив при помощи функции CType.

Private Function FindAnimalWords(ByVal sentence As String) As String()

‘Заполнение набора

Dim Words As New ArrayList

Dim Word As String

For Each Word In sentence. Split()

Select Case Word

Case "собака", "кошка", "мышка", "слон"

Words. Add(Word)

End Select

Next

‘Преобразование ArrayList в массив и приведение общего массива

‘строго типизированному массиву с помощью функции CType()

Return CType(Words. ToArray(GetType(String)), String())

End Function

Самостоятельно

1.  Выполните тестирование разработанной функции.

2.  Отсортируйте элементы полученного массива или объекта класса ArrayList, используйте методы Array. Sort и ArrayList. Sort.

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

Классы Array и ArrayList имеют перегруженную версию метода Sort, которая принимает индексы, определяющие сортируемое подмножество, и собственный объект ICompare, способный выполнять специализированную сортировку, например, List. Sort(3,5,Nothing) – сортировка пяти элементов, начиная с индекса 3 (или 4-го элемента).

4.  Примените перегруженный метод Sort для выполнения сортировки только указанного подмножества вашего множества данных.

Задание 6. Использование хэш-таблицы – коллекции Hashtable

Задача

Вам нужен словарный (основанный на применении ключей) набор, или вы хотите заменить стандартный массив System. Array на более эффективный альтернативный вариант.

Решение

Используйте класс System. Collections. Hashtable.

Обсуждение

Класс Hashtable – это словарный набор, каждый элемент которого индексируется по уникальному значению. Набор Hashtable идеален, когда нужно быстро извлечь отдельные элементы, потому что он позволяет искать элементы по соответствующим значениям ключа, а не путем перебора всех элементов. Однако в отличие от массивов Array или класса ArrayList он не поддерживает доступ к элементам по номерам их индексов.

При работе с хэш-таблицей вы должны решить, какие данные использовать для индексации элементов. Эта информация должна быть уникальной. Она может быть основана на параметрах самого объекта, или ее можно генерировать по требованию в форме GUID (Globally Unique Identifier – глобальный уникальный идентификатор, целое число, включающее 128 двоичных разрядов; в. NET входит структура GIUD, имеющая метод GUID. NewGuid) или порядкового номера.

Например, мы можем предполагаем использовать в качестве индексов коды стран (US, UK) и хотим получять название соответствующих стран, чтобы выводить их на экран. Ниже приводится пример объекта Hashtable, в котором хранятся коды и полные названия стран. Строка используется и как ключ (код страны) для элемента, и как значение элемента (название страны).

¾  UK United Kingdom

¾  US United States

¾  DE Germany

Рассмотрим все особенности применения хэш-таблиц:

1.  Элементы ключ/значение. Каждый элемент в Hashtable содержит больше информации, чем элемент в Array или ArrayList, то есть он «интеллектуальнее». Объект Hashtable меньше связан с хранением данных в списке и больше – с поиском и извлечением информации.

2.  Вставка элементов. При использовании Hashtable можно добавлять любое необходимое число пар ключ/значение. При использовании простых массивов необходимо заранее указывать их размер, а потом этот размер трудно изменять. В случае использования объекта Hashtable (как и ArrayList) подобных ограничений не существует.

3.  Удаление элементов. Удаление элементов из объектов Hashtable происходит с той же легкость, что и их добавление. Подобно ArrayList, Hashtable сам размещает в памяти пары ключ/значение.

4.  Быстрый поиск информации. Коллекция Hashtable обеспечивает быстрый поиск информации, но в сравнении с ArrayList процесс поиска выполняется медленнее.

Рассмотрим использование объекта Hashtable на конкретном примере. Создадим объект класса Hashtable точно так же, как это делается в случае ArrayList или любого другого класса. NET:

Sub Main()

Dim myHashTable As New Hashtable

Затем добавляем пары ключ/значение. Напомним, что ключ – это то же самое, что индекс позиции,.a значение – хранимые данные. Каждый элемент запоминается с помощью метода Add().

myHashTable. Add("UK", "United Kingdom")

myHashTable. Add("US", "United States")

myHashTable. Add("DE", "Germany")

Dim CountryName As String

Этот оператор вернет значение Germany. Ключи чувствительны к регистру, поэтому, написав следующее myHashTable("de") мы получим нулевой результат.

CountryName = myHashTable("DE")

Console. WriteLine(CountryName)

Console. ReadLine()

End Sub

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

Sub Main()

Dim myHashTable As New Hashtable

myHashTable("UK") = "United Kingdom"

myHashTable("US") = "United States"

myHashTable("DE") = "Germany"

Dim CountryName As String

CountryName = myHashTable("DE")

Console. WriteLine(CountryName)

Console. ReadLine()

End Sub

Рассмотрим, каким образом производится перечисление элементов хэш-таблицы. При перечислении общего набора используется тип содержащихся в нем элементов. Класс Hashtable не поддерживает эту договоренность. Вместо этого вы должны просмотреть набор объектов DictionaryEntry. Каждому элементу набора соответствует один объект DictionaryEntry. Получить ключ элемента позволяет свойство DictionaryEntry.Key, а извлечь элемент набора – DictionaryEntry.Value.

Sub Main()

Dim myHashTable As New Hashtable

myHashTable("UK") = "United Kingdom"

myHashTable("US") = "United States"

myHashTable("DE") = "Germany"

Dim Item As DictionaryEntry

For Each Item In myHashTable

Console. WriteLine("Key: " & Item. Key. ToString)

Console. WriteLine("Object: " & Item. Value. ToString)

Console. WriteLine()

Next

Console. ReadLine()

End Sub

Самостоятельно

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

2.  Как и ArrayList, класс Hashtable хранит элементы как типы Object, поэтому, после извлечения элементов их нужно приводить к нужному типу. Выполните это самостоятельно.

3.  К числу других полезных методов и свойствам класса Hashtable относятся:

Метод Remove удаляет элемент из набора. Этот метод принимает ключ элемента, который нужно удалить.

Методы Contains и ContainsKey возвращают True или False в зависимости от того, имеется ли в хэш-таблице указанный ключ, а метод ConstainsValue – тоже, если есть указанный объект.

Свойство Keys возвращает набор всех ключей хэш-таблицы, а свойство Values – набор всех объектов.

Используйте все эти свойства и методы самостоятельно.

Задание 7. Использование коллекции SortedList

Задача

Вам нужен набор, автоматически сортируемый при каждом добавлении или удалении.

Решение

Класс System.Collections.SortedList сортирует элементы на основе их ключей.

Обсуждение

Класс SortedList – это основанный на применении ключей словарный набор, который постоянно поддерживает упорядоченное состояние своих элементов. Конечно, это замедляет добавление/удаление элементов. И все же эти операции с набором SortedList обычно выполняются быстрее, чем постоянная пересортировка объекта Array или ArrayList.

Самое важное в классе SortedList то, что он упорядочивает элементы, опираясь на значение ключей, а не сами объекты. Рассмотрим на конкретном примере использование сортированного словарного набора.

Например, можно использовать отсортированный список для хранения статей в словаре (табл. 1).

Табл.1.

Статьи энциклопедии

Ключ

Значение

Aardvark (муравьед)

Крупное живущее в норах ночное копытное млекопитающее.

Amaryllis (амариллис)

Осенний цветок. Южно-африканское луковичное растение.

Armadillo (броненосец)

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

Создадим Windows-форму, включающую элементы управления Label, ComboBox и Button. Напишем процедуру загрузки формы:

Dim List As New SortedList

Private Sub Form10_Load(ByVal sender As Object, ByVal e As System. EventArgs) Handles MyBase. Load

Dim item As DictionaryEntry

List("Armadillo") = "Представитель семейства живущих в норах неполнозубых млекопитающих."

List("Amaryllis") = "Осенний цветок. Южно-африканское луковичное растение."

List("Aardvark") = "Крупное живущее в норах ночное копытное млекопитающее"

List("Artichoke") = "Высокое сложноцветное растение."

For Each item In List

ComboBox1.Items. Add(item. Key)

Next

End Sub

Напишем обработчик события для командной кнопки:

Private Sub Button1_Click(ByVal sender As System. Object, ByVal e As System. EventArgs) Handles Button1.Click

Label1.Text = List(ComboBox1.SelectedItem)

End Sub

Самостоятельно

1.  Разберите программный код, приведенный ниже, и используйте его для построения консольного или Windows приложения.

2.  Примените другой синтаксис для добавления полученных объектов в отсортированный список.

3.  Проверьте ситуацию в случае изменения имени файла в каталоге С:\.

Imports System. IO

Module Module1

Sub Main()

Dim Dir As New DirectoryInfo("C:\")

Dim Files() As FileInfo

Files = Dir. GetFiles

Dim List As New SortedList

Dim File As FileInfo

For Each File In Files

List. Add(File. Name, File)

Next

Dim Item As DictionaryEntry

For Each Item In List

Console. WriteLine(CType(Item. Value, FileInfo).Name)

Next

Console. ReadLine()

End Sub

End Module

В приведенном примере необходимо объяснить некоторые строки кода.

Так как процедура связана с поиском и отображение файловой структуры, для запуска этого кода необходимо импортировать пространство имен System. IO.

С использованием классов DirectoryInfo и FileInfo мы познакомимся подробнее в теме «Файлы и каталоги».

Задание 8. Использование очереди (набора, работающего по принципу FIFO)

Задача

Вам нужен набор, элементы которого будут извлекаться в порядке добавления.

Решение

Используйте класс System.Collections.Queue – набор, работающий по принципу «первый вошел, первым вышел» (first-in, first-out, FIFO).

Обсуждение

Очереди применяются для выполнения разных последовательных задач. Например, в очереди можно хранить список заданий, которые должен выполнить серверный компонент. Так как очередь является набором FIFO, ее самые старые элементы всегда обрабатываются первыми.

С концептуальной точки зрения, объект Queue – это массив с динамически изменяемыми размерами, хранящий объекты во многом аналогично объекту ArrayList. При создании очередь имеет емкость, равную 32 элементам (если в конструкторе вы не указали другое значение), удваивая ее при необходимости.

Для получения числа элементов очереди служит свойство Count, для добавления элемента – метод Enqueue, а для извлечения – метод Dequeue, который при этом удаляет элемент из очереди. Кроме того, метод Peek позволяет извлечь следующий элемент очереди без его удаления. Однако вы не можете извлекать объекты из очереди в произвольном порядке или по номеру индекса.

Рассмотрим создание и использование очереди на конкретном примере. Создадим простую очередь элементов и выполним чтение каждого из них:

Module Module1

Sub Main()

Создание очереди

Dim Q As New Queue

Заполнение очереди элементами

Q. Enqueue("Первый вошел")

Q. Enqueue("Второй вошел")

Q. Enqueue("Третий вошел")

Q. Enqueue("Четвертый вошел")

Q. Enqueue("Пятый вошел")

Для извлечения элементов очереди создадим объект String

Dim Item As String

Свойство Count позволяет организовать конечный цикл просмотра очереди

Do While Q. Count > 0

Извлечение из очереди происходит с одновременным удалением

Item = CType(Q. Dequeue, String)

Console. WriteLine("Вышел: " & Item)

Console. WriteLine(Q. Count. ToString & " осталось в очереди")

Loop

Очередь пуста

Console. ReadLine()

End Sub

End Module

Самостоятельно

Организуйте ввод числовых значений, включающих нули. Используйте очередь, как структуру, выполняющую прием входящей информации. Создайте на основе данных из очереди массив, не имеющий нулевых значений.

Задание 9. Использование стека (набора, работающего по принципу LIFO)

Задача

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

Решение

Используйте тип System. Collections. Stack, который представляет собой набор, работающий по принципу «последний вошел, первым вышел» (last-in, first-out, LIFO).

Обсуждение

Стеки применяются в случае, если требуется, чтобы последние добавленные элементы всегда обрабатывались первыми. Как и объекты ArrayList и Queue, Stack является массивом с динамически изменяемыми размерами. При создании стек имеет емкость, равную 32 элементам (если вы не указали другое значение в конструкторе), удваивая ее при необходимости.

Для получения числа элементов служит свойство Count, для добавления элемента на вершину стека – метод Push, а для извлечения – метод Pop, который при этом удаляет элемент из стека. Кроме того, метод Peek позволяет извлечь следующий элемент стека, не удаляя его. Однако вы не можете извлекать объекты из стека в произвольном порядке или по номеру индекса.

В следующем примере создается простой стек и выполняется чтение всех его элементов:

Module Module1

Sub Main()

Создадим структуру стек

Dim S As New Stack

Разместим в стеке данные

S. Push("Первый вошедший элемент")

S. Push("Второй вошедший элемент")

S. Push("Третий вошедший элемент")

S. Push("Четвертый вошедший элемент")

S. Push("Пятый вошедший элемент")

Извлечем элементы из стека. После извлечения элементов стек пуст.

Dim Item As String

Do While S. Count > 0

Item = CType(S. Pop, String)

Console. WriteLine("Вышел " & Item)

Console. WriteLine(S. Count. ToString & " элементов осталось в стеке")

Loop

Console. ReadLine()

End Sub

End Module

Самостоятельно

Используйте структуру стека для хранения числовой информации, на основе которой создайте типизированный массив только четных чисел.

Задание 10. Методы: процедуры и функции

Задача

Научиться компоновать фрагменты кода программы в виде небольших законченных подпрограмм, оформленных специальным образом.

Решение

Описать, в соответствии с определенным в синтаксисом, повторяющиеся фрагменты кода в виде процедур или функций и обеспечить доступ к этим структурам.

Обсуждение

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

По своей функциональности методы условно можно разделить на процедуры (sub) и функции (function). Главным отличием функции от процедуры является то, что функция возвращает под своим именем результирующее, вычисленное при исполнении кода функции, значение, процедура такого значения не возвращает.

Сигнатура (описание заголовка) функции выглядит следующим образом:

Private|Public Function Имя_Функции(ByVal | ByRef параметры As Тип_Параметров) As Тип_Результата

Соответственно, сигнатура (описание заголовка) процедуры выглядит так:

Private|Public Sub Имя_Процедуры(ByVal | ByRef параметры As Тип)

Ключевые слова Public, Private устанавливают уровень доступа к методам: или метод доступен только в текущем модуле, форме – Private, или он доступен глобально, в любой имеющейся структуре – Public.

Ключевые слова ByRef, ByVal описывают способ передачи входных параметров: или по ссылке (By Reference) или по значению (By Value).

В случае передачи параметра по ссылке (ByRef) компилятор связывает метод непосредственно с самим значением переменной, хранящимся в памяти.

В случае передачи параметра по значению (By Value) компилятор выделяет место в памяти для хранения копии значения переменной, сама переменная остается неизменной. По умолчанию (то есть при отсутствии указания на способ передачи параметра) используется передача параметров по значению (ByVal).

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

Досрочное завершение методов выполняется специальными операторами Exit Sub или Exit Function, соответственно.

Остается сказать, что для выдачи вычисленного значения из функции (говорят: функция возвращает значение) используется ключевое слово Return:

Return результат вычисления.

Приступим к изучению технологии использования методов в коде программы.

1.  Рассмотрим пример, иллюстрирующий передачу параметров по ссылке и по значению.

Module Module1

Sub Main()

Dim X As Integer=45

proba1(X)

Console. WriteLine("Переменная вне процедуры = " & X)

proba2(X)

Console. WriteLine("Переменная вне процедуры = " & X)

Console. ReadLine()

End Sub

2.  Напишем код процедуры proba1(), входной параметр, для которой будет передаваться по значению (ByVal), то есть копия параметра. В ходе выполнении процедуры proba1() к значению объекта X будет добавляться число 5, другими словами, X изменяется в процедуре.

Имена формальных и фактических переменных могут быть как одинаковыми, так и разными. Это ни на что не влияет.

Sub proba1(ByVal X As Integer)

X += 5

Console. WriteLine("Переменная, переданная по значению, в процедуре = " & X)

End Sub

3.  Напишем процедуру proba2(), в которую входной параметр будет передавать по ссылке (ByRef), то есть, обеспечен доступ непосредственно к самой переменной X. Пусть процедура proba2() так же изменяет значение X, увеличивая его на 5.

Sub proba2(ByRef X As Integer)

X += 5

Console. WriteLine("Переменная, переданная по ссылке, в процедуре = " & X)

End Sub

End Module

4.  Соберите консольное приложение и запустите его на исполнение. Убедитесь в том, что передача параметров по ссылке и по значению имеет различные последствия для самих переменных.

5.  Тот же эффект наблюдается для любых типов данных, например, для строк. Рассмотрим этот случай:

Module Module1

Sub Main()

Dim S As String S="Основная строка"

proba1(S)

Console. WriteLine("Переменная вне процедуры = " & S)

proba2(S)

Console. WriteLine("Переменная вне процедуры = " & S)

Console. ReadLine()

End Sub

Sub proba1(ByVal S As String)

S = "Измененная строка"

Console. WriteLine("При передаче строки по значению: " & S)

End Sub

Sub proba2(ByRef S As String)

S = "Измененная строка"

Console. WriteLine("При передаче строки по ссылке: " & S)

End Sub

End Module

6.  Теперь рассмотрим синтаксис описания функции и ее вызов в коде основной программы на классическом примере нахождения корней квадратного уравнения.

Функция для вычисления количества корней квадратного уравнения и значений этих корней может выглядеть, например, так:

Public Function Roots(ByVal A As Double, ByVal B As Double, ByVal C As Double, ByRef X1 As Double, ByRef X2 As Double) As Integer

Dim D As Double

If A = 0 Then

Return 0

Else

D = B * B - 4 * A * C

If D > 0 Then ' решений два

Return 2

X1 = (-B + System. Math. Sqrt(D)) / (2 * A)

X2 = (-B - System. Math. Sqrt(D)) / (2 * A)

ElseIf D = 0 Then ' решений одно

Return 1

X1 = - B / (2 * A)

Else ' решений нет

Return 0

End If

End If

End Function

Обратите внимание на различные способы передачи входных параметров в функцию Roots: значения коэффициентов не должны изменяться, поэтому они передаются по значению (ByVal), а корни уравнения вычисляются, переменные для их значений передаются в функцию по ссылке (ByRef).

7.  Пример обращения к функции напишем следующий:

Sub Main()

Dim A, B, C, X1, X2 As Double, nSol As Integer

Console. WriteLine("Введите значение A")

A = Console. ReadLine

Console. WriteLine("Введите значение B")

B = Console. ReadLine

Console. WriteLine("Введите значение C")

C = Console. ReadLine

nSol = Roots(A, B, C, X1, X2)

If nSol = 0 Then

Console. WriteLine("Решений нет")

ElseIf nSol = 1 Then

Console. WriteLine("X1= " & X1)

Else

Console. WriteLine("X1= " & X1 & "X2= " & X2)

End If

Console. ReadLine()

End Sub

Воспользуемся текущим примером, чтобы ввести понятие «импортирование пространств имен». Вы должны были обратить внимание на вызов стандартной математической функции для вычисления корня квадратного SQRT(). Функция находится в пространстве имен System. Math, которое можно было заранее импортировать в проект, то есть сделать доступным следующим оператором, написанным до кода всех процедур:

Impotrs System Math

Теперь к функции SQRT() можно обращаться просто по имени, например, X1 = (-B + Sqrt(D)) / (2 * A). Самостоятельно внесите соответствующие изменения в код функции и снова протестируйте приложение.

Пространства имен можно перечислять через запятую, используя одно ключевое слово Imports.

Самостоятельно

Пусть требуется создать столбец текстовых строк фиксированной ширины. Например, для оформления вывода текста в элементах управления ListBox, ComboBox или просто для вывода в окне консоли или для печати.

Другими словами, напишите и протестируйте в различных практических случаях метод, который дополняет строки текстового массива пробелами в соответствии с самой длинной строкой в массиве. Метод может быть реализован как функция, или как процедура, по-вашему, усмотрению.

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

Для решения задачи можно использовать методы класса System. String PadLeft или PadRight, дополняющие строку пробелами слева или справа, соответственно, до указанного размера, который является входным параметром метода.

Поскольку, пока, у вас нет еще достаточного опыта написания процедур и функций, этот пример начнем рассматривать вместе, но тестирование метода вы выполните самостоятельно.

В листинге ниже представлена процедура, позволяющая выполнять перечисленные действия. Используйте ее для построения интерфейса консоли или Windows приложения.

Private Sub PadString(ByVal strings() As String, ByVal padLeft As Boolean)

'Определение максимальной длины строки в массиве

Dim MaxLength As Integer = 0

Dim str As String

For Each str In strings

If str. Length > MaxLength Then MaxLength = str. Length

Next

'Дополнение всех строк массива до максимальной длины.

'Цикл For..Next здесь не годится, потому что допускает только чтение,

'а нам нужно изменять строки

Dim i As Integer

For i = 0 To strings. GetUpperBound

If padLeft Then

strings(i) = strings(i).PadLeft(MaxLength)

Else

strings(i) = strings(i).PadRight(MaxLength)

End If

Next

End Sub

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

Sub Main()

Dim Fruits() As String = {"яблоки", "манго", "бананы", "апельсины"}

Dim Colors() As String = {"красный", "желтый", "оранжевый", "зеленый"}

PadStrings(Fruits, True)

PadStrings(Colors, False)

Dim i As Integer

For i = 0 To Fruits. GetUpperBound

Console. WriteLine(Fruits(i) & " " & Colors(i))

Next

Console. ReadLine()

End Sub

Задание 11. Обработка даты и времени

Задача

Получить текущую дату и время. Выполнить математические действия над датами и интервалами времени.

Решение

Использовать объекты классов или структур System. Data, System. DateTime, System. TimeSpan, их свойства и методы.

Обсуждение

Для работы с информацией о дате и времени. NET предоставляет структуры System.DateTime (хранит дату и время, например, 20.01.2006 12:00:00) и System.TimeSpan (хранит интервал времени, например 3 часа), а также класс System.Date. При объявлении переменных формата дата/время вы можете использовать оба типа данных (DateTime и Date), как например, в листинге ниже. Объекты типа TimeSpan удобно использовать при вычислениях с датами.

Для определения текущих значений даты и времени можно использовать методы Now и ToDay, которые возвращают значение типа Date, содержащее текущие дату и системное время. Этими методами располагает, как DateTime, так и Date. Рассмотрим на примере определение текущей даты и времени:

Sub Main()

Dim d As Date

Console. WriteLine(d. Today)

Console. WriteLine(d. Now)

d = d. Today. AddDays(30)

Console. WriteLine(d)

Dim d1 As DateTime

Console. WriteLine(d1.Today)

Console. WriteLine(d1.Now)

Console. WriteLine(d1.Today. AddYears(1))

Console. ReadLine()

End Sub

С помощью символа (#) происходит непосредственное присваивание значения даты. Вычисления с датами удобно выполнять методами Add или Subtract (оба типа имеют аналогичные методы). Результат метода Subtract удобно присвоить не просто числовому объекту, а объекту типа TimeSpan, который в свою очередь, имеет ряд свойств и методов.

Sub Main()

Dim d As Date = #10/10/2005#

Console. WriteLine(d)

Dim dd As TimeSpan

dd = DateTime. Now. Subtract(d)

Console. WriteLine(dd. Days)

Console. ReadLine()

End Sub

При работе с датами нельзя использовать операторы сравнения (< и >). Однако вы можете получить число, представляющее интервал TimeSpan, используя свойства, TotalHourse, TotalMinutes, TotalMilliseconds или Ticks, которое может участвовать в операции сравнения как операнд.

Самостоятельно

Создайте приложение, позволяющее определять, на какой день недели выпадает конкретная дата, является ли указанный год високосным и сколько дней содержится в конкретном месяце.

Используйте пространство имен System.Globalization, в котором определены классы, поддерживающие календарные даты, связанные с региональными стандартами. В число этих классов входят классы GregorianCalendar (западный стандарт), HebrewCalendar, JulianCalendar, JapaneseСalendar и так далее. В этих классах инкапсулирован ряд методов, которые позволят вам решить поставленную задачу:

¾  GetDaysInMonth – возвращает число дней в конкретном месяце;

¾  GetDaysInYear – возвращает число дней в конкретном году;

¾  IsLeapYear – возвращает True, если конкретный год является високосным и так далее.

Пример объявления объекта класса GregorianCalendar и обращения к его функциям представлен ниже:

Module Module1

Sub Main()

Dim calendar As New System. Globalization. GregorianCalendar

Console. WriteLine ("Количество дней в феврале 2006 года " & calendar. GetDaysInMonth(2006, 2, 0))

Console. ReadLine()

End Sub

End Module