Это четвертый из серии гайдов по нововведениям NVSE4, освежившими моддинг сцену Нью Вегаса: UDFы, строковые переменные и массивы. Моддеры Обливиона, незнакомые с ОБСЕ 16+ тоже получат пользу от этих статей.
В данной части речь пойдет о массивах, их переменных и применении их в игре.
1. Что? Терминология, Постоянство
Переменные массивов отсылают к массивам.
Массивы – списки. Представьте себе таблицу с двумя столбцами в Ворде.
Массивы содержатся в NVSE для ваших нужд и если они существуют в момент сохранения, то их сохраняют в .nvse файле соответствующего сохранения. В этом плане они идентичны строковым переменным.
Но отличие от строковых в том, что NVSE убирает их за вас: когда массив ни на что не ссылается, будь это переменная массива в активном скрипте или другой массив, держащий ваш массив, он удаляется. В общем, либо в массиве храним информацию на длинный период и используем для этого квестовую переменную массива, либо на короткий, для чего придется де-инициализировать соответствующую переменную как можно быстрее после того, как в её услугах нет нужды, чтобы не получить разбухание сохранения.
2. Типы Массивов, Ключей и Значений
Существуют разные типы массивов.
Первый, будем называть его «регулярным» массивом (массив типа «массив», «массив» массив – такие еще термины в ходу, но звучит немного тупо, имхо), выглядит как формлист, подобный левел листам из ГЕККа:
НеАлкогольныеНапитки формлист:
0 MS05NukaColaQstm
1 MS05IceNukaCola
2 NukaCola
3 WaterUnpurified
4 WaterPurified
Как и формлисты, регулярные массивы индексируются последовательностью целых натуральных чисел, начиная с нуля. Эти индексные числа называются ключами.
Главное отличие и преимущество всех видов массив в наличие значений, ассоциируемых с этими ключами. Значениями может быть всё что угодно: не только формы и референсы, но и числа, строки и даже другие массивы. (Когда мы говорим о комбинации ключ-значение, мы используем термин «элемент»).
Вот регулярный массив:
0 NukaCola ; форма
1 3 ; число
2 "You can't beat the real thing" ; строка
3 RecipesInvolvingNukaColaArray ; массив
Как и с формлистами индексы всегда будут последовательными. Если удалить любый элемент, то элементы выше него сдвинутся – пропуски в последовательности ключей недопустимы, так же, как и десятичные дроби или отрицательные числа:
0 NukaCola
1 "You can't beat the real thing"
2 RecipesInvolvingNukaColaArray
Это ограничение, в случае с другим типом числового индексирования, можно обойти картами (maps), которые тоже массивы, только они могут хранить любое число как ключ и позволяют делать зияния (поскольку допускают наличие десятичных дробей). Так что мы можем, к примеру, сконвертировать приветствие с CS Wiki в эту карту, где время сконвертируется в десятичные и активности в строки:
8.5 "Запись, кофе" ; массивы не хранят кавычки, я их написал, чтобы вы не забыли, что это строка
9 "Введение"
9.5 "Презентация A"
10.5 "Кофе-брейк"
11 "Презентация B"
Вот еще один массив, хранящий внешние ячейки как значения с их X координатой как ключем, некоторые из ключей отрицательны:
-21 MojaveOutpost
-9 MojaveDriveIn
7 188TradingPost
12 BoulderCity
17 BitterSprings
Наконец, иногда чисел любого рода недостаточно, поэтому у нас строковые карты (stringmaps), хранящие строки как ключи:
"Имя" "Prudencia"
"Возраст" 27
"Тело" "T6M"
"Профессия" "Test character"
"Времени в игре (часов)" 106.5
"Ячейек посещено" CellsVisitedArray ; увы, пока что там хранится только GSDocMitchellHouse за все это время, под ключом 0
3. Обьявление и Инициализация Переменных Массива
Обьявляем переменные массива любого типа вот так:
array_var somearrayvarname
Прямо как строковые переменные, переменные массива должны быть прежде инициализированы чтобы ссылаться на существующий массив в NVSЕ. Инициализировать массивы можно так:
- прямо сконструировав их, используя команды let и ar_construct с типом массива как параметром
let somearrayvarname := ar_construct array
let somearrayvarname := ar_construct map
let somearrayvarname := ar_construct stringmap
«Массив», «карта» и «строковая карта» - параметры строки у функции ar_construct, подобные параметру Health в GetAV, GetAv Health. Важно, что при включенной перезаписи компилятора (script compiler override), вам высветится предупреждение по поводу этих строк, в котором GeckPU скажет что-то в духе «я полагаю, что это строки, подумай, стоит ли их оставлять, а я пока не буду это компилировать», но если проигнорировать это предупреждение, то всё скомпилируется как миленькое, и в игре всё будет работать без проблем.
- используя let присваиваем им другую переменную массива, то есть теперь обе переменных массива ссылаются на один и тот же массив:
let somearrayvar2 := somearrayvar1
let somearrayvar1[2] := something --> somearrayvar2 now also has the new element, because it's the same array
Если вторая переменная массива ссылается на тот же массив подобным образом, вы можете спокойно сломать одну из двух переменных, используя команду ar_construct, чтобы удалить всё её содержимое.
let somearrayvar2 := ar_construct array --> somearrayvar2 теперь будет содержать совершенно новый, пустой массив
Если нужно скопировать массив в другой, просто используйте ar_copy или ar_deepcopy (см. седьмой раздел этого туториала).
- используя let присваиваем результат функции (the result of a function), который возвращает массив (включая UDF, которые возвращают массив с помощью команды SetFunctionValue)
let somearrayvar := sv_Split "Chop up this string." " ."
в результате получится обычный массив, содержащий фрагменты строки в виде строк: '
0 Chop
1 up
2 this
3 string
let somearrayvar := someActorRef.NX_GetEVFlAr "SomeNXKey"
; вернет stringmap, содержащий все nx ключей EVFL, которые находятся в someActorREf, содержащем или равном "SomeNXKey" в качестве ключей stringmap, со значениями nx в качестве значений stringmap
Множество связанных с массивами команд (ar_copy, ar_range, ar_map и т.д.) возвращают массивы и так инициализируют переменные массивов, которым присвоены эти массивы через let.
Можно де-инициализировать переменные массивов можно через присвоение им ar_Null с помощью let:
let somearrayvar := ar_Null
В таком случае это будет нечто в духе переменной ref со значением null (null ref var). Если хотите знать, инициализирован ли массив, прежде, чем что-либо с ним будете делать, можно его проверить как переменную bool или использовать команду LogicalNot (!) в случае, если она де-инициализирована.
if somearrayvar
; переменная массива инициализирована
else
; де-инициализирована
endif
if eval !(somearrayvar)
; переменная массива не инициализирована
endif
Если вы и так в курсе её состояния, то тогда нет смысла добавлять этот код.
4. Возврат и Хранение Элементов
Если вы знаете ключ значения, то вы можете прямо его вызвать подобной строчкой кода:
let somevar := somearrayvar[key] ; обратите внимание на квадратные скобки
применимо и к примерам из предыдущей главы:
let sv_somestring := NukaColaArray[2]
; sv_somestring сейчас ссылается на копию "You can't beat the real thing." Строк, хранящейся в нашем массиве.
let rSomeCellForm := CellMap[-9]
; rSomeCellForm сейчас приравнена к ячейке MojaveDriveIn
let iSomeInt := CharacterStringMap["Age"]
; iSomeInt теперь равен 27
let fSomeFloat := CharacterStringMap["Time played (hrs)"]
; fSomeFloat is 106.5
let rSomeCellForm := CharacterStringMap["Cells visited"][0]
; rSomeCellForm теперь равна GSDocMitchellHouse (первая ячейка хранилась как значение в регулярном массиве под ключом «Посещенные Ячейки» в нашей строковой карте символов)
Необязательно строго пробрасывать значение из массива в локальную переменную скрипта всякий раз, когда хотите сделать что-то с ними. Если помните раздел по перезаписи компилятора () в туториале по синтаксису, как только вы его включили, вы можете сотворить вот это:
let fSomeFloat := 3 * (somearrayvar[key] - iSomeInt) ; если хранимое значение в этом массиве - число
someRef.call someUDF fSomeFloat somearrayvar[key] ; если UDF ожидает тип переменной подобный тому, что находится в массиве как второй параметр
и подобные этой комбинации – всё в ваших руках. Тестите, что работает, а что нет.
И добавлять или заменять элементы массива можно вот так:
let somearrayvar[index] := something
let NukaColaArray[2] := "Everything goes better with Nuka."
; заменяет старую строку как значение, которая уничтожается, если на неё больше не ссылаются
let NukaColaArray[2] := sv_someStringVar
; same, just different
let CellMap[-18] := GoodSprings
; добавляет форму внешней ячейки GoodSprings из индекса -18
let CharacterStringMap["Race"] := PlayerRef.GetRace
; добавляет испанцев как расу в значение ключа "Race", поскольку это отражает расу игрока
let CharacterStringMap["Cells visited"][1] := GoodSprings
; добавляет внешнюю ячейку GoodSprings как второй элемент массива (ключ = 1), хранящийся под названием «Посещенные Ячейки» в нашей строковой карте
Замечу, что бывают ситации, когда присваивание через let элемента массива к результату функции напрямую, как в случае с расой, может выйти боком, так что придется сперва хранить их в переменной локального скрипта. Сразу скажу, не ждите, что на getitemcount можно положиться. И, да, возвращая или помещая в строковую карту, можно использовать строковые переменные вместо строковых ключей.
Неважно, в каком порядке добавляются элементы в массив, он автоматически сортирует их по возрастающей, числовым способом для регулярных массивов и карт, по алфавиту для строковых карт:
let somearray[1] := value1 let somestringmap["B"] := valueB
let somearray[0] := value0 let somestringmap["A"] := valueA
будет отсортировано:
0 value0 A valueA
1 value1 B valueB
5. Наполняем Массивы Чуть Быстрее
Не надо быть семи пядей, чтобы увидеть, что такое присвоение:
let somearrayvar[index] := Value
может быть чем-то вроде рутины по набору текста, если вам нужно поместить в него много элементов, что еще и занимает драгоценные строки скрипта. Впрочем, есть несколько сокращений (shortcuts).
Для регулярных массивов можно применить команду ar_List, которая создает массив с 20 элементами в одной строчке, обычно разделяемых запятыми:
let somearrayvar := ar_List value1, value2, value3, value4, ... value20
let NukaColaArray := ar_List NukaCola, 2, "You can't beat the real thing.", RecipesInvolvingNukaColaArrayVar
Порядок добавления элементом зависит от индексасии массива (от 0 до 19).
0 NukaCola
1 2
2 "You can't beat the real thing."
3 RecipesInvolvingNukaColaArrayVar
Ar_list – функция, возвращающая массив, так что она сразу же инциализирует массив, к которому вы применили let.
Если надо добавить элементы в виде диапазона целых чисел в регулярный массив, то примените команду ar_Range:
let someArrayVar := ar_range FirstInt LastInt StepInt ; StepInt is optional, if you leave it out it's assumed to be 1
let someArrayVar := ar_range 1 9 2 ; идентично let someArrayVar := ar_List 1, 3, 5, 7, 9
let someArrayVar := ar_range 1 4 ; идентично let someArrayVar := ar_List 1, 2, 3, 4
Для карт и строковых карт применяют команду ar_Map, снова до 20 элементов для обьявления, только ключи могут быть любыми, так что нужно обьявить их для каждого элемента самому, связав каждый ключ со значением с помощью двойных двоеточий:
let somemap := ar_Map someKeyNumber::someValue someKeyNumber::someValue someKeyNumber::someValue ... the20thKeyNumber::the20thValue
let somestringmap := ar_Map someKeyString::someValue someKeyString::someValue ... the 20thKeyString::the20thValue
let CellMap := ar_Map -21::MojaveOutpost -9::MojaveDrivein 7::188TradingPost 12::BoulderCity 17::Bittersprings
let CharacterStringmap := ar_Map "Name"::"Prudencia" "Age"::27 "Body"::"T6M" "Profession"::"Test character" "Time Played (hrs)"::106.5 "Cells visited"::CellsVisitedArray
Карты и строковые карты опять автоматически отсоритуруют свое содержимое в порядке возрастания, когда всё будет готово.
А если элементом больше 20? А если мы не знаем, сколько их там будет?
6. Осмотр Массивов: Проход по Массивам, TYPEOF, Дампинг Массивов в Консоль
В таком случае нужен способ отправить часть во временный массив с помощью команды ar_list, добавив каждый элемент другому массиву, уже содержащему парочку элементов.
Хотя можно просто использовать ar_InsertRange для таких целей (см. главу 8). Но для обучения пока условимся забыть эту информацию (в любом случае эта команда работает только с регулярными массивами).
В общем, нужен способ выбирать каждый элемент массива поочередно и менять его.
С регулярными массивами, коль скоро ключи – целые последовательные числа, мы можем применить ar_size (см. глава 7), чтобы построить (to build) цикл while, в котором мы добавим каждое значение, содержащееся в массиве2 массиву1 посредством команды ar_append (см. главу 8):
let array1 := ar_list value1, value2, value3, value4, ... value20 ; значения могут быть любыми, в документации OBSE написано «multi»
let array2 := ar_list value1, value2, value3, value4, ... value20
let iSize := ar_size array2 ; этот шаг можно пропустить
let iNum := -1
while (iNum += 1) < iSize ; самый низкий индекс равен 0, а самый высокий возможный индекс равен общему размеру - 1
let someVar := array2[iNum]
ar_append array1 someVar
loop
И всё прекрасно работает, покуда каждое значение в массиве2 имеет тот же тип (форма, число, строка или массив), чтобы мы знали, как должен быть объявлен 'someVar'. Проблема в том, что массивы могут содержать любую комбинацию таких значений. Строго говоря, мы могли бы использовать удобную команду TypeOf, чтобы узнать тип значения в виде строки:
let iNum := -1
while (iNum += 1) < (ar_size array2)
let sv_somestring := TypeOf array2[iNum] ; TypeOf determines the type of the value under array2[iNum] and passes it to our string var
if eval sv_somestring == "Form" ; TypeOf возвращает "Form" для форм и референсов
let rRefVar := array2[iNum]
ar_append array1 rRefVar
continue ; мы получили, что хотели, пропустим остальное и движемся дальше по циклу
elseif eval sv_somestring == "Number" ; TypeOf возвращает "Number" для целых и дробных чисел
let fFloatVar := array2[iNum]
ar_append array1 fFloatVar
continue
elseif eval sv_somestring == "String" ; TypeOf возвращает "String" для строк
let sv_StringVar := array2[iNum]
ar_append array1 svStringVar
continue
else ; TypeOf возвращает либо "Array", "Map" или "StringMap" для массивов, в таком случае вернуть надо все
let ar_ArrayVar := array2[iNum] ; помните, что это делает ar_ArrayVar референсом массива array2[iNum], а не копией
ar_append array1 ar_ArrayVar
continue
endif
loop
Всё это скорее обходной путь, да и в любом случае цикл while не работает с картами (зияниями и десятичными) или строковыми картами (строковые ключи).
Переходим к циклу foreach.
Циклы foreach выбирают каждый элемент типа коллекции (collection type) в прямом цикле, и, как и цикл while, воспринимайте цикл foreach как блок. Для строк они выбирают каждый символ, для контейнеров — каждую ссылку на инвентарь, а для любого типа массива они выбирают каждый элемент массива. Как и в случае с циклами while, вы прерываете цикл foreach командой «break» и пропускаете экземпляр тела цикла командой «continue».
При использовании цикла foreach для массива каждый элемент копируется в stringmap, итератор, который необходимо объявить — ключ проверяемого элемента будет найден в stringmap["key"], а значение — в stringmap["value"]:
"key" thekeyoftheelement
"value" thevalueoftheelement
Итератор, т. е. временная строковая карта, будет иметь только эти 2 элемента: "key"::KeyOfTheInspectedElement и "value"::ValueOfTheInspectedElement. Вам не нужно инициализировать его — это делает команда foreach — и когда цикл завершается, он немедленно обнуляется/неинициализируется.
array_var tempstringmap ; для итераторов я предпочитаю использовать «entry» или «ar_entry» в качестве имени переменной, как будто вы листаете энциклопедию
array_var arraytowalk
foreach tempstringmap <- arraytowalk ; вам нужен этот символ в виде стрелки
; проверьте или сделайте что-нибудь с tempstringmap["key"] и/или tempstringmap["value"]
loop
так что длинный цикл while может превратиться в этот короткий цикл foreach:
array_var array1
array_var array2
array_var ar_entry
let array1 := ar_List Value1, Value2, ... Value20
let array2 := ar_List someRef, someFloat, "someString", someStringVar, someArray, ... Value20
foreach ar_entry <- array2
ar_append array1 ar_entry["value"] ; независимо от типа каждого значения в array2, мы можем ссылаться на каждое из них с помощью ar_entry["value"]
loop
Как пример с нашей картой, хранящей внешние ячейки по их X координате: допустим, мы хотим разделить её на карту с западными ячейками (-Х) и восточными (+Х).
array_var CellMap
array_var CellMapWest
array_var CellMapEast
array_var entry
float fXPos
let CellMap := ar_Map -21::MojaveOutPost -9::MojaveDriveIn 7::188TradingPost 12::BoulderCity 17::BitterSprings
let CellMapWest := ar_construct Map
let CellMapEast := ar_construct Map
foreach entry <- CellMap
let fXPos := entry["key"] ; passing the key, the X position, to a float
if 0 > fXPos ; float is negative, so the cell is west
let CellMapWest[fXPos] := entry["value"] ; storing the cell - entry["value"] - under the fXPos key in the West map
else
let CellMapEast[fXPos] := entry["value"]
endif
loop
А нам нужен fXPos? Нет.
foreach entry <- CellMap
if eval 0 > entry["key"]
let CellMapWest[entry["key"]] := entry["value"]
else
let CellMapEast[entry["key"]] := entry["value"]
endif
loop
И напоследок. Как сказано еще в туториале по синтаксису, нужно тестить все штуки в игре, так что нам нужен вывод в консоль для наших массивов. Используем команду ar_Dump:
ar_dump someArrayVar
** Dumping Array #1 **
Refs: 1 Owner 3D: DSTest36.esp
[ 0.000000 ] : Value0
[ 1.000000 ] : Value1
etc.
Строковые карты очевидно хранят строки в квадратных скобках, где пишутся ключи.
Refs: 1 – значит, что только один референс хранится в этом массиве, в данном случае переменная массива в скрипте, вызванная функцией ar_dump
Owner – мод, создавший массив, то есть DSTest36.esp, а также его номер в актуальном Порядке Загрузки.
ID массива – число, присвоенное массиву при создании. Можно вызвать дамп массива, специфицировав ID, а не просто вызвав его на переменную массива:
ar_DumpID someIDInt
Но знайте, коль скоро массивы удалены, уже не ссылаясь ни начто, их ID будут использованы вновь. ar_DumpID – полезна по большей части только при вызове из консоли. Просто надо четко знать число ID.
Обе функции ar_dump работают как printc; они не зависят от debugmode. Однако создать версию, зависящую от debugmode, в UDF очень просто:
call DBArDump somearrayvar
scn DBArDump
array_var a ; parameter
Begin Function {a}
if GetDebugMode ; or some quest var int that you set to 1 when people toggle debugmode on in MCM
ar_dump a
endif
End
На самом деле чтение показаний консоли утомляет, поэтому есть удобная функция con_scof, которая выводит все, что появляется в консоли, в текстовый файл в корневой папке:
con_scof "somefilename.txt"
7. Функции Массивов: Быстрый Обзор
Ar_Size вернет размер массива в виде целого числа. -1 означает, что массив еще не инициализирован, 0 — что он инициализирован, но пуст.
let someInt := ar_size someArrayVar
Поиск информации:
Ar_Find вернет ключ для значения, которое нужно найти.
let iSomeInt := ar_Find ElementToSearchFor ArrayToSearch
let fSomeFloat := ar_Find ElementToSearchFor MapToSearch
let sv_SomeStringVar := ar_find ElementToSearchFor StringMapToSearch
Если значение нигде не найдено, будет возвращено значение -99999.0 для обычных массивов и карт, и пустая строка (sv_length sv_somestringvar == 0) для строковых карт.
Ar_HasKey проверит, существует ли элемент с указанным вами ключом, возвращая простое логическое значение.
if ar_HasKey ArrayToSearch someKey
; found!
Endif
Отмечу, что если есть эквивалентные элементы «ключ-значение» (оба являются числами или строками), то для поиска нужного элемента с помощью ar_HasKey потребуется чуть меньше времени/мощности процессора, чем с помощью ar_Find, поэтому, если считаете, что вам нужно искать что-то с одной стороны чаще, чем с другой, используйте их в качестве ключей.
ar_Keys возвращает обычный массив, содержащий все ключи исходного массива/карты/строковой карты:
let somearray := ar_Keys sourcearray
Следующее должно быть очень удобно при работе с картами и строковыми картами (хотя совершенно не нужно при работе с обычными массивами):
ar_First возвращает первый ключ.
let someInt/Float/StringVar := ar_First SomeMapVar
ar_Last возвращает последний ключ.
let someInt/Float/StringVar := ar_Last SomeMapVar
ar_Next возвращает следующий ключ из указанного.
let someInt/Float/StringVar := ar_Next SomeMapVar someKey
ar_Prev возвращает предыдущий ключ из указанного.
let someInt/Float/StringVar := ar_Prev SomeMapVar someKey
Ar_Next и ar_Prev возвращают значение 'bad index', когда ничего не находят. Запрашиваем 'bad index' для сравнения, используя ar_BadNumericIndex или ar_BadStringIndex в зависимости от типа массива:
let fFloat := ar_Next SomeMapVar someFloatKey
if eval fFloat != ar_BadNumericIndex
; do stuff, you've got a valid key there
endif
let svStringVar := ar_Prev someStringMapVar someStringKey
if eval svStringVar != ar_BadStringIndex
; do stuff, you've got a valid key there
endif
Копирование:
Ar_Copy копирует элементы из одного массива в другой:
let someArray2 := ar_Copy someArray1
Если someArray1 в свою очередь содержит несколько массивов в качестве значений, someArray2 будет содержать рференсы тех же массивы, а не копии.
Чтобы скопировать подмассивы «чисто», используйте ar_DeepCopy:
let someArray2 := ar_DeepCopy someArray1
Я не могу советовать какой лучше - это зависит от структуры вашего мода, типа скриптов, с которыми работаете (постоянных или нет), и того, что вы пытаетесь сделать, т. е. хотите ли вы, чтобы изменения, которые вы вносите в эти подмассивы через someArray1, влияли на эти подмассивы под someArray2. Выбор за вами, просто четко определитесь, что нужно.
Стирание элементов:
Ar_Erase – классно, что название отражает суть - делает свое дело:
ar_erase yourarrayVar KeyOfTheElementToErase
Он также может стереть диапазон элементов, если вы укажете этот диапазон в нотации среза, разделив первый и последний ключи для стирания двоеточием:
ar_erase YourArrayVar FirstKey:LastKey
Если удалить элемент или диапазон элементов из обычного массива, элементы с более высокими ключевыми номерами автоматически сместятся вниз.
Если elfkbnm элемент из массива из цикла foreach, когда этот элемент находится в итерации, это, скорее всего, разорвет цикл. С обычными массивами это происходит не всегда, но случается, если, к примеру, передать массив в качестве параметра в UDF из цикла. С картами и строковыми картами это приведет к прерыванию (break). Здесь нужно стирание (erasing) по завершению цикла, допустим, добавив элементы для стирания в новый массив во время цикла и удалив их из исходного впоследствии, или добавив элементы для сохранения в новый массив и позволить старому впоследствии стать его копией.
Сортировка:
Ar_Sort возвращает новый массив со значениями старого, отсортированными в определенном порядке. Это можно сделать только в том случае, если все они одного типа (числа, объекты или строки), иначе он просто вернет пустой массив. Строки сортируются в алфавитном порядке, числа — в числовом, а формы — в соответствии с их formID. Результирующий массив — это «регулярный» массив, независимо от того, какой массив сортировался:
let array2 := ar_sort array1 descendingSortBool ; если не специфицировать Bool, порядок будет по возрастанию
Ar_SortAlpha делает что-то похожее, но значения не обязательно должны быть одного типа — все они преобразуются в строки, как если бы вы использовали ToString, и сортируются в алфавитном порядке: строки остаются строками, числа становятся строковыми представлениями чисел, формы становятся именами базового обьекта, если они у них есть (в противном случае это их строки FormID).
let array2 := ar_sortalpha array1 descendingSortBool
Если вам нужна более конкретная сортировка, вы можете использовать Ar_CustomSort, для которой вам нужно написать UDF.
Ar_CustomSort будет многократно (repeatedly) брать 2 элемента из массива для сортировки и передавать их в UDF в качестве параметров в виде массивов. Ваша UDF определяет, как значения 2 параметров var массива должны быть отсортированы относительно друг друга.
Это особенно удобно, если сортировать формы не по их имени/formID (например, вес, размер, навыки и т. д.) или подмассивам. Вы даете функции знать, что один элемент следует поставить «прежде» или «позже» в порядке результирующего массива, позволяя UDF возвращать «true», то есть, задавая значение функции.
let array2 := ar_customsort array1 SortingUDF
descendingSortbool
Попробуйте так. Ок?
Я хотел сортировать жителей Гудспрингс по их навыку медицины, те, что круче, в начало, те, что слабее, вниз.
scn MyComparisonUDF
array_var a ; parameters
array_var b
ref rA ; local ref vars
ref rB
Begin Function {a b}
let rA := a[0]
let rB := b[0]
if rA.GetAV Medicine > rB.GetAV Medicine
SetFunctionValue 1 ; SetFunctionValue anyInt marks the comparison as returning: element value a < element value b
endif
End
scn MyCallingScript
array_var array1
array_var array2
let array1 := ar_List TrudyRef, SunnyRef, DocMitchellRef, EasyPeteRef, GSJoeCobbRef, GSChetRef
let array2 := ar_CustomSort array1 MyComparisonUDF
теперь array2 — это обычный массив, отсортированный по возрастанию, что в нашем случае означает по убыванию навыка медицины:
0 DocMitchellRef ; У Дока 33 баллов навыка
1 GSChetRef ; У Чета 15
2 TrudyRef ; все по 12
3 SunnyRef
4 EasyPeteRef
5 GSJoeCobbRef ; Джо Кобб имеет 10
Если я использую функцию ar_customsort с descendingbool, она отсортирует массив в порядке убывания, что в нашем случае означает в порядке возрастания навыка медицины:
0 GSJoeCobbRef
1 EasyPeteRef
2 SunnyRef
3 TrudyRef
4 GSChetRef
5 DocMitchellRef
8. Функции «Постоянного Массива»
Как уже много раз было сказано, регулярные массивы могут хранить только целые натуральные числа как ключи, начиная с нуля. Это ограничение, но и благославение, поскольку мы можем автоматизировать весь процесс, а не париться как с картами и строковыми картами.
Например, Ar_Append добавляет элемент в массив со следующим доступным номером ключа.
Ar_append ArrayVar ValueToAdd.
Что то идентично вот этому:
let SomeInt := ar_size ArrayVar
let arrayVar[SomeInt] := ValueToAdd
Если бы нам пришлось делать это с картами и строковыми картами, и мы не знали бы, какие ключи у них уже есть, нам пришлось бы выбрать случайный ключ, проверить, есть ли он у (строковой) карты с помощью ar_HasKey, и только потом добавить его или нет, выбрав другой. Или использовать ar_last и добавить что-то к этому, чтобы создать новый ключ.
Ar_Insert добавляет значение в массив по указанному числовому ключу, сдвигая вверх те, что уже есть, а также те, у кого ключ выше. Также можно применить команду ar_insert к общему размеру массива, фактически делая то же самое, что и ar_append. Однако нельзя вставлять значение по индексу, превышающему общий размер массива, потому что вы потеряете его часть.
ar_insert ArrayToAddToVar KeyToAddAt ValueToAdd
let array1 := ar_list "value1", "value2", "value3", "value4"
ar_insert array1 2 "value2.5"
будет выглядеть так:
0 "value1"
1 "value2"
2 "value2.5"
3 "value3"
4 "value4"
Ar_InsertRange делает еще один шаг вперед и позволяет вам вставлять диапазон значений в середину или в конец массива. Вы указываете диапазон как еще один массив.
ar_InsertRange ArrayToAddToVar KeyToStartAddingAt ArrayToAddVar
Итак, здесь мы продолжим наш предыдущий пример из главы 6, добавив array2 в конец array1:
let array1 := ar_list value1, value2, value3, value4, ... value20 ; who knows what type or how many
let array2 := ar_list value1, value2, value3, value4, ... value20 ; who knows what type or how many
let iSize := ar_size array1 ; don't strictly need this, how about:
ar_InsertRange array1 iSize array2 ; ar_InsertRange array1 (ar_size array1) array2
создаст
0 value1fromarray1
...
39 value20fromarray2
Если было 20.
Наконец, Ar_Resize позволяет вам сократить массив до указанного вами размера или расширить его до указанного вами размера.
ar_resize ArrayToResizeVar NewSizeInt PaddingMulti(opt)
Подождите, что такое paddingMulti? Ну, Multi — это значение элемента массива — может быть чем угодно. Так что под 'paddingMulti' подразумевают любое заданное значение для новых значений, если вы масштабируете массив. Они наполняются.
let arrayVar := Ar_Range 1 20
;--> создает обычный массив с 20 элементами, значениями которого являются целые числа от 1 до 20, а ключи, очевидно, от 0 до 19
ar_resize arrayVar 5ar_resize arrayVar 5
;--> наш массив теперь имеет только 5 элементов, с целыми числами 1-5 в качестве значений, и ключами 0-4
ar_resize arrayVar 10 "SomeString"
;--> Теперь наш массив содержит 15 элементов с ключами от 0 до 9, первые 5 значений — от 1 до 5, а последние 5 имеют значение «SomeString».
Ar_insert, ar_insertrange и ar_resize также могут возвращать bool, сообщающее, выполнили ли они свою работу — вы можете проверить это в той же строке, где отдаете им команду выполнить ее:
if ar_resize arrayVar 10 "SomeString"
; do something with your resized array
endif
Комментарии