diff --git a/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs b/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs index 388125736..b3f3945bc 100644 --- a/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs +++ b/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs @@ -5,16 +5,21 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System.Collections.Generic; using OneScript.Contexts; using OneScript.Exceptions; using OneScript.Types; using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; +using System.Collections.Generic; namespace OneScript.StandardLibrary.Collections { + /// + /// Коллекция элементов произвольного типа. + /// Возможно обращение к значениям элементов по числовому индексу (нумерация начинается с 0). + /// Доступен обход в цикле Для Каждого Из. + /// [ContextClass("Массив", "Array")] public class ArrayImpl : AutoCollectionContext, IValueArray { @@ -24,6 +29,11 @@ public ArrayImpl() { _values = new List(); } + + public ArrayImpl(int capacity) + { + _values = new List(capacity); + } public ArrayImpl(IEnumerable values) { @@ -56,16 +66,23 @@ public override void SetIndexedValue(IValue index, IValue val) Set((int)index.AsNumber(), val); else base.SetIndexedValue(index, val); - } - + } + #region ICollectionContext Members - + + /// + /// Получает количество элементов в массиве + /// + /// Количество элементов массива [ContextMethod("Количество", "Count")] public override int Count() { return _values.Count; - } - + } + + /// + /// Удаляет все элементы из массива + /// [ContextMethod("Очистить", "Clear")] public void Clear() { @@ -82,19 +99,27 @@ public override IEnumerator GetEnumerator() { yield return item; } - } - + } + #endregion - + + /// + /// Добавляет элемент в конец массива + /// + /// Произвольный: Добавляемое значение. Если не указано, то добавляется Неопределено [ContextMethod("Добавить", "Add")] public void Add(IValue value = null) { - if (value == null) - _values.Add(ValueFactory.Create()); - else - _values.Add(value); - } - + _values.Add(value ?? ValueFactory.Create()); + } + + /// + /// Вставляет значение в массив по указанному индексу + /// + /// Число: Индекс вставляемого значения. + /// Если индекс превышает размер массива, то массив дополняется элементами Неопределено до указанного индекса. + /// Если индекс отрицательный, то выбрасывается исключение + /// Произвольный: Вставляемое значение. Если не указано, то вставляется Неопределено [ContextMethod("Вставить", "Insert")] public void Insert(int index, IValue value = null) { @@ -104,26 +129,27 @@ public void Insert(int index, IValue value = null) if (index > _values.Count) Extend(index - _values.Count); - if (value == null) - _values.Insert(index, ValueFactory.Create()); - else - _values.Insert(index, value); + _values.Insert(index, value ?? ValueFactory.Create()); } + /// + /// Выполняет поиск элемента в массиве + /// + /// Произвольный: Искомое значение + /// Если элемент найден, возвращается его индекс, иначе Неопределено [ContextMethod("Найти", "Find")] public IValue Find(IValue what) { var idx = _values.FindIndex(x => x.StrictEquals(what)); - if(idx < 0) - { - return ValueFactory.Create(); - } - else - { - return ValueFactory.Create(idx); - } - } - + return idx>=0 ? ValueFactory.Create(idx) : ValueFactory.Create(); + } + + /// + /// Удаляет значение из массива + /// + /// Число: Индекс удаляемого элемента. + /// Если индекс находится за границами массива, то выбрасывается исключение + /// [ContextMethod("Удалить", "Delete")] public void Remove(int index) { @@ -133,12 +159,23 @@ public void Remove(int index) _values.RemoveAt(index); } + /// + /// Получает наибольший индекс элемента массива + /// + /// Наибольший индекс в массиве. Если количество элементов массива равно 0, возвращает -1 [ContextMethod("ВГраница", "UBound")] public int UpperBound() { return _values.Count - 1; - } - + } + + /// + /// Получает значение из массива по индексу + /// + /// Число: Индекс элемента. + /// Если индекс находится за границами массива, то выбрасывается исключение + /// + /// Значение элемента массива [ContextMethod("Получить", "Get")] public IValue Get(int index) { @@ -146,8 +183,15 @@ public IValue Get(int index) throw RuntimeException.IndexOutOfRange(); return _values[index]; - } - + } + + /// + /// Устанавливает значение в массиве по индексу + /// + /// Число: Индекс элемента. + /// Если индекс находится за границами массива, то выбрасывается исключение + /// + /// Произвольный: Устанавливаемое значение [ContextMethod("Установить", "Set")] public void Set(int index, IValue value) { @@ -165,20 +209,22 @@ private void Extend(int count) } } - private static void FillArray(ArrayImpl currentArray, int bound) + private static ArrayImpl CreateArray(int bound) { + var array = new ArrayImpl(bound); for (int i = 0; i < bound; i++) { - currentArray._values.Add(ValueFactory.Create()); + array._values.Add(ValueFactory.Create()); } + return array; } - private static IValue CloneArray(ArrayImpl cloneable) + private static ArrayImpl CloneArray(ArrayImpl cloneable) { - ArrayImpl clone = new ArrayImpl(); + ArrayImpl clone = new ArrayImpl(cloneable._values.Count); foreach (var item in cloneable._values) { - clone._values.Add(item ?? ValueFactory.Create()); + clone._values.Add(item is ArrayImpl arr ? CloneArray(arr) : item ); } return clone; } @@ -198,35 +244,37 @@ public static ArrayImpl Constructor() public static ArrayImpl Constructor(IValue[] dimensions) { if (dimensions.Length == 1 && dimensions[0] is FixedArrayImpl fa) - { - return Constructor(fa); + { + return Constructor(fa); } - - ArrayImpl cloneable = null; - for (int dim = dimensions.Length - 1; dim >= 0; dim--) + + // fail fast + int size = 0; + for (int dim = 0; dim < dimensions.Length; dim++) { if (dimensions[dim] == null) - throw RuntimeException.InvalidNthArgumentType(dim + 1); - - int bound = (int)dimensions[dim].AsNumber(); - if (bound <= 0) + throw RuntimeException.InvalidNthArgumentType(dim + 1); + + size = (int)dimensions[dim].AsNumber(); + if (size <= 0) throw RuntimeException.InvalidNthArgumentValue(dim + 1); - - var newInst = new ArrayImpl(); - FillArray(newInst, bound); - if(cloneable != null) - { - for (int i = 0; i < bound; i++) - { - newInst._values[i] = CloneArray(cloneable); - } - } - cloneable = newInst; - + } + + var newInst = CreateArray(size); // длина по последней размерности + + for (int dim = dimensions.Length - 2; dim >= 0; dim--) // если размерность >= 2 + { + ArrayImpl nested = newInst; + int bound = (int)dimensions[dim].AsNumber(); + + newInst = new ArrayImpl(bound); + for (int i = 0; i < bound; i++) + { + newInst._values.Add(CloneArray(nested)); + } } - return cloneable; - + return newInst; } [ScriptConstructor(Name = "На основании фиксированного массива")] diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs index 3fdbff358..f3cfcd92d 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs @@ -149,7 +149,7 @@ public void LoadColumn(IValue values, IValue columnIndex) [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] public ArrayImpl UnloadColumn(IValue column) { - var result = new ArrayImpl(); + var result = new ArrayImpl(_rows.Count); foreach (var row in _rows) { diff --git a/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs index 10a1c8259..077de1ae2 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs @@ -146,7 +146,7 @@ public void LoadColumn(ArrayImpl values, IValue columnIndex) [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] public ArrayImpl UnloadColumn(IValue column) { - ArrayImpl result = new ArrayImpl(); + ArrayImpl result = new ArrayImpl(_rows.Count); foreach (ValueTreeRow row in _rows) { diff --git a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs index 57b74b6b2..57a3f4c81 100644 --- a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs +++ b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs @@ -172,8 +172,8 @@ public FixedArrayImpl GetLogicalDrives { get { - var arr = new ArrayImpl(); var data = Environment.GetLogicalDrives(); + var arr = new ArrayImpl(data.Length); foreach (var itm in data) { arr.Add(ValueFactory.Create(itm)); diff --git a/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs b/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs index 9999dd86c..98aec99f1 100644 --- a/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs +++ b/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs @@ -135,7 +135,7 @@ public void WaitCompletionOfTasks() public ArrayImpl GetBackgroundJobs(StructureImpl filter = default) { if(filter == default) - return new ArrayImpl(_tasks.ToArray()); + return new ArrayImpl(_tasks); var arr = new ArrayImpl(); foreach (var task in _tasks) diff --git a/tests/array.os b/tests/array.os new file mode 100644 index 000000000..6d2bd49a2 --- /dev/null +++ b/tests/array.os @@ -0,0 +1,91 @@ +#Использовать asserts + +/////////////////////////////////////////////////////////////////////// +// Тест класса Массив +/////////////////////////////////////////////////////////////////////// + +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоискВМассиве"); + ВсеТесты.Добавить("ТестДолжен_СоздатьМногомерныйМассив"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗаписьВМногомерныйМассив"); // issue #1663 + ВсеТесты.Добавить("ТестДолжен_ПроверитьОшибкуРазмерности"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьПоискВМассиве() Экспорт + + Массив = Новый Массив(); + Массив.Добавить("1"); + Массив.Добавить("2"); + Массив.Добавить("3"); + + Если Массив.Найти("1") <> Неопределено Тогда + НашлиЗначение1 = Истина; + Иначе + НашлиЗначение1 = Ложь; + КонецЕсли; + + Если Массив.Найти("9") <> Неопределено Тогда + НашлиЗначение9 = Истина; + Иначе + НашлиЗначение9 = Ложь; + КонецЕсли; + + юТест.ПроверитьРавенство(НашлиЗначение1, Истина, "Нашли 1"); + юТест.ПроверитьРавенство(НашлиЗначение9, Ложь, "Не нашли 9"); + юТест.ПроверитьРавенство(Массив.Найти("2"), 1, "Индекс элемента по значению"); + +КонецПроцедуры + +Процедура ТестДолжен_СоздатьМногомерныйМассив() Экспорт + + Массив = Новый Массив(2,4,6,8); + + Ожидаем.Что(Массив.Количество(), "1-я размерность").Равно(2); + Ожидаем.Что(Массив[0].Количество(), "2-я размерность").Равно(4); + Ожидаем.Что(Массив[1][1].Количество(), "3-я размерность").Равно(6); + Ожидаем.Что(Массив[1][1][2].Количество(), "4-я размерность").Равно(8); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗаписьВМногомерныйМассив() Экспорт + + Массив = Новый Массив(3,3,3); + Массив[0][0][0] = "Привет"; + Массив[1][0][0] = "Привет."; + Массив[1][1][0] = "Привет!"; + Массив[2][0][0] = "Привет!!!"; + + Ожидаем.Что(Массив[0][0][0]).Равно("Привет"); + Ожидаем.Что(Массив[1][0][0]).Равно("Привет."); + Ожидаем.Что(Массив[1][1][0]).Равно("Привет!"); + Ожидаем.Что(Массив[2][0][0]).Равно("Привет!!!"); + Ожидаем.Что(Массив[2][1][0]).Равно(Неопределено); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОшибкуРазмерности() Экспорт + + Попытка + Массив = Новый Массив(-1,-2,-3); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + юТест.ПроверитьНеРавенство(Найти(Ошибка,"Неверное значение аргумента номер 1"), 0, "Неверный вид ошибки: "+Ошибка); // не "номер 3"! + Возврат; + КонецПопытки; + + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; + +КонецПроцедуры \ No newline at end of file