Помощь новичкам


#1080

Чисто теоретически, это возможно, по практически о таком я не слышал. По идее, можно использовать не Array[,], а Array of Array. В таком случае, в корневом массиве будет храниться не содержимое строк, а ссылки на строки, занимающие 4 байта. В таком случае, Вы можете создать новый массив массивов и присваивать его элементам ссылки на нужные строки. Что-то типа того:

Uses System;

Procedure SetLength<T>(var arr: array of T, length: Int32);
Begin
  arr := new T[length];
End;

Begin
  var a: array of array of Int32;
  SetLength(a, 10); // 10 строк
  For Var i := 0 to a.Length - 1 do
    a[i] := new Int32[9]; // 10 столбцов
  var newA: array of array of Int32;
  SetLength(newA, 9); // 9 строк. Последнюю удаляем.
  newA[0] := a[0];
  newA[1] := a[1];
  newA[2] := a[2];
  newA[3] := a[3];
  newA[4] := a[4];
  newA[5] := a[5];
  newA[6] := a[6];
  newA[7] := a[7];
  newA[8] := a[8];
  a := newA;
End.

#1081

Если вам надо быстро вставлять/удалять элементы - надо использоваться связные списки. Хотя для многомерных придётся свои написать.

Если же надо просто матрицу у которой можно быстро менять размер - реализуйте свой прямоугольный список на матрице, так же как List основано на массиве.


#1082

Если использовать массив массивов такой var a := new List<integer>[5] то можно удалять столбцы в таком массиве методами для List, и вставлять тоже можно без переопределения памяти. А вот со строками как в этом случае быть я не знаю.


#1083

Привёл код выше.


#1084

Дело тут не в скорости, а в затрате памяти. Самая быстрая конструкция - чистый массив. Всё остальное будет уступать по производительности. Листы и словари - это лишь высокоуровневые оболочки обычного массива, предоставляющие всевозможный сахар.


#1085

Да, но не недооценивайте их. Списки хранят у себя массив большего чем надо размера, с запасом, чтоб позволяет быстро добавлять элементы. И при этом не надо выделять каждый раз новую память, чтоб изменить количество элементов, это самое важное, почему я их рекомендую.

И связные списки, кстати, не основаны на массиве. И скорость добавления элементов у них больше чем у чего либо другого, а памяти выделяется лишь чуть больше чем надо и только когда надо.


#1086

так это выходит каждый раз когда нужно изменить исходный массив массивов придется создавать дополнительный массив массивов подходящей длины и туда копировать ссылки, а после этого копировать ссылку на него в исходный? И в чем тут профит, если все равно нужна еще одна память под дополнительный массив?


#1087

до связных списков я еще не добрался, а вот насчет реализовать прямоугольный списо на матрице… я даже не представляю как это выглядит, не говоря уже за реализацию =)


#1088

Осторожно: очень много костылей! Это лишь чтоб показать идею.

type
  RectList<T> = class
    
    private mtx:array[,] of T;
    private l1,l2:integer;
    
    private function GetItem(i1,i2:integer):T;
    begin
      if cardinal(i1) > cardinal(l1) then raise new System.IndexOutOfRangeException('i1 неправильный') else
      if cardinal(i2) > cardinal(l2) then raise new System.IndexOutOfRangeException('i2 неправильный') else
      Result := mtx[i1,i2];
    end;
    
    private procedure SetItem(i1,i2:integer; o:T) :=
    if cardinal(i1) > cardinal(l1) then raise new System.IndexOutOfRangeException('i1 неправильный') else
    if cardinal(i2) > cardinal(l2) then raise new System.IndexOutOfRangeException('i2 неправильный') else
    mtx[i1,i2] := o;
    
    
    
    public constructor;
    begin
      mtx := new T[4,4];
    end;
    
    
    
    public procedure Resize(nl1,nl2:integer);
    begin
      var res := new T[nl1,nl2];
      
      //ToDo реализовать через System.Array.Copy, так быстрее
      //Только копировать придётся построчно, иначе не сработает
      for var i1 := 0 to Min(Cap1,nl1)-1 do
        for var i2 := 0 to Min(Cap2,nl2)-1 do
          res[i1,i2] := mtx[i1,i2];
      
      mtx := res;
    end;
    
    public property Length1:integer read l1;
    public property Length2:integer read l2;
    
    public property Cap1:integer read mtx.GetLength(0) write Resize(value,Cap2);
    public property Cap2:integer read mtx.GetLength(1) write Resize(Cap1,value);
    
    public property Item[i1,i2:integer]:T read GetItem write SetItem; default;
    
    public procedure Add1(o:ICollection<T>; at:integer);
    begin
      if cardinal(at) > cardinal(l1) then  raise new System.IndexOutOfRangeException('at неправильный');
      var ol := o.Count;
      if ol > l2 then  raise new System.IndexOutOfRangeException('o.Count неправильный');
      
      if l1 = Cap1 then Resize(Cap1*2, Cap2);
      
      //ToDo опять же, через Array.Copy надо
      for var i1 := l1-1 downto at do
        for var i2 := 0 to l2-1 do
          mtx[i1+1,i2] := mtx[i1,i2];
      
      var i2 := 0;
      foreach var a in o do
      begin
        mtx[at, i2] := a;
        i2 += 1;
      end;
      
      l1 += 1;
    end;
    
    public procedure Add2(o:ICollection<T>; at:integer);
    begin
      if cardinal(at) > cardinal(l2) then  raise new System.IndexOutOfRangeException('at неправильный');
      var ol := o.Count;
      if ol > l1 then  raise new System.IndexOutOfRangeException('o.Count неправильный');
      
      if l2 = Cap2 then Resize(Cap1, Cap2*2);
      
      //ToDo опять же, через Array.Copy надо
      for var i2 := l2-1 downto at do
        for var i1 := 0 to l1-1 do
          mtx[i1,i2+1] := mtx[i1,i2];
      
      var i1 := 0;
      foreach var a in o do
      begin
        mtx[i1, at] := a;
        i1 += 1;
      end;
      
      l2 += 1;
    end;
    
    public procedure Remove1(at:integer);
    begin
      if cardinal(at) > cardinal(l1) then  raise new System.IndexOutOfRangeException('at неправильный');
      
      //ToDo опять же, через Array.Copy надо
      for var i1 := at to l1-1 do
        for var i2 := 0 to l2-1 do
          mtx[i1,i2] := mtx[i1+1,i2];
      
      l1 -= 1;
    end;
    
    public procedure Remove2(at:integer);
    begin
      if cardinal(at) > cardinal(l2) then  raise new System.IndexOutOfRangeException('at неправильный');
      
      //ToDo опять же, через Array.Copy надо
      for var i2 := at to l2-1 do
        for var i1 := 0 to l1-1 do
          mtx[i1,i2] := mtx[i1,i2+1];
      
      l2 -= 1;
    end;
    
    
    
    class function operator implicit(a: RectList<T>): array[,] of T;
    begin
      Result := new T[a.l1,a.l2];
      
      //ToDo опять же, через Array.Copy надо
      for var i1 := 0 to a.l1-1 do
        for var i2 := 0 to a.l2-1 do
          Result[i1,i2] := a.mtx[i1,i2];
      
    end;
    
  end;

begin
  var mtx := new RectList<byte>;
  mtx.Resize(3,3);
  mtx.l2 := 3;//так вообще не даст сделать в нормальном коде, надо как то лучше реализовать.
  
  mtx.Add1(new byte[](10,11,12),0);
  mtx.Add1(new byte[](13,14,15),1);
  mtx.Add1(new byte[](16,17,18),2);
  
  var res: array[,] of byte := mtx;
  res.Println;
  writeln;
  
  mtx.Add1(new byte[](21,22,23),1);
  mtx.Add2(new byte[](31,32,33,34),1);
  
  res := mtx;
  res.Println;
  
end.

Примерно так же реализован обычный список.

А связный список - этот тот, в котором после каждого элемента идёт ссылка на следующий. Таким образом прямо в его центр можно вставить что то, без особых проблем. Хотя, конечно, получение элемента по индексу - ужасно медленное, из за того, сколько указателей придётся разыменовывать. Он полезен только если надо вставить много элементов в разные места. Тогда надо сначала преобразовать массив к связному списку, потому добавить/убрать всё что надо, а потом преобразовать назад в массив.


#1089

Сообщение перенесено в тему Болталка PascalABC.NET


#1091

Если неэффективность процессорного времени можно стерпеть, то неправильное использование памяти - катастрофа!

Хотя необоснованные огрехи логики/производительности/памяти свидетельствуют о неверной идее, небрежности или отсутствии знаний, что нужно не терпеть, а исправлять, однако обоснованное (осознанное) пожертвование одним ради другого (например, памятью ради простоты структуры) – это основа современного проектного ПО, где балансируют рисками и компромиссами:

  • что будет если не хватит памяти? - не запустятся другие программы;
  • а что будет если ошибиться в более сложной структуре ради последнего выжатого байта? - нет смысла в такой программе.

Как-то попадалась реализации “заархивированной” структуры, может и в современном многопоточном терабайтном мире .NET есть подобные ноу-хау.


#1092

Именно это я имею в виду. Я всегда делаю оптимизацию именно по памяти, за редким исключением, когда несколько байт даюсь жёсткое замедление программы.

Ну так я же сказал, что мне это напомнило фразу, а не является тем же самым.

Списки предназначены для сложных операций с массивами. Человек хочет всего лишь удалять строки из матриц. Это можно сделать гораздо быстрее с помощью простых массивов, при необходимости подкрепив это указателями.


#1093

Нет, говорили про оба, и столбцы и строки

А разве не по производительности? Тогда почему вы это называли преимуществом:

Это ведь не даст улучшения на фронте памяти, это лишь увеличит производительность.

Ну всё равно, если нужно преимущество памяти - надо тогда как раз создавать новую матрицу, и в новую копировать то что было. Потому что старую после этого удалит, а значит лишней памяти не надо. А array of array создаёт больше ссылок и заголовков каждого массива, поэтому на него надо больше памяти.
Вообще, если вам надо сэкономить максимум памяти - выделяйте область в неуправляемой памяти, с помощью System.Runtime.InteropServices, а array of array вам ничем не поможет.

Более того, следующее:

Не значит что проблема в памяти. выделение больших объёмов памяти так же бьёт и по производительности.


#1094

Не только, не только строки, и столбцы, и не только удалять, но и добавлять =). Хорошо, со строками разобрались вроде, ну так. И со столбцами такое тоже можно наверное замутить, отдельно. А если нужно в одной задаче удалять (добавлять) и строки и столбцы, тогда как?


#1095

Точно так же.


#1097

напишите код пожалуйста


#1100
Uses System;

Procedure SetLength<T>(var arr: array of T, length: Int32);
Begin
  arr := new T[length];
End;

Begin
  var a: array of array of Int32;
  SetLength(a, 10); // 10 строк
  For Var i := 0 to a.Length - 1 do
    a[i] := new Int32[9]; // 10 столбцов
  var newA: array of array of Int32;
  SetLength(newA, 9); // 11 строк. Последнюю надо подписать.
  newA[0] := a[0];
  newA[1] := a[1];
  newA[2] := a[2];
  newA[3] := a[3];
  newA[4] := a[4];
  newA[5] := a[5];
  newA[6] := a[6];
  newA[7] := a[7];
  newA[8] := a[8];
  newA[9] := a[9];
  newA[10] := строка;
  a := newA;
End.

#1120

Как в TabControl помещая вкладки слева (Alignment: Left), получить название вкладок горизонтально – с заглавием слева-на-право, а не снизу-вверх? И, насколько я понял, в WindowForms непросто раскрасить вкладки, только через OwnerDrawFixed?

Также где-то потерял пример фоновой записи (с возможностью отмены) в текстовый файл.

Благодарю


#1121

Это вообще из System.Windows.Forms, или откуда? Приведите пример кода, что где как алайнится, и где в не таком порядке имена даёт.


#1122

Да, WindowsForms, и даже без кода – прямо с дизайнера проекта: если табы-заголовки выровнять по левому краю, они автоматически пишутся снизу-вверх (вертикально) и пока не знаю как победить, чтобы выглядело на подобии wizard’а (слева-направо) горизонтально.