Замечания и предложения

Я думаю, счет идет не на десятки, а на сотни тысяч. Часть свободного времени провожу на бесплатном ресурсе “Знания”, имеющем более 100 млн (!) посещений в сутки и абсолютное большинство школьников и студентов-младшекурсников России, Украины, Белоруссии и Казахстана просят что-то решить на “PascalABC” (который на поверку оказывается все-таки PascalABC.NET), либо на PascalABC.NET, Другое дело, что они почти поголовно все еще используют подмножество языка “Турбо Паскаль”, но и за решения в функциональном стиле не забывают сказать “спасибо”.

А давайте проведем аналогию между обучением и выводом спутника на орбиту. Много ли спутник, находясь уже “там”, думает о ступенях ракеты-носителя, которые его на эту орбиту вывели и потом были отброшены? Так что, эти ступени делать не надо было или надо было делать кое-как? PascalABC.NET - и это написано - трамплин к С# и ключ к современным технологиям программирования, обёрнутый в простой, четкий и понятный синтаксис языка Pascal.

Да, наверно это лучше.

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

:slight_smile: Можете вычеркивать, но учтите:

Пять негритят судейство учинили,
Засудили одного, осталось их четыре. 
4 программиста работали на Си,
Один из них хвалил Паскаль, и их осталось 3. 
begin
  var s := 'Карл у Клары украл кораллы Клара у Карла украла кларнет';
  var el := s.ToWords.Select(t -> (t, t.Length)).MaxBy(t2 -> t2[1])[0];
  Writeln($'Первое из самых длинных слов: {el}');
end.

А зачем вообще массив, если вся последовательность перебирается только 1 раз?

P.S. а вообще кортежи тут не сдались

begin
  var s := 'Карл у Клары украл кораллы Клара у Карла украла кларнет';
  var el := s.ToWords.MaxBy(w -> w.Length);
  Writeln($'Первое из самых длинных слов: {el}');
end.

А я не понимаю, зачем тут вообще не только 2 доп. последовательности и 2 массива, но еще и кортеж в них:

begin
  var s:='Карл у Клары украл кораллы Клара у Карла украла кларнет';  
  writeln('Первое из самых длинных слов: ', s.ToWords.MaxBy(t->t.Length));
end.

P.S. Серега опередил меня, пока я сочинял ответ про школу :slight_smile:

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

<значение> <количество_повторов> [строка,столбец] [строка,столбец] …

procedure RunWithArray;
begin
  
  var s := 'Карл у Клары украл кораллы Клара у Карла украла кларнет';
  
  var ws := s.ToWords.ToArray;
  
  var max_l := ws.Max(w->w.Length);
  var max_l_ws := ws.Where(w->w.Length=max_l);
  foreach var w in max_l_ws do;
  
  s := ws.Where(w->(w.Length=max_l) or (w.Length < 4)).JoinIntoString;
  
end;

procedure RunWithoutArray;
begin
  
  var s := 'Карл у Клары украл кораллы Клара у Карла украла кларнет';
  
  var ws := s.ToWords;//эта строчка единственное различие
  
  var max_l := ws.Max(w->w.Length);
  var max_l_ws := ws.Where(w->w.Length=max_l);
  foreach var w in max_l_ws do;
  
  s := ws.Where(w->(w.Length=max_l) or (w.Length < 4)).JoinIntoString;
  
end;

begin
  
  var c := 1000*1000;
  var t:System.DateTime;
  
  t := System.DateTime.Now;
  loop c do RunWithArray;
  var ts1 := System.DateTime.Now-t;
  
  t := System.DateTime.Now;
  loop c do RunWithoutArray;
  var ts2 := System.DateTime.Now-t;
  
  writeln($'time 1 is {ts1}');
  writeln($'time 2 is {ts2}');
  readln;
  
end.

У меня выдаёт соответственно 4 и 3.4 сек. Потому что лишние 2 раза посчитать .ToWords проще чем создавать массив (у массива очень не эффективный код заполнения, а .ToWords находит в строке координаты всех слов и копирует их).

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

///Преобразует строку в массив слов
function ToWords(Self: string; params delim: array of char): array of string; extensionmethod; 

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

1 лайк

Да, мы знаем масштабы. Нас скачивают 3000 раз в день и уже скачали 3 млн. раз с момента начала проекта. Понятное дело - заставляют.

2 лайка

Не говоря о сути решения - так будет лучше?

TL;DR

Summary

Что касается десятков/сотен тысяч обычных школьников (хотя кто их считал вообще?) и студентов не IT-специальностей, абсолютно уверен, что им вообще не нужны все эти продвинутые фичи, типа классов, модулей, интерфейсов, расширяемых пространств имен, срезов, лямбд, последовательностей, LINQ, перегрузки операторов и функций, дженериков, методов расширения и т.п. “наворотов” из .NET и прочих сугубо профессиональных языков.

Убежден, что, по крайней мере в средней школе, им нужна классическая информатика, а не компьютерные науки, т.е. базовые знания об устройстве классических компьютеров, простых алгоритмах, структурах данных, простых способах ввода-вывода и обработки информации, алгебре логики и т.п. Им прежде всего нужно дать понять, как работает их компьютер/телефон, как работает программа, привить им алгоритмическое мышление, а не просто показывать разные красивые фокусы, типа решения домашней задачки в одну-две строчки на LINQ или демонстрации готовых китайских коптеров, танцующих роботов и прочих модных игрушек, которые, конечно, завораживают юную душу, но никак её не развивают.

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

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

Я хоть активно и не помогаю решать детские задачки лентяям и халявщикам школьникам на разных ресурсах, но тоже не вчера родился и кое-что знаю о современной школе/ВУЗах и молодежи. Вообще, нынешняя сверхдоступность массовых коммуникаций, готовых ответов и разных “помощников” в интернетах вкупе с массовым недостатком хороших и мотивированных учителей (+ коррупция, циничный бизнес на всем, шаблонные задания и прочее) развращают многих учеников/студентов до безобразия, демотивируя их думать своей головой и далее по жизни критически относиться к поглощаемой информации!

Приведенный вами факт, что только на одном русскоязычном “школьном” ресурсе регулярно наблюдается >100 млн. посещений в сутки (думаю, что все-таки, загрузок страниц или типа того, а не уникальных посетителей, но все равно это очень много), подтверждает мои наблюдения – большинство школьников интересуют не знания и творческий процесс поиска решения, а готовые ответы и хорошие оценки! Копи-паста заменила им мозг :frowning:

В итоге мир заполонили миллионы дрессированных обезьянок, шустро клепающих г0внокод, но свято убежденных, что они программисты. А иногда так думают даже их работодатели – видимо, выходцы из таких же школ и ВУЗов :frowning:

Я вас умоляю, какой спутник, какие орбиты?

  1. В большинстве случаев они банально клянчат готовое решение домашнего задания, а не хотят получить толковое разъяснение по пропущенной/непонятной им теме или короткую подсказку, чтобы продвинуться в поиске своего решения дальше.
  2. 99% из них используют только возможности и синтаксис ТурбоПаскаля и это вряд ли изменится в обозримом будущем (хотя бы потому, что для школьной программы этого достаточно, хотя есть тому и масса других причин).
  3. Они, как правило, рады любому рабочему решению, вне зависимости от его кривости или продвинутости, понимают они его или нет.
  4. “Продвинутое” решение на .NET, детально не понимаемое учеником, совсем не обязательно должно понравиться его учителю, даже если он сам знает .NET. Мне бы точно не понравилось. Плюшки .NETa – это не цель для обычной школы, а тупая копипаста – вообще самое большое зло!
  5. Забивая простым школьникам голову каким-то конкретным фреймворком и его фичами, типа готовя их вывести на орбиту великих достижений, на самом деле провоцирует детей прыгать по вершкам и создает из них скорее разумных обезьянок, а не будущих пытливых инженеров. Модные фреймворки приходят и уходят, а вот классические фундаментальные познания остаются актуальными очень долгое время, поэтому, на мой взгляд, не стоит в базовой программе по информатике тратить время на подобные вещи – учебных часов и так катастрофически не хватает.

Этот проект действительно не для продакшена – скорее это смелый академический эксперимент, классическое подмножество которого (+ некоторые полезные упрощения) весьма удобно применять в начальном IT-образовании как замену классическому Паскалю, а также для хобби и быстрого создания рабочих прототипов “на коленке”. Поэтому когда кто-то говорит, что внесение в этот Паскаль какой-то новой фичи “поможет тысячам”, меня забавляет такая слепая вера в могущество и популярность столь нишевого диалекта PascalABC.NET. Для тех “тысяч”, о которых вы говорите, уже давно все сделано и работает – только учитесь! А кол-во остальных продвинутых добровольцев пока что измеряется несколькими десятками, к сожалению…

Это характерное мнение. Но - согласитесь - это мнение человека, который не учит школьников массово.

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

Однако общепризнанный прогресс в развитии образовательных технологий для детей делает своё дело: от Logo с его Черепашкой и обучением детей программированию на графике до Scratch, в котором зашиты события, параллельность, сетевое взаимодействие - огромный скачок в методике обучения именно младших школьников. При этом хватало ретроградов, которые говорили, что всё это не нужно - достаточно основ алгоритмизации на бейсике.

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

Но что совершенно точно - современный школьник стремится решать достаточно сложные высокоуровневые задачи, которые раньше - при подходе старых учителей - было решать невозможно, т.к. языки были убогими и подавлялась сама мысль о необходимости решения таких задач (“не нужно это вам”)

3 лайка

Вот это я совсем не понял. Расширение .ToWords и так возвращает массив, зачем его еще раз в массив превращать?

да, мне уже выше сказали об этом…

В классе String имеется экземплярный метод Replace(s1,s2:string):string, который возвращает строку, полученную из исходной строки заменой всех вхождений подстроки s1 на строку s2.

Существует большой класс задач, в которых требуется произвести однократную замену s1 на s2, причем порядковый номер вхождения s1 в исходную строку не обязательно равен единице. Безусловно, такую замену сделать можно и существующими средствами, но это дает достаточно громоздкий внешне и, подозреваю, неэффективный по выполнению код, если вспомнить сказанное о “read-only” реализации операций над строками в среде .NET.

Поэтому - очередная “хотелка”.

Нельзя ли добавить метод/расширение, назовем его условно ReplaceOnce, который будет позволять сделать в строке однократную замену k-го вхождения s1 на s2 (по умолчанию k=1) ? Если k-е вхождение не существует, можно вернуть исходную строку без изменения, либо вызвать исключение.

Мы даём такие задания школьникам.

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

Вносить такой метод как стандартный не вижу необходимости. Тем более, что в .NET такой стандартный метод не сделали.

Очень жаль. Потому что проблема не индексы получить - проблема в замене. Либо надо делать эту ужасную пару Delete + Insert, либо собирать результирующую строку по схеме “до индекса” плюс заменяющая подстрока плюс “после индекса со сдвигом на длину найденной подстроки”. Очень некрасиво. Получается, что “замену везде” можно сделать быстро и удобно, а замену в одном месте приходится выполнять с разными фокусами.

Вот пример, не далее как вчерашний.

     Какая строка получится в результате применения приведённой ниже программы
к строке, состоящей из 193 идущих подряд цифр 5?
ПОКА нашлось ('333') ИЛИ нашлось ('555') ВЫПОЛНЯТЬ
   ЕСЛИ нашлось ('555') ТО заменить ('555' НА '3')
   ИНАЧЕ заменить ('333' НА '5')

Собрался писать свою процедуру “заменить”. А потом вспомнил, что в многократно обруганном бейсике есть своя REPLACE, имеющая аж шесть параметров:

  • исходная строка
  • что заменить
  • чем заменить
  • начальная позиция поиска (необязательный)
  • количество проводимых замен (необязательный)
  • cпособ сравнения строк

Это был тот самый случай, когда понимаешь, что нужно знать несколько языков…

Ну да, я согласен пожалуй - не хватает такой функции.

Вот что мне не нравится. Как Вы её хотите применять? Для каких задач? Понятно, что заменять вначале первое вхождение, потом искать с начала и заменять второе (теперь первое) вхождение неэффективно.

Заменить второе вхождение - это - ну - можно сделать, но часть ли возникает такая задача?

Я бы делал так: с помощью Regex.Matches искал бы позиции всех вхождений, находил бы нужное, а потом - ненавистная пара delete-Insert.

1 лайк

Вы не поверите: я именно так и делаю… ))) Из всех задач, что мне попадались, наверно в 8-9 случаях из 10 заменяется только первое вхождение. Если нужно потом еще - я обычно выкручивался срезами. Если сразу несколько замен - один проход в цикле с конца строки, чтобы при вставке не сдвигать текущею позицию просмотра в строке.

1 лайк

А вот так замена решается в Go:

func Replace(s, old, new string, n int) string

Replace returns a copy of the string s with the first n non-overlapping instances of old replaced by new. If old is empty, it matches at the beginning of the string and after each UTF-8 sequence, yielding up to k+1 replacements for a k-rune string. If n < 0, there is no limit on the number of replacements.

Если в PascalABC.NET снабдить метод Replace(s1,s2:string):string третьим параметром n:integer:=-1, тогда во-первых, не понадобится вводить дополнительный метод, а во-вторых, сохранится преемственность с имеющимся Replace. А реализацию случая множественной (но не полной) замены для эффективности можно сделать на базе StringBuilder.

1 лайк

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