суббота, 14 июня 2014 г.

[Перевод] G.Weinberg - 1.1 Чтение программ


Когда-то, во времена популярности таких языков как COBOL, очень активно обсуждался вопрос о возможности менеджеров читать программы своих программистов. Как выяснилось позже, все эти действия были направлены просто на привлечение менеджерами дополнительных средств: они просто не хотели зависеть от своих программистов. О реальной возможности чтения программ речи не шло. Ведь, действительно, зачем менеджерам читать программы? Даже сами программисты их не читают.
Но разве есть реальная необходимость читать программы? Разве программы пишутся не для ЭВМ? И да, и нет. Даже закрывая глаза на то, что программы нужно модифицировать, создавать им интерфейс для общения друг с другом и т.п., идея читать программный код не такая уж и плохая с точки зрения обучения программированию.
Программирование, среди всего прочего, это своего рода письменность. Чтобы освоить письменность, нужно, в первую очередь, писать. Но, что касается других форм письменности, чтение также способствует освоению этого навыка. А может ли программист научиться писать программу, просто почитывая программный код? Теоретически — да, практически — никогда. Что же полезного тогда можно извлечь из чтения программ? Давайте рассмотрим маленький пример на языке PL/1 (см. Пример 1).

Пример 1.
XXX: PROCEDURE OPTIONS(MAIN);
     DECLARE B(1000) FIXED (7,2),
             C FIXED (11,2),
             I,J FIXED BINARY;
     C = 0;
     DO I = 1 TO 10;
         GET LIST((B(J) DO J = 1 TO 1000));
         DO J = 1 TO 1000
             C = C + B(J);
             END;
         END;
     PUT LIST('SUM IS', C);
     END XXX;

Что полезного можно извлечь из чтения этого куска кода? Поскольку программный код — это не любимый роман, в котором понравившиеся моменты отмечены замятым уголком книги, нам нужны какие-то концептуальные основы, которые объясняли бы происхождение каждой строчки кода. Иными словами, когда мы смотрим на каждый фрагмент кода, мы задаем себе вопрос: «Почему этот фрагмент здесь появился?»

Машинные ограничения
Одна из причин появления какого-либо фрагмента кода — это ограничения ЭВМ, на которой решается данная конкретная задача в сравнении с некоторой идеальной ЭВМ. В предложенном примере видно, что в общей сложности была получена сумма десяти тысяч чисел, но прочитаны они были в циклах по тысяче. Поскольку числа, очевидно, перфорированы1 в формате списков PL/1, то единственная причина такого решения — невозможность сохранить все 10 000 чисел одновременно. Другими словами, не имея 40 000 байтов в наличии, программист должен разбить свою процедуру суммирования на маленькие "секции", что привело к появлению дополнительного цикла. Если бы ЭВМ была в состоянии хранить все 10 000 чисел, то, вероятно, код программы выглядел бы так, как показано в примере 2.

Пример 2.
XXX: PROCEDURE OPTIONS(MAIN);
     DECLARE A(10000) FIXED (7,2),
             C FIXED (11,2),
             J FIXED BINARY;
     C = 0;
     GET LIST((A(J) DO J = 1 TO 10000));
     DO J = 1 TO 1000
         C = C + A(J);
         END;
     PUT LIST('SUM IS', C);
     END XXX;

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

Ограничения языка
Однако, машинные ограничения нас мало интересуют, ведь мы в вопросах психологии ищем более "человечные" причины написания того или иного кода. Шагом навстречу им будет рассмотрения роли языковых ограничений в приведенном нами Примере 2.
Вообще говоря, язык PL/I предоставляет программисту встроенную функцию под название SUM для вычисления суммы элементов множества. Изначально функция SUM была больше «математической» функцией, чем «арифметической»: она принимала входящие значения в формате с плавающей запятой или преобразовывала их к такому формату при необходимости. Однако, такое преобразование могло привести к потери точности, если бы массив состоял из десятичных дробей с фиксированной запятой (FIXED DECIMAL). Многие программисты было обеспокоены обнаружением потери пени, при попытке использования функции SUM для расчёта бухгалтерских балансов.
Возникшая ситуация привела к тому, что функцию SUM объявили как непригодную для точных расчётов, т.е. как чисто арифметическую. Если бы не это языковое ограничение,то программа из нашего примера могла бы выглядеть лаконичнее, как показано в Примере 3.

Пример 3.
XXX: PROCEDURE;
     DECLARE A(10000) FIXED (7,2),
             J FIXED BINARY;
     GET LIST((A(J) DO J = 1 TO 10000));
     PUT LIST('SUM IS', SUM(A));
     END XXX;

В примере 3, мы также убрали OPTIONS(MAIN) при объявлении PROCEDURE, иллюстрируя другую форму языкового ограничения - ограничение конкретной реализации. Этот неуклюжий атрибут требовался только в определенных операционных системах, интерфейс которых требовал, чтобы головные MAIN-программы обрабатывались иначе, чем подпрограммы.

Ограничения программиста
Чисто психологическими являются ограничения самого программиста, который по каким-то причинам недостаточно хорошо владеет языком, компьютером или самим собой. В нашем примере, программист действительно не понимал обозначения массива в PL/I - до такой степени, что даже не осознавал возможности поместить имя массива в список данных, чтобы получить все его элементы. Если бы он был знаком с этой особенностью языка (которая была, в конце концов, даже в FORTRAN) он, возможно, написал бы программу, показанную в Примере 4.

Пример 4.
XXX: PROCEDURE;
     DECLARE A(10000) FIXED (7,2);
     GET LIST(A);
     PUT LIST('SUM IS', SUM(A));
     END XXX;

Незнания полной мощности используемого языка — это так называемые лексические ограничения.

Историческое наследие (legacy-код)
Очень часто в программах можно обнаружить код, классифицируемый по любому из вышеуказанных ограничений, но доставшийся программе "в наследство". Например, как только функция SUM превратилась в арифметическую функцию, то не осталось ни одной причины использовать её для точных расчётов, как в Примере 2. Тем не менее, если эта программа уже находится в состоянии промышленной эксплуатации, то маловероятно,что кто-нибудь начнет в ней ковыряться только из-за того, что изменилось определение функции SUM.

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

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

Требования
Рассматривая Примеры 1 и 4, можно подумать, что решением самой задачи занимается довольно малая часть всего написанного кода. Довольно справедливое замечание для приведенных примеров, однако, не исключающее наличие программ, занимающихся конкретно только решением задачи.
И всё же, даже успешно извлекая полезное "ядро" программы из приведённых примеров, не стоит впадать в иллюзию, что при разработке всё так очевидно и просто: сначала решается задача, а якобы потом достраивается код, преодолевающий всевозможные ограничения. Кроме очевидных трудностей в определении намерений программиста, таких как незнание языка или написание "преждевременно оптимизированного" кода, всегда будет оставаться фактом то, что в большинстве случаев мы не знаем, что мы получим, пока не "сядем в лужу", начав это программировать.
Требования развиваются вместе с программами и программистами. Написание программы является процессом обучения и для программиста и для заказчика. Кроме того, этот процесс обучения имеет место в контексте определенной машины, определенного языка программирования, определенного программиста или программной команды в особых производственных условия, и определенном наборе исторических событий, которые определяют не только форму кода, но также и то, что делает код. В некотором смысле, самая важная причина изучения процесса программирования заключается не в том, чтобы сделать программы более эффективными, компактными, дешевыми или более понятными. Самая важная выгода - это перспектива получения от наших программ того, что мы действительно от них хотим, а не того, что нам удастся выжать из них всеми силами.

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



1 - речь идёт о программах на перфокартах.
Читать дальше......

понедельник, 9 июня 2014 г.

[Перевод] G.Weinberg - The Psychology Of Computer Programming

Компьютерное программирование как самостоятельная практически значимая деятельность существует уже около 70 лет (если не брать в расчёт госпожу Аду) — для человеческого ремесла срок небольшой. Однако, за всё это время огромное количество книг было написано по технологиям, парадигмам, методологиям, принципам и паттернам программирования. Но тема психологии такой сложной деятельности как программирование всё время как-то обходится стороной, а ведь все программисты, проектировщики и разработчики — это, в первую очередь, люди. Зачастую принимаемые ими решения — это результат не какого-то строгого машинного сравнения "что лучше", а тех или иных убеждений, попыток найти простое решение сложной проблемы, попыток обойти имеющиеся ограничения в задаче и т.п. Действительно, из знаменитых и читаемых книг по психологии вспоминается лишь "Peopleware: Productive Projects and Teams" Тома Де Марко (в русских изданиях — "Человеческий фактор: успешные проекты и команды") и частично "Facts & Fallacies Of Software Engineering" Роберта Гласса (рус. — "Факты и заблуждения профессионального программирования"). Как-то скудновато, по сравнению с тоннами всяких самоучителей по PHP и "как создать сайт с нуля за 4 часа" для "чайников".
Однако, есть одна тематическая книга, которая до сих пор является бестселлером на западе, несмотря на свой солидный возраст (первое издание отпечатано в 1971 году). Она выдержала несколько переизданий, однако в новых изданиях, если верить этому источнику, не изменилось ни единой буквы. Такой историей может похвастаться только стóящая глыба в IT-литературе.


Почему бестселлер почти неизвестен в России? Ответ прост — за тридцать с лишним лет не было ни единого русского издания этой книги. Откуда я узнал об этой книге? Мне её порекомендовал к прочтению один опытный программист, живущий в англоговорящей стране. К слову, в узких кругах всё же книга в стране известна, но по моему скромному мнению узнать о ней нужно куда большему числу людей. Просто потому что она, в первую очередь,о том, почему программы во всём мире пишутся именно так, как пишутся. А пишутся они зачастую так, что человек со стороны, читающий код программы, ломает над ней голову. А причина этого чаще всего кроется в темных уголках психологии программиста. В эти самые углы автор — образованный человек с ярко выраженным системным мышлением, и пытается проникнуть, чтобы узнать суть и причину действий, а также пытается их объяснить.
К слову, я принял решение публиковать краткий конспект каждой главы этой книги — буквально основополагающие тезисы. Книга любопытная, у меня есть большое желание поделиться её переводом здесь и где-нибудь на Хабре, но, как говорится, авторское право — non penis canina. Поэтому, чтобы не нарушать законодательства, я постараюсь опубликовать самую суть. Итак, поехали. Содержание книги в переводе выглядит так:

  • I. Программирование как человеческая деятельность
  • II. Программирование как социальная деятельность
    • 2.1 Группа программистов
    • 2.2 Команда программистов
    • 2.3 Проект
  • III. Программирование как индивидуальная деятельность
    • 3.1 Отклонения в задачах программирования
    • 3.2 Личностные факторы
    • 3.3 Способность рассуждать разумно, или способность к решению проблем
    • 3.4 Мотивация, обучение и опыт
  • IV. Инструменты для программиста
    • 4.1 Языки программирования
    • 4.2 Некоторые принципы проектирования языков
    • 4.3 Другие инструменты

По мере обновления материала в содержании будут появляться ссылки. Буду рад вашему мнению и комментариям.


Сайт автора: www.geraldmweinberg.com/
Твиттер автора: @JerryWeinberg
Читать дальше......

четверг, 5 июня 2014 г.

[Перевод] Худшая диаграмма в мире: почему вам следует отказаться от использования круговых диаграмм

Предлагаю вашему вниманию свой вольный перевод одной интересной статьи Уолтера Хики с "Бизнесинсайдера" (Walter Hickey) по поводу использования круговых диаграмм. Есть много интересных моментов, к которым следует прислушаться.

Круговая диаграмма — это, пожалуй, самый худший в мире способ представления информации за всю историю визуализации данных.
Конечно, существуют и другие куда более неуклюжие способы визуализации, но ни один из них не имеет такой популярности и широкого распространения, как круговая диаграмма.
Ниже, я объясню вам почему нет ничего хуже круговых диаграмм и почему вам следует как можно быстрее отказаться от их использования. Для начала давайте подумаем, почему мы вообще решаем использовать диаграммы? Итак:
  • Диаграммы — это способ наглядно отобразить информацию;
  • В общем случае, диаграммы призваны облегчить сравнение различных множеств данных;
  • Чем больше информации диаграмма способна передать без повышения своей сложности, тем лучше;
Однако теперь я покажу вам, почему круговые диаграммы проваливаются практически по всем перечисленным параметрам и уступают другим видам диаграмм.
Суть круговой диаграммы — отобразить соотношение частей чего-то целого между собой. Давайте взглянем, как плохо справляются круговые диаграммы с задачей, для которой они, в общем-то, предназначены. Смотрим на рисунок ниже.


Представим, что они отображают ход голосования с пятью кандидатами в три различных временных среза: время А, время В и время С. Итак, какую же полезную информацию мы можем извлечь из этих диаграмм? Так как это доли голосов за каждого кандидата, читателю должно быть хорошо и наглядно видно, каков результат выборной гонки. Однако, читатель здесь этого не видит.
Набрал ли кандидат № 5 больше голосов, чем кандидат №3 в момент времени А? Кто набрал больше голосов между моментами времени А и В: кандидат № 2 или кандидат № 4? Кто стремительнее всего набирает голоса?
Если цель диаграммы сделать информацию нагляднее и проще для восприятия, то справилась ли с этой задачей круговая диаграмма?
Действительно, не было ли бы проще мне дать вам таблицу с результатами голосования, чтобы при беглом взгляде вы смогли узнать ответы на интересующие вас вопросы, чем смотреть на эту круговую диаграмму?
А сейчас, давайте взглянем на ту же самую информацию — о частях целого — представленную в виде гистограммы. Посмотрите, насколько нагляднее и понятнее стала эта информация.


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


И главный наш вопрос : а способны ли мы в действительности сравнивать выделенные секторы круга между собой, замечать различия в размерах между ними? Если мы просто пытаемся узнать, что, да, доля EPP в парламенте больше , чем доля S&D, то на кой чёрт нам вообще эта диаграмма? Я смогу рассказать это всего двумя цифрами.
Но нет, диаграммы полезны только в том случае, когда нам нужно сравнить по размерам друг с другом каждый элемент в составе какого-то одного множества.


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


Заметьте, как вы сравниваете все части гистограммы друг с другом. На деле вы просто сравниваете длину прямоугольников и сразу понимаете, что вам пытаются донести! Если очень хочется, можете даже изменить левую ось на проценты, чтобы оценить распределение партий в парламенте. Однако, сейчас вы можете видеть, сколько мандатов имеет каждая партия — информация, которая не могла адекватно представляться в круговой диаграмме.
А теперь давайте взглянем, как легко можно манипулировать круговыми диаграммами. Вот те же данные, что и выше, но представленные в виде трёхмерной круговой диаграммы:


Люди постоянно, раз за разом рисуют такие объёмные круговые диаграммы, просто потому, что это отличный способ наврать вам с три короба.
Глядя на эту диаграмму кажется, что доля партии S&D (красный сектор) примерно такая же, как доля партии EPP (ярко-голубой сектор). Но в реальности, объём сектора искажается из-за перспективы самой диаграммы, и красный сектор кажется больше, чем он есть на самом деле. Делается подобное очень просто, к величайшему стыду какого-нибудь Excel.
Ниже на рисунке вы видите ещё один недостаток круговых диаграмм. Факт состоит в том, что 10% мужчин, которые взглянут на этот рисунок вообще не поймут, на что мы тут жалуемся. (Название диаграммы — "Дальтонизм у мужчин" — прим.перев.)


Хорошие диаграммы вообще не требуют подписей к отображаемым данным. Нет необходимости использовать какие-то дополнительные цифры для своих данных, чтобы донести людям их суть. Если такая необходимость возникла — вы просто используете не те диаграммы. Так вот, в большинстве случаев круговые диаграммы — это НЕ те диаграммы, которые в действительности вам нужны!
Так что давайте подведем итог:
  • Всякие раз, когда есть сходства в отображаемой информации (цифры, проценты и т.д.), круговые диаграммы вам не нужны;
  • Всякий раз, когда имеется несколько (3 и более) различных частей одного целого, которые необходимо сравнивать — круговые диаграммы вам не нужны;
  • Круговые диаграммы очень легко исказить;
  • Если вам нужно подписывать числа или проценты, чтобы пояснить диаграмму — круговые диаграммы вам не нужны.
Прежде чем мы произведем контрольный выстрел критики для круговых диаграмм, неплохо бы упомянуть случай, в котором они действительно хороши.
Единственный случай, когда круговая диаграмма имеет право на жизнь — это когда вы сравниваете 2-3 части одного целого, которые сильно отличаются друг от друга по размерам (процентам,цифрам и т.п.). Всё.
И когда вы вдруг находите подходящий для этого случая пример, всё же единственный вариант использования такой диаграммы — наглядно показать людям, как выглядит конкретная доля чего-либо на общем фоне. Например, оправдание для круговой диаграммы — показать, как выглядит 32% от ста.
На прошлой неделе я написал в комментарии, что круговая диаграмма — это Nickelback* в визуализации данных. Коммент ушел в народ. Но после раздумий, я нашел более подходящую метафору. Круговые диаграммы — это Аквамен в визуализации данных.
Аквамен действительно хорош только в одной ситуации. Но при всем уважении, другие супергерои делают работу Аквамена даже лучше, чем сам Аквамен: Супермен может задерживать дыхание под водой, у Бэтмэна вообще своя подводная лодка. Если дошло до того, что нефтяной танкер терпит бедствие посреди океана, кого вы позовёте: Аквамена или Супермена?
Всем всегда было интересно, зачем вообще Аквамена зовут, когда нужен супергерой? Ведь единственный звездный час для Аквамена, или круговой диаграммы — если вдруг вам понадобится поговорить с рыбами или объяснить, как доля в 32% выглядит в стопроцентном круге. Вот и остаётся только удивляться, зачем люди не задумываясь начинают строить круговые диаграммы или зовут на помощь такого «узкого» специалиста, как Аквамен?
Короче, прекращайте использовать круговые диаграммы. Они бесполезны, с их помощью легко исказить информацию и они не справляются с задачей, которую должны решать диаграммы — они не делают информацию наглядной и легковоспринимаемой. Круговые диаграммы — это Аквамен.
Что же изображать взамен "старых добрых" круговых диаграмм?
Как сказал Эдвард Тафте — специалист по данным, который также описывал недостатки круговых диаграмм: «Люди, использующие круговые диаграммы, заслуживают тех же подозрений и скептицизма как и те, кто вечно путает its и it’s или their и there. Для сравнения данных используйте маленькие таблицы, выражения, но не круговые диаграммы».



*Nickelback — канадская пост-гранж группа, ставшая в последняя время объектом шуток по причине крайнего однообразия и показной лиричности своих песен.
Читать дальше......