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

Уокенбах Формулы в Excel

.pdf
Скачиваний:
212
Добавлен:
26.03.2016
Размер:
35.82 Mб
Скачать

Возвращение максимального значения из рабочих листов

Чтобы определить максимальное значение (например, В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

 

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

Рис. 25.7. Функция RANGERANDOMIZE возвращает диапазон, элементы которого расположены в случайном порядке
ИИГГ*8oi xi

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

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