29 нояб. 2011 г.

Почему я не люблю курильщиков?

О негативном отношении к курящим людямМеня раздражают курящие люди, но делают они это каким-то своим хитрым способом. При этом нельзя сказать, что я однозначно недолюбливаю всех курильщиков. Если я уже знаю человека с хорошей стороны, то тот факт, что он курит, мне почти не мешает. Но любого постороннего курящего человека я почему-то сразу принимаю хуже.

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

Вроде бы не очень сложные навыки и мысли, так ведь?

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

В самом деле, вред от курильщиков не очень велик (пусть раздражают, но жить с этим можно), а за своё здоровье они уже сами отвечают. Это алкоголики и наркоманы нападают на людей, чтобы получить деньги на очередную порцию, это кредитоманы обманывают друзей и родственников, чтобы уговорить их стать поручителями по «пустяковому делу», это игроманы тратят деньги на игровые шмотки, оставляя семью без еды. А у среднестатистических курильщиков таких проблем нет.

А что же тогда есть? Откуда может быть неприязнь к этим милым людям, которые всего лишь мирно посасывают подожжённую бумажную трубочку? Разве армейская присказка «курящие пока перекурят, а остальные метут плац», фактически подталкивающая всех солдат к сигаретам, может заставить смотреть на курильщиков с большим скепсисом? Пожалуй, нет.

Но причина неприязни есть! Один из важнейших недостатков курильщиков — они приносят прибыль производителям сигарет. Смешно звучит? Ну да, раз существуют, значит прибыльны. Беда в том, что если что-то приносит прибыль, то любой нормальный бизнесмен постарается прибыль увеличить или хотя бы поддержать. А как можно поддерживать сигаретный бизнес? Надо привлекать новых клиентов (вместо постепенно выбывающих). И эффективнее всего окучивать именно детей, потому что
- их проще обмануть разговорами о «крутости»,
- чем раньше начнут курить, тем больше прибыли принесут за свою жизнь.

Рекомендую прочитать на эту тему статью «ОАО «Детский табак». Саму статью цитировать здесь не буду, но советую ознакомиться с детскими текстами на промо-сайтах этих сигарет (например, smokingirl и kiss-club). На этих форумах дети прямо указывают, что им, например, 12 лет, после чего спрашивают совета (как обмануть учительницу, которая не только застукала ребёнка за курением, но и собирается позвонить родителям).

Если производители сигарет поддерживают детское курение на своих собственных промо-сайтах, то можем ли мы надеяться, что они не хотят продавать сигареты детям до 18 лет? Особенно, если выпускаются специальные сигареты с яркими рисунками, которые взрослый человек скорее примет за упаковку конфеток, чем за пачку сигарет. Особенно, если производится реклама, в которой очень молодо выглядящие модели призывают: «Если нельзя, но очень хочется, то можно». Особенно если цена у этих сигарет близка к смешным 20 рублям за пачку (даже дети из малообеспеченных семей найдут эту сумму). Ну а раз эти бизнесмены открыто агитируют за курение детей до 18 лет, то и противостоять им кто-то должен, так ведь?

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

А почему вы любите или не любите курильщиков?

20 нояб. 2011 г.

Три разнородные ссылки

Добрый день!

Сегодня в нашей традиционной рубрике «Три чего-нибудь» я предлагаю три ссылки.

1. В записи «Послесловие к самоссылающимся задачам» Константин Кноп предлагает перевод на русский язык хорошей подборки таких задачек. Прекрасный пример интересной и достаточно сложной задачки — восстановить все буквы в предложении «Это предложение содержит ___ слов(?), ___ ___ слог(?) и ____ ____ букв(?)» (вопросы в скобках означают одно из возможных окончаний).

Гораздо проще найти ответы на следующие вопросы (и куда интереснее подобные вопросы придумать самому):
- Чему равна площадь квадрата, периметр которого равен 16?
- Какой долей ведра являлась древнерусская мера объема «четверть»?
- Какова доля гласных в слове «половина»?

2. В заметке «Спецификации как мера неэффективности» Илья Бирман формулирует простую мысль: «Если в каком-то устройстве слишком быстрый процессор или слишком много памяти, то я понимаю две вещи: 1) оно стоит больше, чем могло бы, потому что я плачу за лишнее железо; 2) оно работает меньше, чем молго бы, потому что вся эта фигня зря жрёт батарейку». Но слишком многие люди привыкли хотеть не результата, а ещё более высоких характеристик, поэтому маркетологи успешно впаривают очередные гигагерцы и гигабайты, хотя я бы предпочёл скорость и стабильность.

3. А в разборе «Про политику: строить vs. ломать» приводится анализ распространённых мифов о высказываниях/планах некоторых политиков. Мол, они все идиоты, которые ещё и нередко проговариваются о своих антинародных планах. Вырвать слова из контекста и растиражировать их через юмористические ресурсы — дело не очень сложное... Тем более, что «журналисту» достаточно эти слова просто выдумать. И происходит это по одной простой причине: очень многие не только не станут проверять правдивость сообщения, но и не вспомнят, что каждый месяц возникает несколько одинаковых новостных поводов об очередных страшилках вроде «вместо винтовок министерство обороны закупает бадминтонные ракетки». Опровержения всегда звучат тише агитационных материалов, поэтому желающие всегда имеют повод радостно наслаждаться ужасами, не нагружая зря мозг. И, что очень досадно, весь этот неконструктивный визг успешно вытесняет темы, которыми на самом деле надо заниматься.

Хорошего воскресенья!

14 нояб. 2011 г.

Пробуй!

Хорошо продуманная задачка устроена так, что большинство решающих сначала проходят по всем запланированным автором «граблям», а только потом добираются до решения. Опытный человек сразу замечает простейшие ответвления, на которые лучше даже не тратить время, что позволяет ему быстрее дойти до ответа. Очень талантливые и развитые товарищи сразу видят кратчайший путь к заветной цели (естественно, если задачка это позволяет :)

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

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

Ровно месяц назад мы рассматривали задачки про маляров и котлеты. Ответы в комментариях появились почти сразу, а вот решения мы там не разобрали. Подобные задачки я отношу к группе «если пробовал решить, то решил» (т.е. очень простые, так как не содержат какого-либо подвоха, а предполагают только умение читать). Соответственно, эти задачки отделяют тех, кто не делал домашнее задание от тех, кто делал, но не смог (с рядом оговорок, конечно).

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

Сначала разберёмся с тремя котлетами, которые надо приготовить на двухместной сковородке за минимальное время. Первую минуту мы можем жарить любые две котлеты (т.е. первый ход можно зафиксировать). Но вот если на вторую минуту ничего не поменять (лишь перевернуть котлеты), то к началу третьей минуты у нас останется одна сырая с двух сторон котлета, которая потребует ещё двух минут времени (и половина сковородки будет зря простаивать, что намекает на неоптимальность данного решения). Это значит, что уже на второй итерации надо что-то менять. А поменять-то мы можем только одно — набор котлет на сковороде (одну придётся оставить, конечно). Сделав этот шаг, мы сразу понимаем, что именно надо делать на третьей минуте. А так как сковорода каждую минуту была заполнена на 100%, то быстрее приготовить три котлеты невозможно.

Чему нас учит эта задачка? Тому, что иногда надо попробовать решить, а не просто «читать условие». Первая попытка сразу указала нам, где надо менять. И внезапно оказалось, что менять мы можем единственным образом. Т.е. уже вторая попытка сразу привела нас к идеальному решению.

Теперь разберёмся с малярами и перчатками. Опять же, надо пробовать, а не бояться неведомого. У одной перчатки две стороны, а маляры испачканы четырьмя разными цветами, поэтому одной перчатки заведомо не хватит (надо как минимум две). А что можно сделать с двумя перчатками? Есть следующие варианты первого действия:

1) Надеть перчатки на руки маляров, идущих навстречу друг другу,
2) Обе перчатки надеть на руки маляров, идущих в одну сторону.

Если мы пошли по первому пути, то произойдёт три рукопожатия (сначала поприветствуют друг друга маляры в перчатках, что сохранит чистоту внешних сторон перчаток). Проблема в том, что после этих трёх рукопожатий ничего больше нельзя сделать [без добавления чистых перчаток], а нам нужно ещё четвёртое приветствие.

Если же мы пойдём по второму пути, то опять есть два варианта:

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

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

Опять же, мы просто рассмотрели все возможные варианты, пытаясь не потратить больше двух перчаток. Мы попробовали, мы честно расписали возможности. Часто этого достаточно. Безусловно, настоящие сложные задачи не позволят одолеть себя так легко. Но это не значит, что не надо развивать в себе способность справляться с простыми. Когда средние задачи покажутся вам простыми, тогда сложные покажутся средними :)

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

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

Хорошего дня!

10 нояб. 2011 г.

Исключения из исключений

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

За прошлый разговор с ним мы выбрали язык программирования (Javascript), определили задачу (преобразовать английское существительное из единственного числа во множественное), сделали простейшую систему тестирования (собрали небольшую тестовую базу вопросов-ответов из интернет-тестов) и начали реализовывать функцию, решающую задачу (пока эта функция просто добавляет «s» к любому слову).

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

Что же они сделали? Не очень много, но этим хочется поделиться:

1. Расширили тестовую базу в полтора раза (взяли примеры из википедии). В процессе этого расширения осознали, что надо слегка переделать тестовую систему, так как бывают слова, имеющие более одной формы множественного числа (например, можно писать и «volcanoes», и «volcanos»), а сейчас это никак не поддержано. Новая версия содержит 59 вопросов (и ответы на 27 из них считаются неправильными).

2. Гуманитарий рассказал, что о множественном числе существительного имеет смысл говорить, если это существительное является исчисляемым. Более того, невозможно без контекста определить, с исчисляемым ли существительным мы имеем дело. Например, «coffee» обычно считают неисчисляемым. Но если мы говорим не о кофе вообще, а о чашках кофе, то вполне можно говорить «two coffees» (как и «two cups of coffee»). Поэтому стоит добавить вывод предупреждения хотя бы для распространённых неисчисляемых существительных. Два заинтересованных человека показали высокую самостоятельность — они не только нашли в интернете список распространённых неисчисляемых существительных, но и написали функцию isUncountable(), определяющую, является ли существительное неисчисляемым:

function isUncountable(iNoun) {
    
var aCommonUncountable = new Array(
        
"water", "tea", "coffee", "milk", // Liquids
        "air", "oxygen", "hydrogen", "nitrogen" // Gases
        // etc
    );
    
for (var i = 0; i < aCommonUncountable.length; i++)
        
if (aCommonUncountable[i] == iNoun)
            
return true;
    
return false;
}

(конечно, ребята подобрали гораздо больше примеров и добавили простейшую проверку правильности функции)

3. Ещё они очень захотели добавить несколько новых простых правил к нашей функции getPlural(). Например, надо научиться определять, оканчивается ли строка на «ch», «sh», «s», «z» или «x». В таких случаях почти всегда для получения множественного числа надо добавить «es». Реализация этой проверки позволила бы заметно продвинуться в улучшении качества результатов.

Тогда я рассказал ребятам о регулярных выражениях. Конечно, разговор был очень поверхностным. Сначала мы расширили нашу функцию, пользуясь методом substr() (т.е. для каждой строки проверяли, совпадают ли два её последних символа с «ch», совпадает ли её последний символ с «s» и так далее. А потом мы разобрались, как всё это многословное безобразие можно заменить одной строкой регулярного выражения:

function  getPlural(iSingular) {
    
if (/ch$|sh$|x$|s$|z$/.test(iSingular))
        
return iSingular + 'es';
    
return iSingular + 's';
}

С этим дополнением программа стала выдавать неправильный результат на 22 тестах из 59. Стало лучше, но ещё есть куда двигаться, так как осталось ещё несколько правил, которые предстоит поддержать, да и исключения (слова вроде «woman» — «women») надо отдельно обрабатывать.

4. Конечно, ребята остро осознали необходимость интерактивной версии. В комментариях к предыдущей заметке были замечания, что именно с неё, а не с тестовой системы надо начинать. Возможно, тут сказывается моя привычка экономить время там, где его можно зря не терять (именно из-за этого я предпочитаю один раз сделать тестирующий код, а не дёргать постоянно интерактивную версию «руками»)... В любом случае, мы пошли этим путём (сначала система тестов, а уже потом приложение для пользователя), что не помешало ребятам попробовать самостоятельно учиться, начать работать с регулярными выраженими и лучше понять тонкости английского языка. Но рано или поздно надо делать пользовательскую версию, что мы и реализовали.

В прошлой заметке мы убедились, что нарисовать табличку 2 на 2 клетки очень легко, поэтому сейчас мы заполним эти клетки следующим образом

Singular: [поле ввода]
Plural: [результат работы функции]
Сделаем это так (сначала идёт реализация функции, возвращающей множественное число, потом вспомогательная функция для взаимодействия с пользователем, далее HTML-код таблицы с формой):
<script type="text/javascript">

function getPlural(iSingular) {
  
if (/ch$|sh$|x$|s$|z$/.test(iSingular))
    
return iSingular + 'es';
  
return iSingular + 's';
}

function writePlural() {
  document.getElementById(
"plural").innerHTML = getPlural(document.getElementById("singular").value.toLowerCase());
}

</script>

<table border=0>
  
<tr><td>Singular:</td><td><input id="singular" onkeyup="writePlural()" value="test"></td></tr>
  
<tr><td>Plural:</td><td><div id="plural">tests</div></td></tr>
</table>

(сохраните этот код в файл plural.html, после чего откройте браузером, чтобы проверить, как всё работает)

Как видите, в верхней правой ячейке таблицы мы поместили поле ввода (тэг input), из которого при изменении содержимого (на самом деле, при отпускании кнопок клавиатуры, так как используется обработчик onkeyup) вызывается наша новая функция writePlural(). Эта функция устроена очень просто — в нижнюю правую ячейку она записывает результат работы нашей функции getPlural(). Единственная тонкость — мы добавили перевод всех символов в нижний регистр (вызываем метод toLowerCase()), так как функция getPlural() пока не готова только к словам, записанным заглавными буквами.

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

На этом мы и остановились. Решили, что в следующий раз надо будет сделать следующее:
1) поддержать механизм слов-исключений в функции getPlural() («woman» — «women»),
2) добавить остальные правила построения множественного числа по единственному,
3) расширить тестирующий код для поддержки нескольких правильных ответов («volcanoes», и «volcanos»),
4) решить, как лучше всего использовать результат функции, определяющей, является ли существительное неисчисляемым (выводить подсказку/предупреждение?).

А как бы вы решали эту же задачу со школьниками?

Хорошего дня!

23 окт. 2011 г.

Бессистемность

...Мы называемся школой танцев.
Но мы не учимся, мы танцуем...
(c) Михаил Щербаков

Добрый день.

1. Предисловие.

Одни люди настаивают на последовательном и системном обучении, критикуя любителей «салата» из разнородных навыков, а другие справедливо возражают, что от некоторых строгих учебников и уснуть можно, а спящий человек медленно учится. Кто же из них прав? Тут всё зависит от обучаемых.

Например, как первоклассников учат английскому языку? Сначала с ними разучивают песенки-стишки, придумывают простейшие игры, разыгрывают весёлые сценки-диалоги. Конечно, параллельно стараются учить алфавит и так далее (тоже в форме песенок). Но нормальные учителя не стремятся с первых же занятий отбить желание учиться, поэтому не грузят грамматикой. Математики тоже не пугают детей уравнениями третьей степени. Да и аксиомы Пеано в первом классе не дают, а ведут беседы о грушах и яблоках у Пети и Маши. А можно ли обучить ребёнка программированию, заперев его в комнате с книгами, например, Кнута? Конечно нет, поэтому сначала ребёнку дают что-то вроде Light Bot, а потом помогают самому написать что-то простое и интересное. И это правильно.

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

2. Задача.

Можно было назвать эту заметку «Множественное число в английском языке и javascript». Или вот другое подходящее длинное название: «Регулярные выражения, js и английский язык в одном флаконе».

Но зачем смешивать? Да потому что детям скучно и тяжело изучать что-то сложное. А вот если это сложное хорошо переплетается с интересным, то дело идёт куда веселее. Например, некоторым детям довольно противно учить географию просто так. А вот одновременно история с географией идут куда лучше. А бывают совершенно ортогональные темы, которые всё же удаётся удачно увязать.

Итак, в нынешней серии заметок мы вспомним/узнаем следующие простейшие вещи:
1) правила формирования множественного числа существительных в английском языке,
2) создание простейшей таблички и формы в HTML,
3) элементарную работу со строками в Javascript,
4) подход к тестированию программного обеспечения.

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

3. К делу.

И вот пришёл к нам этот пятиклассник, мечтающий научиться программировать. Голова у него есть, поэтому берёмся за это увлекательное дело. Естественно, можно было порекомендовать ему прочитать какую-нибудь книжку (того же Вирта «Алгоритмы и структуры данных»). Но если ребёнок сам искренне хочет учиться, то надо его поддержать, а не «посылать». Книжку, конечно, ему тоже надо будет прочитать, но пусть сперва увидит первые результаты, пусть почувствует, что у него получается.

— Что будем программировать?
— Не знаю.
— Что-нибудь уже писал на каком-нибудь языке?
— Только свою страничку в интернете на html. Но это же не язык программирования?
— Верно. Но хоть что-то. С Javascript что-то пробовал?
— Да, поставил код счётчиков и гостевой книги на свою страничку.
— Отлично. Давай выберем то, что было бы интересно сделать! Захватывающую игру написать будет трудно, поэтому предлагаю автоматизировать что-нибудь, что пока плохо получается делать головой. Есть такое на примете?
— Да, мы сейчас на английском прошли множественное число, а там столько правил и исключений! Как бы это всё понять?(да, конечно это было после 5 минут расспросов)

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

— Умеешь сделать простую табличку два на два?
— Запросто! Вот так:

<table>
  
<tr><td>left-top</td><td>right-top</td></tr>
  
<tr><td>left-bottom</td><td>right-bottom</td></tr>
</table>

— Отлично! Теперь давай учиться делать функции. Потом мы реализуем серьёзную функцию, которая будет преобразовывать существительные из единственного числа во множественное, но пока нам надо создать простую функцию-заглушку. Это нам нужно, чтобы научиться её тестировать.
— А как?
— Создай файл plural.html со следующим кодом, а потом открой его браузером

<script type="text/javascript">
function getPlural(iSingular) {
  
return iSingular;
}
document.write(
'Test: ' + getPlural('test'));
</script>

— Вижу только строчку «Test: test».
— Да, это последняя строка твоей программы вывела склейку двух строк: «Test: » и результат работы функции getPlural(), получившей на вход строку «test» (а сейчас эта функция возвращает именно то, что получает в качестве аргумента).
— Ага, примерно понятно. А как её тестировать?
— А давай найдём в интернете какой-нибудь тест на знание правил образования множественного числа. Возьмём из этого теста вопросы и ответы — вот и будет тестовая база для нашей программы.

Уже через 15 минут у нас была табличка из четырёх десятков тестовых пар «fork-forks», «book-books» и так далее. Я объяснил, что такое массивы, как можно их описывать в Javascript, поэтому довольно скоро наш файл plural.html расширился функцией, проверяющей, совпадает ли ответ из теста с ответом нашей функции getPlural (а совпадать почти никогда не должно, потому что функция ещё ничего не делает). Конечно, пришлось многое показать и рассказать, но школьник попался толковый, поэтому вникал быстро. Скоро наша программа приобрела следующий вид:

<script type="text/javascript">
function getPlural(iSingular) {
  
return iSingular;
}

function testPlural() {
  
var aSingular = new Array("fork", "watermelon", "dress", "bridge", "book", "garage", "store", "pencil", "horse", "bay", "turkey", "Japanese", "coin", "staple", "fish", "key", "pepper", "wolf", "kitten", "foot", "sky", "oasis", "sit-in", "copy", "commando", "spy", "forget-me-not", "cloud", "watch", "factory", "man", "foot", "mouse", "woman", "tooth", "louse", "child", "ox", "goose");
  
var aPlural = new Array("forks", "watermelons", "dresses", "bridges", "books", "garages", "stores", "pencils", "horses", "bays", "turkeys", "Japanese", "coins", "staples", "fish", "keys", "peppers", "wolves", "kittens", "feet", "skies", "oases", "sit-ins", "copies", "commandos", "spies", "forget-me-nots", "clouds", "watches", "factories", "men", "feet", "mice", "women", "teeth", "lice", "children", "oxen", "geese");
  
var nbErr = 0;
  
for (var i = 0; i < aSingular.length; i++) {
    
if (aPlural[i] != getPlural(aSingular[i])) {
      nbErr
= nbErr + 1;
      document.write(
'For the noun ' + aSingular[i] + ' the plural form must be ' + aPlural[i] + ', but getPlural() returned ' + getPlural(aSingular[i]) + '<br>');
    }
  }
  document.write(
'<b>Number of errors: ' + nbErr + ', number of tests: ' + aSingular.length + '</b>');
}

testPlural();
</script>

Как видите, большая часть этой программы — это таблица существительных (единственное число хранится в массиве aSingular, а множественное — в массиве aPlural). Дальше в цикле for проводится проверка для каждого элемента массива aSingular, совпадает ли ответ нашей функции getPlural() с эталонным ответом, хранящимся в массиве aPlural. Если не совпадает, то программа на единичку увеличивает счётчик ошибок nbErr.

Нынешняя реализация функции getPlural() ничего толкового не делает (не меняет входную строку), поэтому все её ответы должны были быть неправильными. Но радостная новость состоит в том, что даже эта функция-заглушка успешно прошла два теста из тридцати девяти (скопируйте текст этой «программы» в файл plural.html, а потом просто откройте его браузером, чтобы убедиться в этом).

— Отлично, программа работает. Но 37 ошибок из 39 — это очень много. Давай остановимся на более весёлой ноте. Как обычно образуется множественное число в английском языке?
— Добавляется «s».
— Верно! Давай научим нашу функцию хотя бы этому. Ты уже умеешь склеивать строки, поэтому всё можешь сам.
— Да, сейчас попробую. Там же всего одну строчку надо поправить

function getPlural(iSingular) {
  
return iSingular + 's';
}

Ух ты, всего 20 ошибок!
— Вот видишь, мы сразу смогли увидеть, как поменялись результаты работы нашей программы после минимальной правки. Это одно из преимуществ данного подхода (сначала создаём тесты, а потом программу). В тестировании ничего не надо делать вручную, если это можно автоматизировать. А если что-то автоматически сделать нельзя, то надо подумать, а потом всё равно автоматизировать :)

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

Ну а я скоро опубликую продолжение этой истории.

Хорошего дня!

14 окт. 2011 г.

Маляры и котлеты

- Илья, где ты пропадаешь две недели?
- В глубоком-глубоком роуминге :)

Но я вернулся, почти всем ответил на почту и комментарии, а теперь, извините, поделюсь неприятным впечатлением от Питерского Пулково-1. Несколько лет я не был в этом дивном месте, поэтому успел подзабыть его бардак и грязь. Если бы номер гейта на билете соответствовал реальному номеру двери, от которой автобус повезёт к самолёту, если бы табло над гейтом правильно указывало номер рейса, то сотрудницы аэропорта не были бы вынуждены кричать: «Рейс ###, подходите сюда, а рейс ### отойдите, сейчас мы не вас сажаем. Да, мы знаем, что на табло сейчас указан ваш город, поэтому не читайте табло». Зачем я это пишу? Пару лет назад помогло же :) А пока, если меня спросят европейцы, через какую из столиц лучше влетать в Россию, то я однозначно назову Москву. Как минимум, в Москве работают лифты, хорошо светят лампочки и моют туалеты, а в Пулково-1 в тёмных коридорах неподвижно стоят грязные эскалаторы, о которых сразу забываешь, посетив уборную. Злые языки утверждают, что ремонта там не было со времён блокады Ленинграда.

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

1. Много лет назад у нас была задачка о хрупких шариках: Есть стакан с очень дорогими одинаковыми хрупкими шариками из очень прочного материала. И есть лестница с сотней ступенек. Какой минимум шариков придётся разбить, чтобы выяснить предельную высоту (число от 1 до 100 — бросаем со ступенек), с которой можно ронять шарики целыми (т.е. чтобы они не разбились)?

2. Старинная детская задачка о готовке звучит так: На одной сковороде может поместиться не более 2 котлет. Одна сторона котлеты жарится 1 минуту. За какое минимальное время можно пожарить три котлеты с 2-х строн.

3а. Два маляра столкнулись на дороге с другими двумя малярами. Этикет обязывает каждого из первой пары поздороваться с каждым из второй пары, но руки у каждого маляра испачканы в краске своего цвета. А так как маляры не хотят пачкать руки в краске другого цвета, то у них есть несколько чистых перчаток. Какое минимальное количество перчаток они будут вынуждены испачкать, чтобы поприветствовать друг друга?

3б. Три маляра столкнулись на дороге с одним маляром. Соответственно, каждый из них должен с ним поздороваться. Остальное как в задаче (3а): их руки испачканы четырьмя цветами, есть несколько чистых перчаток. Сколько перчаток им придётся испачкать?

(последние две задачки своевременно напомнил Антон, а я чуть-чуть поменял формулировки)

Пожалуйста, напишите в комментариях статус по этим задачкам примерно в таком виде:
   1. Помню, ответ x,
   2. Давно знаю, ответ y,
   3. а) тоже слышал, ответ z1, б) первый раз слышу, ответ z2.


Хороших выходных!

29 сент. 2011 г.

Вспоминаем тригонометрию

Почти все задачи, которые мы решаем в течение жизни, не являются настоящими. В каком смысле? Сейчас поясню.

Редкому человеку даётся шанс ответить на новый вопрос, от которого зависит дальнейшее развитие, например, человечества. Во-первых, необходимо справиться с огромным количеством учебных задач, чтобы подняться на уровень настоящих проблем. Это только в американских фильмах главный герой может «усилием воли» справиться с проблемой, о которой раньше никогда не думал. В реальной же жизни всё куда скучнее — необходимо очень долго и целенаправленно работать, чтобы продвинуться в изучении имеющих смысл, но ранее не исследованных постановок. Во-вторых должно очень повезти с задачей (окружающая среда должна натолкнуть на подходящую проблему), так как ответы далеко не на все вопросы а) возможны, б) нужны.

Вот какой, например, смысл, имеет задачка про получение натуральных чисел из трёх двоек? Да никакой пользы для народного хозяйства от такой задачки быть не может! А зачем она тогда нужна? Естественно, исключительно в учебных целях:
- сначала она работает как автобусные билетики (развитие быстрого счёта, памяти и так далее),
- потом кроме «тупой перебиралки» может подключиться мозг — вспомнив функции одного аргумента, найдёт способ получать произвольное количество натуральных чисел,
- далее можно уже решить исследовательскую задачу — попробовать получить все возможные натуральные числа (а потом можно попробовать ещё усилить задачу, что мы и сделаем в последнем абзаце этой заметки).

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

Если же в этот момент изучают тригонометрию, то тоже есть куда развернуться. В хороших школах учитель добивается не банального решения ограниченного набора типовых задачек, а понимания темы. Например, осваивая тригонометрию, не надо заучивать правила sin(-x)=-sin(x), не надо запоминать cos(0)=1 и так далее. А надо всего лишь разобраться с соответствием углов точкам единичной окружности, а тригонометрических функций точкам нескольких прямых. Если с этим разобраться, то все те «сложные для запоминания правила» станут простыми и естественными (и как будто сами «запомнятся»).

Но давайте на секундочку отойдём от игр с единичной окружностью, есть же ещё прямоугольный треугольник. В самом деле, слово тригонометрия образовано от двух греческих слов trigōnon «треугольник» + metron «измерять». В хороших школах кроме игр с единичной окружностью ещё обязательно играют с прямоугольным треугольником. Полезным бывает анализ функций arcsin(sin(x)), cos(arccos(x)), ctg(arctg(x)), cos(arctg(x)) и так далее. А для него стоит вспомнить определения тригонометрических функций острых углов: синусом/косинусом угла a называется отношение противолежащего/прилежащего катета к гипотенузе (в любом прямоугольном треугольнике, один из углов которого равен a).

Прямоугольный треугольникКак, например, понять, как устроена функция sin(arctg(x))? Достаточно знать всего две вещи: теорему Пифагора и определение тригонометрических функций через отношение сторон в прямоугольном треугольнике. Давайте рассмотрим треугольник со сторонами 1, sqrt(n), sqrt(n+1). Синус верхнего угла в нём равен 1/sqrt(n+1), а тангенс того же угла равен 1/sqrt(n). Соответственно, sin(arctg(1/sqrt(n))=1/sqrt(n+1). Получается, что этой парой функций можно наезжать на 1/sqrt(n) сколько угодно раз, как бы увеличивая n под корнем — sin(arctg(sin(arctg(1/sqrt(n))))=1/sqrt(n+2) и т.д.

Что нам это даёт? Для задачки с тремя двойками нам как раз нужно было научиться увеличивать натуральное число на единицу произвольное число раз. Тут мы получили не совсем тот результат, но у нас ещё и не все двойки потрачены :)

В качестве n мы можем взять 2/2, тогда у нас останется одна двойка, чтобы получить x из 1/sqrt(x).

Так как 0 и 1 мы получать умеем ((2-2)*2 и (2/2)^2, например), то давайте разберёмся со всеми остальными натуральными числами:
2 = sin(arctg(2/2))^-2,
3 = sin(arctg(sin(arctg(2/2))))^-2,
4 = sin(arctg(sin(arctg(sin(arctg(2/2))))))^-2,
и так далее.

Опять же, этот результат совершенно не имеет значения для народного хозяйства. Но при освоении тригонометрии научиться оперировать такими «крокодилами» очень даже полезно. Хотите ещё полезнее? Тогда давайте откажемся от двух двоек — оставим только одну. Итак, задачка Московской математической олимпиады 2010 года за 10 класс: Можно ли, применяя к числу 2 функции sin, cos, tg, ctg, arcsin, arccos, arctg, arcctg в любом количестве и в любом порядке, получить число 2010? (спасибо автору, вовремя показавшему ссылку на эту задачу). На странице 28 документа с решениями приведён разбор. Как обычно, я рекомендую открывать его только после самостоятельного решения задачи.

Хорошего дня!

Понравилась заметка? Подпишитесь на RSS-feed или email-рассылку.

Хотите поделиться ссылкой с другими? Добавьте в закладки:



Есть вопросы или предложения? Пишите письма на адрес mytribune АТ yandex.ru.

С уважением,
      Илья Весенний