О статических и динамических массивах


#6

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


#7

Жаль, что Вы не раскрыли это Ваше “многократно проще” - почему-то у меня есть стойкое ощущение того, что далеко не многократно и не особо проще. В чем-то да, проще, но в чем-то и сложнее. Прежде всего, напомню, что речь шла об аудитории, которой адресован PascalABC.Net. Основная направленность, насколько я понял - все же сфера образования. Включающая, конечно же, общеобразовательные школы. Так уж сложилось, что чаще всего у школьника первым алгоритмическим языком оказывается именно Паскаль. И изучают его не справочной системе системы программирования, а по учебнику. А там первая информационная структура, с которой школьник сталкивается - массив. В его простейшем, статическом понимании. “Заведем десять ячеек. Первое число мы поместим в первую ячейку, второе - во вторую, … и так поступи со всеми десятью числами”. В первое время, пусть с трудом, но доходит. А теперь представьте себе, что речь о динамическом массиве. “Заведем десять ячеек. В НУЛЕВУЮ (?) поместим первое число, в ПЕРВУЮ - второе,… а последнее, десятое число поместим в ДЕВЯТУЮ ячейку”. Мало того, что ввели новое понятие, так еще и нумерацию сдвинули. “- Мариванна, у меня на руке пять пальцев, мне что, надо на уроке информатики теперь мизинец четвертым называть?”.

Задача. В массиве удвоить значение элемента, если его порядковый номер четный и утроить в противном случае. Как понятнее школьнику, если по оператору попытаться восстановить исходное задание?

if Odd(i) then a[i]*=3 else a[i]*=2; // статический [1..n]
или
if Odd(i) then a[i]*=2 else a[i]*=3; // динамический [n]

Хочу быть правильно понятым: динамические массивы - прекрасная вещь! Но во всех языках они несут на себе отпечаток адресации областей памяти и поэтому индексируются от нуля. Статический массив имеет граничные пары, что в необходимых случаях дает возможность добиться от алгоритма большей прозрачности. Простая задача: по заданному номеру месяца вывести его название. В статический массив заносим 12 наименований и a[n] дает искомое. В динамическом - либо результат достигается через a[n-1], вызывая определенное недоумение у начинающего, либо надо определять 13 элементов и “самый первый” - тот, что перед первым (!) стоит, не учитывать. Прошу не считать это сообщение прологом к холивару про индексацию от 0 и от 1.


#8

Ну, вы пишете именно про индексацию. Ясно, что если для введения динамических массивов пользоваться старыми учебниками, формулировки задач в которых ориентированы на статические массивы и индексацию с единицы, то ничего хорошего не выйдет. Мне кажется, что принцип считающего с нуля компьютера воспринимается школьниками достаточно быстро, особенно их не путать «особыми» массивами, индексируемыми с единицы.

У статических массивов есть одно полезное применение — численные расчёты с векторами и матрицами, размеры которых фиксированы, а как структуры данных они очень плохи. Мы фактически сразу вынуждены задавать максимально возможный размер и вводить фактическую заполненность, иначе остаётся решать задачи как в ваших примерах про 10 ячеек или 12 месяцев, плодя по всему коду «магические» константы.

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

При вводе динамических массивов нужно отказываться от понятия порядкового номера, достаточно ограничиться термином «индекс», объяснив при желании, что это на самом деле смещение от начала.

Кстати, я не слышал от @Admin, как сейчас нужно работать с двоичными файлами: что там с сериализацией динамических массивов? Как сохранить в файл запись со строками произвольной длины и динамическими массивами внутри?


#9

Вот Вам “кажется…”, а я ежедневно в качестве модератора информатики на сервисе “Школьные знания” общаюсь не с одним десятком школьников различных возрастов и вживую вижу те проблемы, о которых пишу.

Вот когда Вы издадите свои учебники по информатике, где все будет базироваться на динамических массивах, и когда их повсеместно признают, тогда можно будет говорить об отказе от “анахронизма”. А сейчас для обучения гораздо востребованее не “динамические массивы” с какой-то особой структурой, а массивы с динамически изменяемой верхней границей. Как в Бейсике. Там не делят массивы на статические, динамические… Если надо границы поменять, выдал в нужном месте ReDim [Preserve] и живешь спокойно дальше. Похожее есть и Pascal: SetLength(), только без опции, позволяющей определить, зачищать ли содержимое массива с изменившимися границами.

Не согласен. Вы сами писали о векторах, матрицах - целом разделе математики. И что, говоря о трехмерном пространстве, мы должны считать что координата Х отображается в нулевом индексе? Есть индекс и есть порядковый номер. “- Алло, девушка, почему мне до сих пор не предоставили справку, Вы же сказали. что я первый на очереди? - Да, но Вы забыли, что перед Вами еще нулевой?” И не надо пытаться заставить человечество считать все от нуля лишь потому, что так было удобно когда-то индексировать области памяти в ассемблере.


#10

@RAlex вы зря тут меряетесь количеством школьников: у @bravit достаточно школьников, чтобы судить (да и у меня тоже). Если вы видите проблемы, то это, как справедливо сказал @bravit, проблемы подходов в обучении, методического обеспечения и так далее, но не проблемы динамических массивов. При должном подходе всё получается вполне доступно и даже лучше, чем с громоздким синтаксисом статических. Таков наш опыт в Воскресной компьютерной школе.

По поводу нужности. Не знаю, как в ПаскалеАБЦ, а в жизни статические массивы нужны. Традиционно у них есть свои экологические ниши: численные рассчёты, а также низкоуровневое программирование аля C (буфера). Разницу нужно понимать, но — не школьнику, а хотя бы студенту (или 11-класснику). А школьнику 7-8 классов (когда мы вводим массивы) нужно предоставить простейший из возможных синтаксисов и всё. Без философии, без низкоуровневой организации и т. п.

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

PS Тему надо отделять, на мой взгляд.


#11

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

А приведите пример, пожалуйста, этого нехорошего “громоздкого синтаксиса статических массивов”. Вот я вчера попробовал переработать с Алгол-60 некогда известный алгоритм 120б “Обращение матрицы методом Гаусса-Жордана”. На статических массивах сразу написал и заработало. А для перехода к динамическим массивам пришлось немного потрудиться, потому что надо было переводить все на индексирование с нуля. Программа, естественно, не стала ни короче (в байтах исходник даже длиннее из-за постоянных n-1), ни понятнее.

P.S. И не надо этого цехового “меряетесь количеством школьников…”. Я не о количестве говорю, а скорее, о качественной разнице.

И - да, тему надо отделять. Или просто почистить)))


#13

Тогда уж так

var b := a.ToArray;

Intellisense надо поправить


#14

В двоичных файлах типа file или file of что-то сохранить объекты со ссылочной семантикой не получится.

Для этого надо использовать более мощную .NET-сериализацию:

uses System;
uses System.IO;
uses System.Runtime.Serialization.Formatters.Binary;

type 
  [Serializable]
  RC = record
    s: string := 'ABC';
    a := Arr(1,2,3);
  end;

begin
  var formatter := new BinaryFormatter();
  var fs := new FileStream('d:\aa.dat', FileMode.OpenOrCreate);
  var r: RC; 
  formatter.Serialize(fs, r);
  fs.Close;
  r.s := 'XYZ';
  r.a := Arr(666,666,666);
  fs := new FileStream('d:\aa.dat', FileMode.OpenOrCreate);
  r := RC(formatter.DeSerialize(fs));
  fs.Close;
  Println(r);
end.

#15

Мы будем использовать в срезах синтаксис a[2:10:2]. Не советуйте нам пожалуйста синтаксические конструкции, которыми вряд ли кто-то будет пользоваться


#16

А вот это отличные новости! Чувствуется, что система программирования, как раньше говорили, идет “верной дорогой” )))

Да я только “за”! Если победите Write(a[3:12:4][2:10:2]:6:3), будет замечательно.


#17

Проще. Это Вы привыкли просто. Ребенку, которому рассказывают это впервые, особой разницы нет.

Но - Вы правы - старых учебников много, и там везде с единицы. Мы и не запрещаем старые массивы. Но Вы получаете вместе с ними тяжелое наследство: память выделяется жестко в процессе компиляции и чтобы их передавать - нужна именная эквивалентность:

type Arr = array [1..10000] of integer; // на всякий случай 10000
procedure Print(const a: Arr; n,m: integer)

и

procedure Print(a: array of integer)

Я уж не говорю, что мы лишаемся огромного количества стандартных методов работы с массивами.

Поэтому проще в дальнейшем - для решения более высокоуровневых задач. А так - побаловаться с индексами в короткой программе на 5-10 строк - да, не проще.


#18

Это вопрос, в начале чьего обучения.

Динамические структуры - давайте не смешивать с динамическими массивами. Динамические структуры - это структуры, меняющие свою структуру (связи между элементами) во время выполнения программыю

Не забывайте также, что у нас и строка string - динамическая. Собственно, она и во Free Pascal динамическая если Вы специально не ставите опцию “программировать в стиле устаревшего Turbo Pascal”. Вообще, весь мир - динамический. И - открою страшную тайну - у нас и статические массивы на самом деле динамические, просто они косят под статические :slight_smile:


#19

Именно так. Их же не раздражает, что в ячейках компьютера хранятся нолики и единички.

Как Вы молодого человека убедите - так он и будет сразу считать правильным.

Вспомните - в Путешествии Гулливера были остроконечники и тупоконечники.


#20

Мы поступаем по-другому как видите. Везде на сайте PascalABC.NET будут культивироваться только динамические массивы как правильный код.

В частности, скоро конструкция

for i:=1 to n do

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


#21

Всё зависит от примеров. Недавно в сбербанке я был первым в очереди с номером номерка 973.

Правильные примеры, сказанные уверенно и без издёвки, позволят школьнику легко запомнить индексацию с нуля. Вопрос - в целях.


#22

Нет, нет - тут Вы не правы. C++ нельзя. И - это обычные дети. Ничего необычного в них нет. Просто они хотят знакомиться с современными компьютерными технологиями. C++ нельзя - еще раз. Совсем маленьким можно Scratch - в курсе пользователя.


#23

Ну - как хотите - это ваша привычка - не более. Если бы Вы сразу написали на динамических массивах - то при переходе на статические Вы бы ругались.

Должен заметить, что при обращении матрицы методом гаусса-Жордана Вы задаёте предельный размер матрицы - скажем, 100 на 100 - и постоянно думаете о двух проблемах: память расходуется неэффективно и не понадобится ли мне в будущем матрица размера 101.


#24

Тогда уж так:

const n=15;
type Arr=array[1..n] of integer;
procedure Print(a:Arr);

А никакого “огромного количества” нет. Все это количество - заслуга LINQ, который немедленно массив превращает в последовательность и снова сделать её массивом помогает волшебное слово ToArray. Но если у нас есть возможность преобразования статического массива в динамический посредством этого же волшебного слова, разница нивелируется.

Еще раз - я “всеми лапками” за динамические массивы. Однозначно, будущее программирования за ними. Поэтому на том ресурсе, о котором я упомянул (не хочу повторяться, чтобы не выглядело рекламой), если я выкладываю решение, я пишу его на последней версии PascalABC.NET и практически всегда на динамических массивах+LINQ. Пусть привыкают и видят, как это коротко и красиво. Но пока наши учебные программы для начинающих к этому не готовы, нам нужны и статические массивы. И не на правах бедных родственников. Жаль, что они не аналоги математических векторов, матриц и т.д. но раз так - значит так. Хотя, конечно, MatrixRandom(), который создает отнюдь не матрицу в обычном её понимании - семантически не самое удачное имя.


#25

А я заметил. Программа со статическими массивами работает дольше. Но это уже особенность реализации.

Возможно, Вы не обратили внимания, что было написано: алгоритм ПЕРЕВОДИЛСЯ с Алгол-60. В те времена еще не было массовых привычек индексировать от нуля. Поэтому небольшие проблемы были именно с переходом на такую индексацию. Что до размера - для этого в Паскале давно уже научились объявлять его в const и там менять, если надо.

Кстати, заодно. Может, это я такой невезучий, но на двухмерных массивах Swap(a[i,j],a[j,i]) существенно проигрывает прямому обмену через промежуточную переменную. Проверьте, если время будет.

Да-да, конечно строка динамическая. И как “дань предкам”, обращение s[0] ведет к ошибке, потому что в динамической строке элементы нумеруются … от единицы. Подгадили предки, которые раньше в s[0] длину строки хранили…


#26

Проверил на таком алгоритме.

procedure Transpose(a: array [,] of real);
begin
  var m := a.GetLength(0);
  for var i:=0 to m-1 do
  for var j:=0 to i-1 do
  begin
    var t := a[i,j];
    a[i,j] := a[j,i];
    a[j,i] := t;
  end;
  //Swap(a[i,j],a[j,i]);
end;

begin
  var a := MatrixRandomReal(10000,10000);
  Transpose(a);
  Print(Milliseconds);
end.

Не проигрывает. Приведите свой.