Уокенбах Формулы в Excel
.pdfВозвращение максимального значения из рабочих листов
Чтобы определить максимальное значение (например, В1) на нескольких рабочих листах, используйте такую формулу:
=МАКС(Лист1:Лист4!В1)
Эта формула возвращает максимальное значение ячейки В1 листов Лист 1, Лист4 и всех листов, находящихся между ними. Но что делать, если после Лист4 нужно добавить новый лист (Лист5)? Формула автоматически не обновляется, поэтому нужно отредактировать ее и добавить ссылку на новый лист:
=МАКС(Лист1:Лист5!В1)
Следующая функция принимает аргумент одной ячейки и возвращает максимальное значение этой ячейки на всех рабочих листах рабочей книги. Например, приведенная ниже формула возвращает максимальное значение ячейки В1 на всех листах рабочей книги.
=MAXALLSHEETS(B1)
При добавлении нового листа редактировать формулу не нужно.
Function MAXALLSHEETS(cell As Range) Dim MaxVal As Double
Dim Addr As String
Dim Wksht As Object
Application.Volatile
Addr = cell.Range("Al").Address MaxVal = -9.9E+307
For Each Wksht In cell.Parent.Parent.Worksheets If Wksht.Name = cell.Parent.Name And _
Addr = Application.Caller.Address Then Else
If IsNumeric(Wksht.Range(Addr)) Then If Wksht.Range(Addr) > MaxVal Then
MaxVal = Wksht.Range(Addr).Value End If
End If Next Wksht
If MaxVal = -9.9E+307 Then MaxVal = 0 MAXALLSHEETS = MaxVal
End Function
Оператор For Each используетсядляобращения крабочей книге: cell.Parent.Parent.Worksheets
Родителем ячейки является рабочий лист, а родителем рабочего листа — рабочая книга. Таким образом, цикл Each-Next обрабатывает все рабочие листы в рабочей книге. Первый оператор If внутри цикла выполняет проверку на наличие функции в проверяемой ячейке. Если функция обнаружена, ячейка игнорируется во избежание ошибки циклическойссылки.
Функцию MAXALLSHEETS можно настроить для выполнения других вычислений по нескольким листам: Мин, СРЗНАЧ, СУММ И Т.Д.
560 |
Часть VI.Разработка пользовательских функ |
Функция SHEETOFFSET
Неоднократные недовольства приложением Excel (в том числе и Excel 2002) заключаются в недостаточной поддержке относительных ссылок на листы. Например, представим, что есть рабочая книга с несколькими листами, и на листе Лист2 вводится следующая формула:
=Лист1!
Такая формула прекрасно работает. Однако если копировать эту формулу на следующий лист (ЛистЗ), то формула также будет ссылаться на Лист1. Если вставить лист между Лист1 и Лист2, формула все равно продолжит ссылаться на Лист1 (скорее всего она должна ссылаться на только что вставленный лист). Фактически, нельзя создать формулу, которая бы ссылалась на рабочие листы относительным образом. Однако можно воспользоваться функцией SHEETOFFSET,чтобы избежать этих ограничений:
Функция SHEETOFFSET. Первый шаг
Приведенная ниже процедура функции VBА называется SHEETOFFSET. Function SHEETOFFSET(offset As Integer, Ref As Range)
'Возвращение содержимого листа для Ref при изменении листа Dim WksIndex As Integer
Application.Volatile
Wkslndex = Worksheetlndex(Application.Caller.Parent)
SHEETOFFSET = Worksheets(Wkslndex + offset).Range(Ref.Address) End Function
Функция SHEETOFFSETпринимает два аргумента:
• offset . Смещение листа, который может быть положительным, отрицательным или равным 0.
• ref. Ссылка на одну ячейку. Если аргумент offse t равен 0, то ссылка на ячейку не должна быть такой же, что и на ячейку, содержащую формулу. Иначе получится циклическая ошибка.
Следующая формула возвращает значение ячейки А1 листа, который расположен перед листом с формулой:
=SHEETOFFSET(-1;А1)
Следующая формула возвращает значение ячейки А1 листа, стоящего после листа с формулой: =SHEETOFFSET(1;А1)
Функция корректно работает в большинстве случаев. Например, можно скопировать формулу на другой лист, и относительные ссылки повлияют на все скопированные формулы. А если вставить рабочий лист, то ссылка на лист отрегулируется автоматически.
Однако у этой функции имеется недостаток: если в рабочей книге есть чужеродный рабочий лист (т.е. лист с диаграммой или диалоговые листы Excel 5), то функция может работать некорректно, поскольку попытается сослаться на ячейку на листе, которой там нет.
Функция SHEETOFFSET. Второй шаг
Представленную выше версию функции SHEETOFFSET можно улучшить. В этой версии функции используется вторая функция, которая называется Worksheetlndex. Эта функция возвращает индекс рабочего листа объекта Worksheet, принимаемого в качестве аргумента. Затем в этой функции используется специальное значение для определения другого рабочего
Глава 25. Примеры пользовательскихфункций VBA |
561 |
листа. Ниже приведена версия SHEETOFFSET, которая обычно игнорирует все чужеродные рабочие листы в рабочей книге.
Private Function Worksheetlndex(x As Worksheet) As Integer
1Возвращение индекса Worksheets (не Sheets) Dim wks As Worksheet, WksNum As Integer WksNum = 1
For |
Each |
wks |
In x.Parent.Worksheets |
|
If x.Name |
= wks.Name Then |
|
|
|
Worksheetlndex = WksNum |
|
|
|
Exit |
Function |
|
End |
If |
|
|
WksNum = WksNum + 1 |
||
Next |
wks |
|
|
End Function |
|
|
|
Обратите внимание, что поскольку функция Work she e Index не предназначена для использования в формуле, она описана ключевым словом Private . Таким образом предотвращается ее появление в диалоговом окне вставки функции.
Дополнительные пользовательские функции
В этом разделе рассматриваются дополнительные функции. Примеры демонстрируют некоторые специальные методы, которые можно реализовать в пользовательских функциях.
•Возвращение ошибки из функции
•Возвращение массива из функции
•Использование необязательных аргументов функции
•Использование неопределенного количества аргументов функции
•Использование функций Windows API
Возвращение значения Error
В некоторых случаях пользовательская функция должна возвращать значение, соответствующее ошибке. Предположим, что это функция REVERSETEXT, которая представлена ранее в этой главе.
Function REVERSETEXT(text) As String
' Возвращение аргумента в обратном порядке REVERSETEXT = StrReverse(text)
End Function
Функция возвращает обратное значение содержимого ячейки-аргумента (может быть текстом или значением). Если аргумент является многоячеечным диапазоном, то функция возвращает #ЗНАЧ!
Предположим, что эта функция должна работать только со строками. Если в аргументе содержится не строка, то функция должна возвратить значение ошибки (#Н/Д). Можно просто присвоить строку, которая выглядела бы как значение ошибки формулы Excel. Например:
REVERSETEXT = "#Н/Д"
562 |
Часть VI. Разработка пользовательских функ |
Хотя строка выглядит как значение ошибки, она не воспринимается другими формулами. Чтобы действительно возвратить значение ошибки, воспользуйтесь функцией VBA CVErr, которая преобразует номер ошибки в действительную ошибку.
К счастью, в VBA есть встроенные константы для ошибок, которые должны возвратиться пользовательской функцией. Эти константы перечислены ниже:
•xlErrDivO
•xlErrNA
•xlErrName
•xlErrNull
•xlErrNum
•xlErrRef
•xlErrValue
Ниже приведена функция REVERSETEXT:
Function REVERSETEXT(text) As Variant
' Возвращение аргумента в обратном порядке If Application.ISNONTEXT(text) Then REVERSETEXT = CVErr(xlErr)
Else
REVERSETEXT = StrReverse(text) End If
End Function
В этой функции используется функция Excel ISNONTEXT для определения того, является ли аргумент текстовой строкой. Если аргумент не является текстовой строкой, то функция возвращает ошибку #Н/Д.В противном случае она возвращает символы в обратном порядке.
Тип исходной функции REVERSETEXT обозначен |
как s t r i n g , поскольку функция |
возвращает текстовую строку. В исправленной |
версии функция описана как |
v a r i a n t , поскольку теперь она возвращает не строку.
Возвращение массива изфункции
Большинство функций, разработанных в VBA, возвращают одно значение. Однако можно написать функцию, которая бы возвращала несколько значений в виде массива.
В части 3 рассматриваются массивы и используемые в них формулы. В этих главах приводятся примеры формулы,которая возвращает несколько значений в отдельных ячейках. Как показано далее, можно также создать пользовательские функции, возвращающие массивы.
В VBA есть весьма полезная функция, которая называется Array. Эта функция возвращает массив данных типа v a r i a n t . Важно понимать, что возвращаемый массив и обычный массив, составленный из элементов типа Variant, — не одно и то же. Другими словами, массив v a r i a n t и массив из значений v a r i a n t s — это не одно и то же.
Если формулы массива в Excel вам уже известны, тогда можно приступать к ознакомлению с функцией Array. Формула массива вводится в ячейку после нажатия клавиш <Ctrl+Shift+Enter>. Excel вокруг формулы автоматически добавляет скобки, чтобы показать, что это формула массива. Более подробно о формулах массива рассказано в главе 12.
Глава 25.Примеры пользовательских функций VBA |
563 |
Нижняя граница массива, созданного с помощью функции Array, по умолчанию равна 0. Но ее можно изменить, воспользовавшись оператором Option Base.
Следующая функцияMONTHNAMES показывает, как возвратить массив процедурой функции.
Function MONTHNAMES() As Variant MONTHNAMES = Array( _
"Jan", "Feb", "Mar", "Apr", _ "May", "Jun", "Jul", "Aug", _ "Sep", "Oct", "Nov", "Dec")
End Function
На рис. 25.5 показан рабочий лист с использованием функции MONTHNAMES. Функция вводится после выбора диапазона А4 : L4 и введения следующей формулы:
{=MONTHNAMES()}
^ |
A, i . 'Я, |
J\-<?;, i JS •! |
А -Л% ^J: |
34 J a n F e b |
M a r ^У М |
Jun Jul Aug Sep Oct Nov Dec |
|
!Feb
r!Mar
Ji~May
l L N o v
i ! D e C
Puc. 25.5. Функция MONTHNAMES, введеннаяформулой массива
Как и при работе с формулой массива, чтобы ввести формулу, нужно нажать клавиши <Ctrl+Shift+Enter>. Не ставьте скобок— Excel добавит их сама.
Функция MONTHNAMES, как уже говорилось, возвращает горизонтальный массив размером в одну строку. Чтобы отобразить массив вертикального диапазона в один столбец (как на рис. 25.5 А7 : А18), выделите диапазон и введите следующую формулу:
{=ТРАНСП(MONTHNAMES())}
Чтобы сделать перегруппировку, можно изменить функцию. В следующей функции для возвращения вертикального массива используется функция Excel ТРАНСП.
Function VMONTHNAMES() As Variant
VMONTHNAMES = Application.Transpose(Array( _ "Jan", "Feb", "Mar", "Apr", _
"May", "Jun", "Jul", "Aug", _
"Sep", "Oct", "Nov", "Dec")) End Function
564 |
Часть VI. Разработка пользовательских функций |
Возвращение массива из неповторяющихся случайных целых чисел
Функция RANDOMINTEGERS возвращает массив, состоящий из неповторяющихся целых чисел. Эта функция используется в формулах многоячеечных массивов. На рис. 25.6 показан рабочий лист, в котором в диапазоне Al: D10 используется следующая формула.
{=RANDOMINTEGERS()}
t |
А _ .^ 8 , 1- |
|
D |
\ Е |
' F b | |
|
28 |
19 |
"*зз |
|
38 |
|
|
У- |
23 |
32 |
18 |
|
13 |
|
4 |
36 |
24 |
26 |
|
1 |
|
30 |
29 |
9 |
|
10 |
|
|
"С |
16 |
22 |
3 |
|
25 |
|
|
21 |
12 |
40 |
|
7 |
|
1Г |
17 |
2 |
39 |
|
5 |
|
б |
6 |
35 |
|
14 |
|
|
.у* |
15 |
4 |
27 |
|
31 |
|
• Щ\ |
34 |
20 |
11 |
|
37 |
|
а « V «\sheetl/
Pwc. 25.6. Формула массива генерирует неповторяющиеся целые числа, организованные в случайном порядке
Эта формула вводится в диапазон при нажатии <Ctrl+Shift+Enter>. Формула возвращает массив неповторяющихся случайных целых чисел. Поскольку формула охватывает 40 ячеек, то диапазон целых чисел лежит в интервале от 1 до 40. Далее приведен код формулы
RANDOMINTEGERS:
Function RANDOMINTEGERS() Dim FuncRange As Range
Dim V() As Integer, ValArrayO As Integer Dim CellCount As Double
Dim i As Integer, j As Integer
Dim r As Integer, с As Integer
Dim Tempi As Variant, Temp2 As Variant Dim RCount As Integer, CCount As Integer Randomize
'Создание объекта Range
Set FuncRange = Application.Caller
' |
Возвращение ошибки, если FuncRange слишком большое |
|||
|
CellCount |
= FuncRange.Count |
||
|
If CellCount > 1000 Then |
|||
|
RANDOMINTEGERS = CVErr(xlErrNA) |
|||
|
Exit |
Function |
|
|
|
End If |
|
|
|
1 |
Назначение переменных |
|||
|
RCount |
= |
FuncRange.Rows.Count |
|
|
CCount |
= |
FuncRange.Columns.Count |
|
|
ReDim V(l To RCount, 1 To CCount) |
|||
|
ReDim ValArrayd |
To 2, 1 To CellCount) |
||
1 |
Заполение |
массива |
произвольными числами |
|
1 |
и целочисленными |
значениями |
||
Глава 25. Примеры пользовательских функций VBA |
565 |
For i = 1 To CellCount
ValArrayCL, i) = Rnd
ValArray(2/ i) = i
Next i
1Сортировка ValArray For i = 1 To CellCount
For j = i + 1 To CellCount
If ValArray(1, i) > ValArray(1, j) Then Tempi = ValArray(1, j)
Temp2 = ValArray(2, j) ValArray(1, j) = ValArray(1, i) ValArray(2, j) = ValArray(2, i) ValArray(1, i) = Tempi ValArray(2, i) = Temp2
End If Next j
Next i
1Заполнение произвольными значениями массива V
i= О
For r = 1 То RCount
For с = 1 То CCount i = i + 1
V(r, с) = ValArray(2, i) Next с
Next r RANDOMINTEGERS = V
End Function
Перетасовка массива
Приведенная ниже функция FlANGERANDOMIZE принимает аргумент диапазона и возвращает введенный массив, элементы которого располагаются в случайном порядке.
Function RANGERANDOMIZE(rng)
Dim V() As Variant, ValArray() As Variant Dim CellCount As Double
Dim i As Integer, j As Integer
Dim r As Integer, с As Integer
Dim Tempi As Variant, Temp2 As Variant Dim RCount As Integer, CCount As Integer Randomize
1Возвращение ошибки, если rng слишком большое CellCount = rng.Count
If CellCount > 1000 Then RANGERANDOMIZE = CVErr(xlErrNA) Exit Function
End If
1Назначение переменных RCount = rng.Rows.Count CCount = rng.Columns.Count
ReDim V(l To RCount, 1 To CCount) ReDim ValArray(1 To 2, 1 To CellCount)
566 |
Часть VL Разработка пользовательских функ |
'Заполнение ValArray произвольными числами
1и значениями из rng For i = 1 То CellCount
ValArray(1, i) = Rnd ValArray(2, i) = rng(i)
Next i
1Сортировка ValArray For i = 1 To CellCount
For j = i + 1 To CellCount |
|
|||
If |
ValArray(1, |
i) > ValArray(1, |
j) Then |
|
|
Tempi = ValArray(1, j) |
|
||
|
Temp2 = ValArray(2, j) |
|
||
|
ValArray(1, |
j) |
= ValArrayd, i) |
|
|
ValArray(2, |
j) |
= ValArray(2, |
i) |
|
ValArray(1, |
i) |
= Tempi |
|
|
ValArray(2, |
i) |
= Temp2 |
|
End |
If |
|
|
|
Next j
Next i
1Занесение произвольных значений в массив V i = О
For r = 1 То RCount
For с = 1 То CCount i = i + 1
V(r, с) = ValArray(2, i) Next с
Next r RANGERANDOMIZE = V
End Function
Этот код немного похож на код функции RANDOMINTEGERS.На рис. 25.7 показана эта функция в действии. Формула массива в диапазоне С2 : СИ такая:
(=RANGERANDOMIZE(A2:A11) }
Эта формула возвращает содержимое ячеек А2 : АИ, но в случайном порядке.
Использование необязательных аргументов
Во многих встроенных функциях рабочих листов Excel используются необязательные аргументы. Например, функция ЛЕВСИМВ возвращает символы левой части строки. В этой функции используется следующий синтаксис:
ЛЕВСИМВ(text;num_chars)
Первый аргумент обязателен, а второй задается по желанию. Если опустить необязательный аргумент, то Excel примет предполагаемое значение, в данном случае 1.
В пользовательских функциях, которые создаются в VBA, тоже есть необязательные аргументы. Перед именем таких аргументов стоит ключевое слово Optional. Ниже приведена простая функция, которая возвращает имя пользователя.
Function USER()
USER = Application.UserName Enf Function
Глава 25. Примеры пользовательских функций VBA |
567 |
Представим, что в некоторых случаях имя пользователя должно отображаться в верхнем регистре. В следующей функции используется необязательный аргумент.
Function USER(Optional Uppercase As |
J N Original |
Randomized |
|
||
™3f?Alan |
Jack |
|
|||
Boolean) |
|
||||
IJK Bob |
Hilda |
|
|||
If |
IsMissing(Uppercase)Then Uppercase = |
T|Tf Cindy |
Alan |
i! |
|
jS^Dave |
Frank |
||||
False |
"l^Ellen |
Dave |
|||
|
|||||
If |
Uppercase = True Then |
У s Frank |
Ellen |
|
|
1T;G,nny ' |
Cindy |
|
|||
USER = Ucase(Application.UserName) |
J L H i l d a |
Irvin |
|
||
Else |
10" Irvin |
Bob |
|
||
TllJack |
Ginny |
|
|||
USER = Application.UserName |
|
|
|
||
End |
If |
|
) , . . r , , , . 4 1 1 | _ i _ . |
|
|
End |
Function |
|
|
|
|
Если аргумент FALSE пропущен, то имяпользователя возвращается без каких-либо изменений. Если же аргумент TRUE, то имя пользователя преобразуется в
верхний регистр (припомощи функции VBA UCase) и только после этого отображается на экране. Обратите внимание, что в первом операторе в процедуре используется функция VBA IsMiss ing для определения того, указывался ли аргумент. Если аргумента нет,то оператор устанавливает переменную Uppercase равной FALSE (поумолчанию).
Все приведенные ниже формулы являются правильными (и первые две выдают одинаковый результат):
=USER()
=USER(FALSE)
=USER(TRUE)
Использование неопределенного количества аргументов
В некоторых функциях рабочего листа Excel используется заданное количество аргументов. Примером такой функции может быть функция СУММ, в которой применяется следующий синтаксис:
СУММ(число1;число2.. . )
Первый аргумент обязателен, а после него можно указать вплоть до29 аргументов. Вот пример формулы, в которой используется функция СУММс четырьмя диапазонами в качестве аргументов:
=СУММ(А1:А5;С1:С5;Е1:Е5;Gl:G5)
Типы аргументов могут быть одинаковыми и разными. Например, в следующем примере используется три аргумента — диапазон, значение и оператор.
=СУММ(А1:А5;12;24*3)
Можно создать процедуру с неопределенным количеством аргументов. Суть в том, чтобы использовать массив в качестве последнего (и единственного) аргумента с ключевым словом
ParamArray.
ParamArray можно применить только к последнему аргументу процедуры. У него всегда тип variant и это всегда необязательный аргумент (хотя ключевое слово Optional неиспользуется).
568 |
ЧастьVI. Разработка пользовательскихфункций |
Простой пример неопределенных аргументов
Ниже приведена процедура функции, в которой может использоваться любое количество однозначных аргументов. Онавозвращает сумму аргументов.
Function SIMPLESUM(ParamArray arglist( ) As Variant) As Double Dim arg as Variant
For Each arg In arglis t SIMPLESUM = SIMPLESUM + arg
Next arg End Function
Приведенная ниже формула возвращает сумму аргументов-ячеек:
=SIMPLESUM(А1;А5;12)
Наиболее серьезное ограничение функции SIMPLESUM заключается в том, что она не работает с несколькими диапазонами. А следующая версия функции работает:
Function |
SIMPLESUM(ParamArray arglist( ) As Variant) As Double |
|||
Dim |
arg |
as |
Variant |
|
Dim |
cell |
as |
Range |
|
For |
Each arg In arglis t |
|||
|
If TypeName(arg) |
= «Range» Then |
||
|
|
For |
Eah call |
In arg |
|
|
|
SIMPLESUM = SIMPLESUM + cell |
|
|
Else |
Next cell |
|
|
|
|
|
|
|
|
|
SIMPLESUM = SIMPLESUM + arg |
||
|
End |
If |
|
|
Next arg |
|
|
||
End Function |
|
|
|
|
Эта функция проверяет каждую запись массива A r g l i s t . Если запись является диапазоном,то в коде используется цикл For Each-Next, чтобы выполнить суммирование ячеек диапазона.
Даже эта версия не является заменой функции Excel СУММ. Проверьте эту функцию в действии, используя различные типы аргументов, и вы увидите, что функция не работает, если все аргументы не являются значениями или ссылкой на диапазон. Также если аргумент состоит из целого столбца, то функция будет работать очень медленно, поскольку она просчитывает всеячейки, в том числе пустые.
Имитирование функции СУММ
В этом разделе представлена процедура функции, которая называется MYSUM. В отличие от функции SIMPLESUM, о которой рассказывалось в предыдущем разделе, MYSUM прекрасно заменяет функцию Excel SUM.
Перед тем как взглянуть на код функции MYSUM, давайте проанализируем функцию Excel СУММ. В этой функции может быть любое количество аргументов (даже "пропущенные" аргументы); аргументами могут быть цифры, ячейки, диапазоны, текстовое выражение цифр, логические значения идаже внедренные функции. Например, посмотрите наследующую формулу:
=SUM(A1;5;"6";/ИСТИНА;SQRT(4);В1:В5)
Эта формула — правильная — онасодержит все типы аргументов, перечисленные ниже в порядке появления в формуле:
•ссылка на одну ячейку (А1)
•константа (5)
•строка в виде точного значения (" б")
Глава 25. Примеры пользовательских функций VBA |
569 |
