Работа с массивами
Любой массив - это объект ссылочного типа данных. Размер массива неизменен. В OverScript поддерживаются только одномерные массивы.
Ниже рассмотрим функции для добавления и удаления элементов массива. Но нужно понимать, что функции, которые меняют размер массива, на самом деле создают каждый раз новый массив. Создание массива - ресурсоемкая операция, поэтому в C# нет таких функций, а есть тип List. Тем не менее я решил сделать функции для добавления и удаления элементов, а использовать их или нет - дело ваше.
Insert и ConvInsert
long[] arr={10, 20, 30}; //правильнее было бы: {10L, 20L, 30L}, но и так сработает arr.Insert(1, 15L); //вставка по индексу 1. Тут, если написать 15, а не 15L, то будет ошибка! WriteLine(Join(' ', arr)); //10 15 20 30 int c=arr.Insert(0, 3L, 5L); //вставка двух значений в начало. Можно и больше двух вставлять. WriteLine(Join(' ', arr)+"; Новый размер: "+c); //3 5 10 15 20 30; Новый размер: 6 arr.ConvInsert(c, 40, "50"); //вставка в конец двух значений с конвертированием их в нужный тип WriteLine(Join(' ', arr)); //3 5 10 15 20 30 40 50
При вставке в массив своего типа проверка типа вставляемых экземпляров не делается.
Foo[] arr=new Foo[0]; arr.Insert(0, new Foo(), new Bar()); WriteLine(Join(", ", arr)); //Instance of Foo, Instance of Bar class Foo{} class Bar{}
Вставлять можно в массивы любых типов:
object arr=Array("System.UInt64", 10, 20, 30); //функция Array создаёт массив заданного типа. Вместо "System.UInt64" лучше писать typeof("System.UInt64"), чтобы тип не искался при каждом обращении, а typeof это литерал. arr.ConvInsert(2, 777); //10 20 777 30 WriteLine(Join(' ', arr));
Поддерживается вставка в объекты реализующие System.Collections.IList:
object list=Create("System.Collections.Generic.List`1[System.Int64], System.Collections"); list.Insert(list.Length(), 10L); list.Insert(list.Length(), 20L); list.ConvInsert(list.Length(), 30); WriteLine(Join(' ', list)); //10 20 30
InsertRange
Эта функция позволяет вставлять значения из массива.
long[] arr={10, 20, 30}; arr.InsertRange(1, new int[]{555, 777}); //тут int-значения копируются в long-массив, что позволяется WriteLine(Join(' ', arr)); //10 555 777 20 30 //arr.InsertRange(0, new decimal[]{123, 789}); //выдаст ошибку, т.к. decimal нельзя запихнуть в long[] arr.InsertRange(0, new decimal[]{123, 789}, true); //чтобы при вставке значения конвертировались, нужно добавить третий аргумент - true WriteLine(Join(' ', arr)); //123 789 10 555 777 20 30
Вставка в список:
object list=Create("System.Collections.Generic.List`1[System.Int64], System.Collections"); list.InsertRange(0, new long[]{555, 777}); WriteLine(Join(' ', list)); //555 777 list.InsertRange(0, new decimal[]{123, 789}, true); //с конвертированием WriteLine(Join(' ', list)); //123 789 555 777
Push и ConvPush
long[] arr=new long[]{10, 20, 30}; arr.Push(40L); //вставка в конец числа типа long arr.ConvPush(50); //число 50 типа int сконвертируется в long arr.Push(60L, 70L); //можно несколько значений добавлять WriteLine(Join(' ', arr)); //10 20 30 40 50 60 70
В список:
object list=Create("System.Collections.Generic.List`1[System.Int64], System.Collections"); list.Push(10L, 20L, 30L); list.ConvPush(40, 50); WriteLine(Join(' ', list)); //10 20 30 40 50
В необобщенный стек (обобщенные не поддерживаются):
object stack=Create("System.Collections.Stack, System.Collections.NonGeneric"); stack.Push(10, 20L, "Hello"); WriteLine(stack.Peek()); //Hello
В необобщенную очередь (обобщенные не поддерживаются):
object queue=Create("System.Collections.Queue, System.Collections.NonGeneric"); queue.Push(10, 20L, "Hello"); //Push для очереди вызывает метод Enqueue WriteLine(queue.Peek()); //10
Insert, ConvInsert, InsertRange, Push и ConvPush возвращают кол-во элементов после вставки.
Peek и Pop
Peek возвращает последнее значение в массиве, списке, стеке, и первое значение в очереди. Pop делает то же самое, но с удалением. Обобщенные стеки и очереди не поддерживаются.
int[] arr={10, 20, 30}; WriteLine(arr.Peek()); //30 WriteLine(arr.Pop()); //30 WriteLine(arr.Peek()); //20 WriteLine(); object list=Create("System.Collections.Generic.List`1[System.Int32], System.Collections"); list.Push(10, 20, 30); WriteLine(list.Peek()); //30 WriteLine(list.Pop()); //30 WriteLine(list.Peek()); //20 WriteLine(); object stack=Create("System.Collections.Stack, System.Collections.NonGeneric"); stack.Push(10, 20, 30); WriteLine(stack.Peek()); //30 WriteLine(stack.Pop()); //30 WriteLine(stack.Peek()); //20 WriteLine(); object queue=Create("System.Collections.Queue, System.Collections.NonGeneric"); queue.Push(10, 20, 30); WriteLine(queue.Peek()); //10 WriteLine(queue.Pop()); //10 //Pop для очереди вызывает метод Dequeue WriteLine(queue.Peek()); //20
At
At возвращает элемент по указанному индексу. Кроме массивов поддерживаются объекты реализующие System.Collections.IList. Можно указывать отрицательный индекс.
int[] arr={10, 20, 30}; WriteLine(arr.At(1)); //20 WriteLine(arr.At(-1)); //30 object arr2=new long[]{100L, 200L, 300L}; WriteLine(arr2.At(1)); //200 object list=Create("System.Collections.Generic.List`1[System.Int32], System.Collections"); list.Push(10, 20, 30); WriteLine(list.At(1)); //20 WriteLine(list.At(-1)); //30
Если объект не реализует IList, а только IEnumerable, то получить элемент можно функцией ElementAt:
object stack=Create("System.Collections.Stack, System.Collections.NonGeneric"); stack.Push("hello", "world", "test"); WriteLine(stack.ElementAt(1)); //world
Поиск в массиве (в любом IList)
Поиск элемента делается функцией Find.
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; WriteLine(arr.Find(byte v, v>44)); //50 WriteLine(arr.Find(byte $v, v>99)); //0 //$ перед v - разрешение на повторное декларирование Foo[] arr2={new Foo("hello"), new Foo("world"), new Foo("test")}; WriteLine(arr2.Find(Foo f, f.Str.EndsWith('d')).Str); //world class Foo{ public string Str; New(string s){Str=s;} }
Первый аргумент (arr) - где искать (поддерживаются любые IEnumerable). Второй (byte v) - переменная, в которую записывать значение элемента на каждой итерации. Третий (v>44) - условие, при котором брать элемент.
Есть функция FindIndex, которая также ищет элемент и возвращает его индекс.
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; byte v; WriteLine(arr.FindIndex(v, v>44)); //4 WriteLine(arr.FindIndex(v, v>99)); //-1
Есть FindAll, которая возвращает массив всех элементов, соответствующих условию.
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; byte[] arr2=arr.FindAll(byte v, v>20 && v<60); WriteLine(Join(' ', arr2)); //30 40 50
Есть функция Select для выборки значений:
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; int[] arr2=arr.Select(byte v, int\v, v>40); //в C# было бы: int[] arr2 = arr.Where(v => v > 40).Select(v=> (int)v).ToArray(); WriteLine(Join(' ', arr2)); //50 60 70
В этом примере выбираются значения больше 40 с преобразованием в int. Аргумент int\v - это значение, которое добавляется в новый массив.
Эту функцию можно использовать без условия:
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; int[] arr2=arr.Select(byte v, int\v*2); WriteLine(Join(' ', arr2)); //20 40 60 80 100 120 140
Её также можно использовать для обычного конвертирования массива в другой тип, но лучше использовать ConvertArray:
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; int[] arr2:=arr.ConvertArray(int); WriteLine(Join(' ', arr2)); //10 20 30 40 50 60 70
Есть ещё функция ToArray, позволяющая создать массив из IEnumerable:
object stack=Create("System.Collections.Stack, System.Collections.NonGeneric"); stack.Push("hello", "world", "test"); string[] arr:=stack.ToArray(string); //чтобы ToArray работала быстрее, лучше задавать размер: stack.ToArray(string, 3) WriteLine(Join(' ',arr)); //test world hello arr:=stack.ToArray(string, 2); //брать только два элемента WriteLine(Join(' ',arr)); //test world
Удаление элементов
Удалить элемент можно функцией Remove:
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; int c=arr.Remove(20, 40, 70); //удаление элементов 20, 40, 70 WriteLine(Join(' ', arr)); //10 30 50 60 WriteLine("New length: "+c); //New length: 4
RemoveAt удаляет по индексу:
byte[] arr={10, 20, 30, 40}; int c=arr.RemoveAt(1); //удаление элемента по индексу 1 WriteLine(Join(' ', arr)); //10 30 40 WriteLine("New length: "+c); //New length: 3
RemoveRange удаляет указанное кол-во элементов, начиная с заданного индекса:
byte[] arr={10, 20, 30, 40}; int c=arr.RemoveRange(1, 2); //удаление двух элементов начиная с индекса 1 WriteLine(Join(' ', arr)); //10 40 WriteLine("New length: "+c); //New length: 2
RemoveAll удаляет все элементы, удовлетворяющие условию:
byte[] arr={10, 20, 30, 40}; int c=arr.RemoveAll(byte v, v>25); //удаление элементов, которые больше 25 WriteLine(Join(' ', arr)); //10 20 WriteLine("New length: "+c); //New length: 2
Все эти функции удаления поддерживают объекты реализующие интерфейс IList.
Slice и Splice
Slice возвращает новый массив, содержащий копию части исходного массива.
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; byte[] newArr=Slice(arr, 2, -2); WriteLine(Join(' ', newArr)); //30 40 50 newArr=Slice(arr, 2); WriteLine(Join(' ', newArr)); //30 40 50 60 70 newArr=Slice(arr, , 2); WriteLine(Join(' ', newArr)); //10 20
Splice изменяет содержимое массива, удаляя существующие элементы и/или добавляя новые.
byte[] arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; byte[] arr2=Splice(arr, 2, 3, 33, 44, 55); //замена элементов 2, 3, 4 (30 на 33, 40 на 44, 50 на 55) WriteLine(Join(' ', arr)); //10 20 33 44 55 60 70 WriteLine(Join(' ', arr2)); //30 40 50 arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; arr2=Splice(arr, 2, 3); //удаление трёх элементов начиная с индекса 2 WriteLine(Join(' ', arr)); //10 20 60 70 WriteLine(Join(' ', arr2)); //30 40 50 arr=new byte[]{10, 20, 30, 40, 50, 60, 70}; arr2=Splice(arr, 2, 0, 22, 222); //вставить 22 и 222 без удаления других WriteLine(Join(' ', arr)); //10 20 22 222 30 40 50 60 70 WriteLine(Join(' ', arr2)); //ничего, т.к. массив пуст
Slice и Splice работают только с массивами встроенных в OverScript типов (int[], string[], double[] и т.д.), а также массивами пользовательских типов.