Практический пример: специализированная сортировка
Практический пример: специализированная сортировка
Предыдущие примеры выглядят искусственно и относятся к категории «игрушечных программ». В этом разделе мы покажем, как использовать делегаты при специализированной сортировке — одной из стандартных областей применения функций обратного вызова. Общая идея заключается в том, что один метод сортировки в зависимости от ситуации может использовать разные критерии сортировки. Предположим, у вас имеется массив имен: «Mike Item», «Dave Mendlen», «Alan Carter», «Tony Goodhew», «Ari Bixhorn», «Susan Warren»-.
Если вызвать метод Sort класса Array, сортировка будет произведена по именам. А если вы хотите отсортировать массив по фамилиям?
Возможный подход к решению этой задачи описан в главе 5 — вы можете написать собственную реализацию интерфейса IComparer и передать ее Sort. Применение обратного вызова на базе делегатов выглядит чуть более элегантно и теоретически обладает большей гибкостью, поскольку программист может определить собственные процедуры сортировки, которые в конкретной ситуации работают эффективнее стандартных алгоритмов.
Чтобы массив поддерживал сортировку по именам, следует определить класс с несколькими методами Compare и при помощи делегата связать алгоритм сортировки с нужным методом Compare через механизм обратного вызова. В частности, это позволит динамически изменять критерий сортировки во время работы программы.
Прежде всего определяется класс, выполняющий сортировку. Чтобы избежать подробного обсуждения различных алгоритмов сортировки, мы воспользуемся простейшим алгоритмом волновой сортировки:
Начать с первого элемента.
Последовательно просмотреть все остальные элементы. Если очередной элемент окажется меньше текущего первого элемента, поменять их местами.
Начать со второго элемента, просмотреть все остальные элементы.
Продолжать до последнего элемента. Основной код волновой сортировки выглядит так:
For i =bottom To (top - bottom) For j =i + 1 To top
If Stuff(j) < Stuff(i))Then
temp = Stuff(i)
Stuff(i) = Stuff(j)
Stuff(j) = temp
End If
Next j
Next I
Чтобы реализовать этот алгоритм с применением функций обратного вызова, необходимо определить класс Special Sort с делегатом, используемым при обратном вызове. Код этого класса приведен ниже:
1 Public Class Special Sort
2 ' Определение делегата
3 Public Delegate Function SpecialCompareCallback(ByVal flrstString _
As String,ByVal secondString As String) As Boolean
4 ' Определение процедуры, вызываемой делегатом
5 Public Shared Sub IfySort(ByVal Stuff As String()._
ByVal MyCompare As SpecialCompareCallback)
6 Dim i, j As Integer
7 Dim temp As String
8 Dim bottom As Integer = Stuff.GetLowerBound(0)
9 Dim top As Integer = Stuff.GetUpperBound(0)
10 For i = bottom To (top = bottom)
11 For j = i + 1 To top
12 If MyCompare(Stuff(j). Stuff(i)) Then
13 temp = Stuff(i)
14 Stuff(1) - Stuff (j)
15 Stuff(j) = temp
16 End If
17 Next j
18 Next i
19 End Sub
20 End Class
В строке З определяется делегат, при помощи которого классу передается информация об используемом порядке сортировки. Делегат может инкапсулировать любую функцию, которая, как и все нормальные функции сравнения строк, получает два строковых параметра и возвращает логическую величину.
В строке 5 определяется общая процедура, одним из параметров которой является переменная с типом делегата. Таким образом, в ключевой строке 12:
If MyCompare(Stuff(j). Stuff(i)) Then
функция сравнения, инкапсулированная в делегате MyCompare, может относиться к другому классу! Например, если определить приведенный ниже класс, эта схема позволит использовать любой из его методов Compare (обратите внимание: методы Compare объявлены общими, поэтому для их вызова нам даже не нужно создавать конкретный экземпляр класса):
Public Class MyCustomCompare
Public Shared Function TheBasicComparetByVal firstString As String,
ByVal secondString As String) As Boolean
Return (firstString <- secondString)
End Function
Public Shared Function TheSpecialCompare(ByVal firstString As String.
ByVal secondString As String)As Boolean Dint tokensl,tokens2 As String()
tokensl = firstString.Split(Chr(32))
tokens2 = secondString.Split(Chr(32))
Return (tokensl(l) <- tokens2(l))
' Сравнение по фамилии!
End Function
End Class
Класс содержит две общие функции, которые ниже будут использованы для создания делегатов. Первая функция, TheBasicCompare, просто сравнивает строки в алфавитном порядке. Более интересная функция TheSpecialCompare предполагает, что строка передается в формате «имя фамилия», и сравнивает фамилии, выделяя их при помощи удобной функции Split.
Остается лишь создать экземпляры класса SpecialSort и делегаты. Это происходит в следующей функции Main (ключевые строки выделены жирным шрифтом):
1 Module Modulel
2 Sub Main()
3 Dim test()As String ={"Mike Iem"."Dave Mendlen"."Alan Carter".
4 "Tony Goodhew","An Bixhorn"."Susan Warren"}
5 ' Объявить переменную обратного вызова в форме класс.делегат
6 Dim MyCallBack As Special Sort.SpecialCompareCal1back
7 MyCallBack = AddressOf MyCustomCompare.TheBasicCompare
8 SpecialSort.MySort(test,MyCallBack)
9 Console.WriteLine("Here is a basic sort by FIRST name")
10 Dim temp As String
11 For Each temp In test
12 Console.WriteLine(temp)
13 Next
14 ' Передать другую процедуру сравнения
15 MyCallBack = AddressOf MyCustomCompare.TheSpecialCompare
16 Sped al Sort. MySort (test. MyCallBack)
17 Console.WriteLine()
18 Console.WriteLineC'Here is a sort by LAST name")
19 For Each temp In test
20 Console.WriteLine(temp)
21 Next
22 Console. ReadLine()
23 End Sub
24 End Module
В строке 6 объявляется «псевдоуказатель на функцию». Чтобы задать его значение, мы передаем адрес функции с правильной сигнатурой (строки 7-15). Поскольку функции объявлены общими, создавать экземпляр класса MyCustomCompare для этого не нужно.После создания делегата в строках 8 и 16 вызывается нужная процедура сортировки класса Special Sort. Поскольку при вызове MySort передается делегат, процедура обращается к классу MyCustomCompare и узнает, по какому критерию должно осуществляться сравнение.