Вообще так:
Есть переменна $html с кодом страницы, кодировка которой utf-8. Что-бы найти какой-либо текст (мне нужно найти ссылку) я использую регулярные выражения. Но что-бы их использовать нужно перекодировать utf-8 d windows-1251, так как регулярные выражения нормально не работают с utf-8. Обычно я делаю это функцией iconv. Но сталкнулся с проблемой - если есть какие-либо спецсимволы, код прекращает работу. Как это обойти? Или как с этим справиться?
p.s. я использую preg_*
1. lagun4ik - 05 Августа, 2011 - 10:18:17 - перейти к сообщению
2. Мелкий - 05 Августа, 2011 - 10:34:02 - перейти к сообщению
lagun4ik пишет:
так как регулярные выражения нормально не работают с utf-8
Используйте модификатор u
3. lagun4ik - 05 Августа, 2011 - 10:39:43 - перейти к сообщению
Мелкий пишет:
Используйте модификатор u
lagun4ik пишет:
так как регулярные выражения нормально не работают с utf-8
Используйте модификатор u
Пробовал, не помогает =(
4. illy - 05 Августа, 2011 - 10:44:25 - перейти к сообщению
скопируй сюда страницу, кодировка которой utf-8
и страницу с iconv+preg_*
посмотрим чё там
и страницу с iconv+preg_*
посмотрим чё там
5. lagun4ik - 05 Августа, 2011 - 10:55:11 - перейти к сообщению
Вот такая штука (тут стриптэг отработал)
То есть если присмотреться стопориться на "--¬"
Конечно можно было бы убрать какими либо методами эти символы, но код должен будет отрабатывать на многих сайтах, и если например он китайский, то опять будет затык на иероглифах. Когда уже сделают PHP6 с людской поддержкой utf8
Спойлер (Отобразить)
10 уродских приёмов программирования — Алик Кириллович Алик Кириллович Хрупкая красота программного кода: десять приёмов программирования, способных её разрушить 26 мая 2009 г. Здесь — полная авторская версия статьи, опубликованной в журнале «Хабрахабр» в сокращённом виде из-за ограничений на размер материала. Тема красоты кода для меня очень важна, поэтому буду благодарен всем, кто поставит ссылочку, и за любой другой «пиар» статьи. Если интересно — можете подписаться на RSS. Для меня программирование — это не только технология, но и, во многом — искусство. И, поэтому, большое значение имеет красота кода. Последние несколько лет я собирал приёмы программирования, разрушающие в программном коде его утончённую красоту: Объявление всех переменных в начале программы; Возврат результата функции через её параметр; Отсутствие локальных функций; Отсутствие else if; Использование параллельных массивов; Обязательное хранение размера массива в отдельной переменной; Доступ к свойствам объекта через obj.getProperty() и obj.setProperty(value); Использование рекурсии для вычисления факториалов и Чисел Фибоначчи; Отсутствие именованных параметров функции; Невозможность объявления объектов «на лету». Объявление всех переменных в начале программы В двух словах: Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы. Все программные системы иерархичны. Программы делятся на пакеты, пакеты — на классы, классы разбиваются на отдельные функции. Данные, относящиеся к тому или иному модулю программы, принято объявлять в начале этого модуля. Локальные переменные объявляются в начале функции; свойства, относящиеся ко всему классу, объявляются в начале определения класса и т.д. Однако функции не являются последним уровнем в иерархии программы. Любая нетривиальная функция состоит из блоков, реализующих отдельны шаги выполнения алгоритма. Тех самых блоков, которые никак не обособляются в коде, разве что отделяются друг от друга парочкой пустых строк. Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные «локальные» переменные! Которые объявляются в начале этого блока и используются только в его пределах. Пример: Предположим, нам надо написать функцию сортировки массива методом слияния. Алгоритм сортировки слиянием состоит из следующих шагов: Если длина массива = 2, то: просто меняем местами эти два элемента в случае неправильного их расположения. Если длина массива > 2, то: разбиваем массив на две половины: левый и правый подмассивы; отдельно сортируем каждый подмассив (рекурсивно); сливаем эти два отсортированных подмассива в один отсортированный результирующий массив. Блоки кода, реализующие шаги этого алгоритма, являются неотъемлемыми частями функции, и не имеют отдельного самостоятельного смысла (может быть, за исключением, блока слияния, который, в принципе, можно преобразовать в отдельную функцию). Однако, они являются полноправными элементами иерархии программы, и решают свои собственные маленькие подзадачи. Поэтому: В начале функции объявляются переменные, относящиеся ко всей функции, например: intLeftIndex, intRightIndex, intLength. А переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока, например: elTemp объявляется в начале блока сортировки массива из 2-х элментов; intCenterIndex — в начале блока разбиения; arSortedSubarray и intLeftElIndex — в начале блока слияния. /* Эта функция реализует сортировку слиянием */ function mergeSort (arArryay, intLeftIndex, intRightIndex) { /* Здесь объявляются переменные, относящиеся ко ВСЕЙ ФУНКЦИИ. Переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока. */ intLeftIndex = intLeftIndex || 0; intRightIndex = intRightIndex || arArryay.length-1; var intLength = intRightIndex - intLeftIndex + 1; if (intLength == 2) { /* БЛОК КОДА: сортировка массива из 2-х элементов */ if (arArryay [intRightIndex] < arArryay [intLeftIndex]) { var elTemp = arArryay [intRightIndex]; arArryay [intRightIndex] = arArryay [intLeftIndex]; arArryay [intLeftIndex] = elTemp; } } else if (intLength > 2) { /* БЛОК КОДА: разбиение массива на два подмассива */ var intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1; var intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1; var intRightSubarrayLength = intRightIndex - intCenterIndex; /* БЛОК КОДА: рекурсивная сортировка каждого подмассива */ mergeSort (arArryay, intLeftIndex, intCenterIndex); mergeSort (arArryay, intCenterIndex+1, intRightIndex); /* БЛОК КОДА: слияние */ var arSortedSubarray = []; var intLeftElIndex = intLeftIndex; var intRihtElIndex = intCenterIndex+1; while (intLeftElIndex <= intCenterIndex || intRihtElIndex <= intRightIndex) { if (intRihtElIndex <= intRightIndex && (arArryay [intRihtElIndex] <= arArryay [intLeftElIndex] || intLeftElIndex > intCenterIndex)) { arSortedSubarray [arSortedSubarray.length] = arArryay [intRihtElIndex]; intRihtElIndex++; } if (intLeftElIndex <= intCenterIndex && (arArryay [intLeftElIndex] <= arArryay [intRihtElIndex] || intRihtElIndex > intRightIndex)) { arSortedSubarray [arSortedSubarray.length] = arArryay [intLeftElIndex]; intLeftElIndex++; } } for (var i = 0; i < intLength; i++) { arArryay [intLeftIndex + i] = arSortedSubarray [i]; } } } И поэтому: Объявление всех переменных в начале функции — страшное зло [1]. Это приводит к смешению переменных, относящихся ко всей функции, с переменными, относящимися только к её отдельному блоку. Это разрывает блок на две части: объявления данных (в начале функции) и использования этих данных (в самом блоке). Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает. Пример: В случае объявления всех переменных в начале функции, переменные, относящиеся ко всей функции, перемешаются с переменными, имеющими смысл только внутри отдельных её блоков. function mergeSort (arArryay, intLeftIndex, intRightIndex) { /* Угадайте: какие из этих переменных относятся ко всей функции, а какие — к отдельным её блокам? */ var intLength, elTemp, intCenterIndex, intLeftSubarrayLength, intRightSubarrayLength, arSortedSubarray, intLeftElIndex, intRihtElIndex, i; intLeftIndex = intLeftIndex || 0; intRightIndex = intRightIndex || arArryay.length-1; intLength = intRightIndex - intLeftIndex + 1; if (intLength == 2) { if (arArryay [intRightIndex] < arArryay [intLeftIndex]) { elTemp = arArryay [intRightIndex]; arArryay [intRightIndex] = arArryay [intLeftIndex]; arArryay [intLeftIndex] = elTemp; } } else if (intLength > 2) { intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1; intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1; intRightSubarrayLength = intRightIndex - intCenterIndex; mergeSort (arArryay, intLeftIndex, intCenterIndex); mergeSort (arArryay, intCenterIndex+1, intRightIndex); arSortedSubarray = []; intLeftElIndex = intLeftIndex; intRihtElIndex = intCenterIndex+1; //...код сокращён... } } Пример: Вы только представьте: У нас есть функция в 300 строк кода [2]. Где-нибудь на 200-й строке нам надо поменять две переменные местами. Для этого мы лезем на 200 сток выше в начало функции, объявляем переменную temp, которая не имеет никакого отношения ко всей функции, а используется только один раз в одном месте, потом опять возвращаемся к 200-й строке и меняем переменные местами… По-моему, это просто кошмар. Хуже всего, что существуют языки, которые считают себя умнее разработчика и заставляют объявлять все переменные в начале функции. Например, такой уважаемый язык как Pascal/Delphi. Чего я ему простить не могу… Возврат результата функции через её параметр В двух словах: Функция должна возвращать результат, зависящий от её параметров, а НЕ принимать результат в качестве аргумента. Понятие функции (как в математике, так и в программировании) имеет чёткий смысл: вычисление результата, зависящего от аргументов. В нормальном программном коде ясно видно, что является результатом, а что аргументами: результат = функция (аргумент1, аргумент2). Однако часто встречается приём, при котором возвращаемое значение передаётся в качестве аргумента функции: функция (аргумент1, аргумент2, &результат). Этот приём ужасен. При его использовании не видно, от чего функция зависит, а что возвращает. Чаще всего, в применении этого приёма виноваты не сами разработчики, а языки программирования. Существуют две основные причины, по которым языки заставляют нас прибегать к этому приёму. Первая причина состоит в том, что в некоторых языках функции не могут создавать и возвращать сложные объекты. Пример: Предположим, что мы хотим на C++ написать функцию, перемножающую две матрицы и возвращающую получившуюся матрицу в качестве результата. Матрицы мы решили представлять в виде двумерного массива. Но мы не можем объявить в функции результирующий двумерный массив, а затем вернуть его: int mtxResult [10][10] = mult (mtxA, mtxB); Поэтому нам придётся сначала вне функции объявить результирующий массив, а затем вызвать функцию перемножения, передав результат в качестве аргумента: mult (mtxA, mtxB, mtxResult); В данном случае от использования этого приёма можно избавиться, храня результат в том виде, который функция может возвратить. Пример: Можно хранить матрицу не в виде двумерного массива, а в виде структуры или объекта: Matrix mtxResult = mult (mtxA, mtxB); Код станет менее лаконичным (из-за объявления дополнительных структур), но, зато, гораздо более красивым. Вторая причина состоит в том, что в большинстве языков программирования функция не может возвращать несколько значений. Пример: Предположим, что мы хотим на C++ написать функцию, решающую квадратное уравнение. Функция принимает в качестве аргумента коэффициенты a, b, c и возвращает три результата: число корней, x1 и x2. Однако вернуть сразу три значения в C++ невозможно: intRootsCount, numX1, numX2 = quadraticEquation (numA, numb, numC) Поэтому нам придётся часть результатов выполнения функции передать через указатель в качестве аргументов: intRootsCount = quadraticEquation (numA, numB, numC, &numX1, &numX2); Здесь, опять же, от этого приёма можно избавиться, возвращая объект или структуру, хранящую результаты выполнения функции в виде полей. Пример: Можно возвращать результаты решения квадратного уравнения в виде структуры с тремя свойствами [3]: QuadrEqResult qerResult = quadraticEquation (numA, numB, numC); intRootsCount = qerResult.count; numX1 = qerResult.x1; numX2 = qerResult.x2; Однако и в первом, и во втором случае, при использовании структур или объектов, мы можем столкнуться с ещё одной проблемой: необходимостью отдельно описывать эти структуры или классы. Причём во многих языках, например на C++, мы не можем описать структуру или класс внутри самой функции (см. следующий раздел). Нам придётся описывать их отдельно от функции, делая их неподчиненными функции сущностями, что уродует иерархию программы. Слава богу, в других языках классы можно описывать прямо внутри функции, а, например в JavaScript можно просто возвратить объект, нигде отдельно не описывая его структуру. Пример: function quadraticEquation (numA, numb, numC) { //... return ({ count: intRootsCount, x1: intX1, x2: intX2 }); } var objResult = quadraticEquation (numA, numB, numC); intRootsCount = objResult.count; numX1 = objResult.x1; numX2 = objResult.x2; Вот это настоящая красота! Отсутствие локальных функций В двух словах: Локальная функция должна объявляться внутри функции, которой она логически подчиняется, а НЕ в глобальном контексте. Как уже говорилось, программные системы (как объектно-ориентированные, так и процедурные) иерархичны и делятся на вложенные друг в друга модули (впрочем, это очевидно). Каждый из модулей располагает своими локальными ресурсами, используемыми только в рамках этого модуля. Например, локальные переменные являются ресурсами модуля функции. Однако ресурсами функции являются не только переменные! Подфункции, классы, структуры и т.д. также являются полноправными ресурсами функции, подчинёнными ей, и используемыми только в её рамках. Пример: Предположим, нам надо написать функцию printDossier, печатающую досье. Сверху и снизу досье должен находиться его номер, выровненный по центру и обрамлённый декоративными свасти звёздочками: ****************** 666 ****************** Имя: Макс Отто фон Штирлиц Звание: штандартенфюрер Национальность: истинный ариец Характер: нордический, выдержанный ****************** 666 ****************** Чтобы не дублировать код, печатающий верхний, и печатающий нижний номер, мы решили вынести его в отдельную функцию printNumber. Функция, печатающая номер досье, является подчинённой по отношению к функции, печатающей всё досье, используется только в её рамках и не имеет самостоятельного смысла. Поэтому, функция printNumber должна быть локальной по отношению к printDossier, и объявляться в её теле. # #Эта функция печатает досье: # def printDossier (people): # #Эта локальная функция печатает номер досье: # def printNumber (): #Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру stringLength = 32 numberLength = len (people.number) starsCount = (stringLength - numberLength) / 2 #Печатаем звёздочки слева, номер досье, и звёздочки справа print ('*' * starsCount) + people.number + ('*' * starsCount) # #Печатаем досье # #Печатаем номер сверху printNumber () #Печатаем: имя, звание, национальность и характер print 'Имя: '+ people.name print 'Звание: '+ people.rank print 'Национальность: '+ people.race print 'Характер: '+ people.character #Печатаем номер снизу printNumber () Поэтому: Объявление функций, структур и т.д. вне функции, которой они иерархически подчиняются — очень плохой приём. Это уродует иерархию программы, делая подчинённую функцию независимой, и иерархически неподчиненной родительской функции сущностью. Это нарушает принцип сокрытия информации, вынося наружу детали внутренней реализации родительской функции — подчинённую функцию. Это затрудняет понимание кода, засоряя глобальный контекст лишними сущностями. Это приводит к возникновению ошибок, делая возможным случайный вызов подчинённой функции не в родительском, а в глобальном контексте. Но многие языки программирования, например C++, не поддерживают локальные функции, классы, структуры и т.д. Пример: Поскольку C++ не поддерживает локальные функции, функцию, печатающую номер досье, нам придётся сделать глобальной, независимой и иерархически неподчинающейся функции, печатающей всё досье: /* Эта функция печатает номер досье. Мы вынуждены сделать её глобальной, хотя она является деталью внутренней реализации функции printDossier и не имеет самостоятельного смысла. */ void printNumber () { //Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру int stringLength = 32; int numberLength = strlen (people.number); int starsCount = (stringLength - numberLength) / 2; //Печатаем звёздочки слева, номер досье, и звёздочки справа for (int i = 0; i < starsCount; i++) cout<<"*"; cout<<people.number; for (int i = 0; i < starsCount; i++) cout<<"*"; } /* Эта функция печатает досье */ void printDossier (People people) { /* Печатаем досье */ //Печатаем номер сверху printNumber (); //Печатаем: имя, звание, национальность и характер cout<< "Имя: " << people.name; cout<< "Звание: " << people.rank; cout<< "Национальность: " << people.race; cout<< "Характер: " << people.character; //Печатаем номер снизу printNumber (); } Язык Pascal/Delphi поддерживает локальные функции, но, заставляет их объявлять только в начале функции. Это не так страшно, как объявлять только в начале все переменные, но тоже, иногда, бывает достаточно некрасиво. Пример: Предположим, у нас есть функция из нескольких блоков кода, каждый из которых выполняет отдельный шаг всего алгоритма. Мы решили переписать один их блоков в рекурсивной форме. Для этого мы переделали его в рекурсивную локальную функцию… после чего вынуждены перетащить этот блок кода (ставший теперь локальной функцией) с того места, где он используется, в начало основной функции. program main (); function block3 (param: integer): integer; // (4)--¬ begin // ^ v Рекурсивный блок кода #3 // ^ v end; // ^ v {} // ^ v procedure block5 (param: integer); // ^ v (7)--¬ begin // ^ v ^ v Рекурсивный блок кода #5 // ^ v ^ v end; // ^ v ^ v {} // ^ v ^ v begin // ^ v ^ v блок кода #1 // (1) ^ v ^ v {} // v ^ v ^ v блок кода #2 // (2) ^ v ^ v {} // v ^ v ^ v {Прокручиваем на самый верх // v ^ v ^ v и находим код рекурсивной функции block3} // v ^ v ^ v block3 (param); // (3)--- v ^ v {} // v ^ v блок кода #4 // (5) ^ v {} // v ^ v {Прокручиваем на самый верх // v ^ v и находим код рекурсивной функции block5} // v ^ v block5 (param); // (6)--- v {} // v блок кода #6 // (8) end. FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Если это всё в iconv то на выходе такое:
[spoiler]<body screen_capture_injected="true">10 уродских приёмов программирования — Алик Кириллович <a href="http://www.alik.su"> Алик Кириллович </a> Хрупкая красота программного кода: десять приёмов программирования, способных её разрушить 26 мая 2009 г. Здесь — полная авторская версия статьи, опубликованной в журнале «Хабрахабр» в сокращённом виде из-за ограничений на размер материала. Тема красоты кода для меня очень важна, поэтому буду благодарен всем, кто поставит ссылочку, и за любой другой «пиар» статьи. Если интересно — можете <a href="http://feeds.feedburner.com/alik-kirillovich" class="aRSS">подписаться на RSS</a>. Для меня программирование — это не только технология, но и, во многом — искусство. И, поэтому, большое значение имеет красота кода. Последние несколько лет я собирал приёмы программирования, разрушающие в программном коде его утончённую красоту: <a href="#all-varibles-declaration-in-program-heading"> Объявление всех переменных в начале программы; </a> <a href="#func-result-via-argument"> Возврат результата функции через её параметр; </a> <a href="#absence-of-local-functions"> Отсутствие локальных функций; </a> <a href="#absence-of-elseif"> Отсутствие else if; </a> <a href="#parallel-arrays"> Использование параллельных массивов; </a> <a href="#array-length-in-separate-variable"> Обязательное хранение размера массива в отдельной переменной; </a> <a href="#getproperty-and-setproperty"> Доступ к свойствам объекта через obj.getProperty() и obj.setProperty(value); </a> <a href="#recursion-to-factorials"> Использование рекурсии для вычисления факториалов и Чисел Фибоначчи; </a> <a href="#absence-of-named-arguments"> Отсутствие именованных параметров функции; </a> <a> Невозможность объявления объектов «на лету». </a> Объявление всех переменных в начале программы <a>В двух словах:</a> Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы. Все программные системы иерархичны. Программы делятся на пакеты, пакеты — на классы, классы разбиваются на отдельные функции. Данные, относящиеся к тому или иному модулю программы, принято объявлять в начале этого модуля. Локальные переменные объявляются в начале функции; свойства, относящиеся ко всему классу, объявляются в начале определения класса и т.д. Однако функции не являются последним уровнем в иерархии программы. Любая нетривиальная функция состоит из блоков, реализующих отдельны шаги выполнения алгоритма. Тех самых блоков, которые никак не обособляются в коде, разве что отделяются друг от друга парочкой пустых строк. Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные «локальные» переменные! Которые объявляются в начале этого блока и используются только в его пределах. Пример: Предположим, нам надо написать функцию сортировки массива методом слияния. Алгоритм <a href="http://en.wikipedia.org/wiki/Merge_sort" title="Merge sort в Wikipedia">сортировки слиянием</a> состоит из следующих шагов: Если длина массива = 2, то: просто меняем местами эти два элемента в случае неправильного их расположения. Если длина массива > 2, то: разбиваем массив на две половины: левый и правый подмассивы; отдельно сортируем каждый подмассив (рекурсивно); сливаем эти два отсортированных подмассива в один отсортированный результирующий массив. Блоки кода, реализующие шаги этого алгоритма, являются неотъемлемыми частями функции, и не имеют отдельного самостоятельного смысла (может быть, за исключением, блока слияния, который, в принципе, можно преобразовать в отдельную функцию). Однако, они являются полноправными элементами иерархии программы, и решают свои собственные маленькие подзадачи. Поэтому: В начале функции объявляются переменные, относящиеся ко всей функции, например: intLeftIndex, intRightIndex, intLength. А переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока, например: elTemp объявляется в начале блока сортировки массива из 2-х элментов; intCenterIndex — в начале блока разбиения; arSortedSubarray и intLeftElIndex — в начале блока слияния. /* Эта функция реализует сортировку слиянием */ function mergeSort (arArryay, intLeftIndex, intRightIndex) { /* Здесь объявляются переменные, относящиеся ко ВСЕЙ ФУНКЦИИ. Переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока. */ intLeftIndex = intLeftIndex || 0; intRightIndex = intRightIndex || arArryay.length-1; var intLength = intRightIndex - intLeftIndex + 1; if (intLength == 2) { /* БЛОК КОДА: сортировка массива из 2-х элементов */ if (arArryay [intRightIndex] < arArryay [intLeftIndex]) { var elTemp = arArryay [intRightIndex]; arArryay [intRightIndex] = arArryay [intLeftIndex]; arArryay [intLeftIndex] = elTemp; } } else if (intLength > 2) { /* БЛОК КОДА: разбиение массива на два подмассива */ var intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1; var intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1; var intRightSubarrayLength = intRightIndex - intCenterIndex; /* БЛОК КОДА: рекурсивная сортировка каждого подмассива */ mergeSort (arArryay, intLeftIndex, intCenterIndex); mergeSort (arArryay, intCenterIndex+1, intRightIndex); /* БЛОК КОДА: слияние */ var arSortedSubarray = []; var intLeftElIndex = intLeftIndex; var intRihtElIndex = intCenterIndex+1; while (intLeftElIndex <= intCenterIndex || intRihtElIndex <= intRightIndex) { if (intRihtElIndex <= intRightIndex && (arArryay [intRihtElIndex] <= arArryay [intLeftElIndex] || intLeftElIndex > intCenterIndex)) { arSortedSubarray [arSortedSubarray.length] = arArryay [intRihtElIndex]; intRihtElIndex++; } if (intLeftElIndex <= intCenterIndex && (arArryay [intLeftElIndex] <= arArryay [intRihtElIndex] || intRihtElIndex > intRightIndex)) { arSortedSubarray [arSortedSubarray.length] = arArryay [intLeftElIndex]; intLeftElIndex++; } } for (var i = 0; i < intLength; i++) { arArryay [intLeftIndex + i] = arSortedSubarray [i]; } } } И поэтому: Объявление всех переменных в начале функции — страшное зло <a id="aCiteRef-1" href="#aCiteNote-1" class="aCiteRef">[1]</a>. Это приводит к смешению переменных, относящихся ко всей функции, с переменными, относящимися только к её отдельному блоку. Это разрывает блок на две части: объявления данных (в начале функции) и использования этих данных (в самом блоке). Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает. Пример: В случае объявления всех переменных в начале функции, переменные, относящиеся ко всей функции, перемешаются с переменными, имеющими смысл только внутри отдельных её блоков. function mergeSort (arArryay, intLeftIndex, intRightIndex) { /* Угадайте: какие из этих переменных относятся ко всей функции, а какие — к отдельным её блокам? */ var intLength, elTemp, intCenterIndex, intLeftSubarrayLength, intRightSubarrayLength, arSortedSubarray, intLeftElIndex, intRihtElIndex, i; intLeftIndex = intLeftIndex || 0; intRightIndex = intRightIndex || arArryay.length-1; intLength = intRightIndex - intLeftIndex + 1; if (intLength == 2) { if (arArryay [intRightIndex] < arArryay [intLeftIndex]) { elTemp = arArryay [intRightIndex]; arArryay [intRightIndex] = arArryay [intLeftIndex]; arArryay [intLeftIndex] = elTemp; } } else if (intLength > 2) { intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1; intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1; intRightSubarrayLength = intRightIndex - intCenterIndex; mergeSort (arArryay, intLeftIndex, intCenterIndex); mergeSort (arArryay, intCenterIndex+1, intRightIndex); arSortedSubarray = []; intLeftElIndex = intLeftIndex; intRihtElIndex = intCenterIndex+1; //...код сокращён... } } Пример: Вы только представьте: У нас есть функция в 300 строк кода <a id="aCiteRef-2" href="#aCiteNote-2" class="aCiteRef">[2]</a>. Где-нибудь на 200-й строке нам надо поменять две переменные местами. Для этого мы лезем на 200 сток выше в начало функции, объявляем переменную temp, которая не имеет никакого отношения ко всей функции, а используется только один раз в одном месте, потом опять возвращаемся к 200-й строке и меняем переменные местами… По-моему, это просто кошмар. Хуже всего, что существуют языки, которые считают себя умнее разработчика и заставляют объявлять все переменные в начале функции. Например, такой уважаемый язык как Pascal/Delphi. Чего я ему простить не могу… Возврат результата функции через её параметр <a>В двух словах:</a> Функция должна возвращать результат, зависящий от её параметров, а НЕ принимать результат в качестве аргумента. Понятие функции (как в математике, так и в программировании) имеет чёткий смысл: вычисление результата, зависящего от аргументов. В нормальном программном коде ясно видно, что является результатом, а что аргументами: результат = функция (аргумент1, аргумент2). Однако часто встречается приём, при котором возвращаемое значение передаётся в качестве аргумента функции: функция (аргумент1, аргумент2, &результат). Этот приём ужасен. При его использовании не видно, от чего функция зависит, а что возвращает. Чаще всего, в применении этого приёма виноваты не сами разработчики, а языки программирования. Существуют две основные причины, по которым языки заставляют нас прибегать к этому приёму. Первая причина состоит в том, что в некоторых языках функции не могут создавать и возвращать сложные объекты. Пример: Предположим, что мы хотим на C++ написать функцию, перемножающую две матрицы и возвращающую получившуюся матрицу в качестве результата. Матрицы мы решили представлять в виде двумерного массива. Но мы не можем объявить в функции результирующий двумерный массив, а затем вернуть его: int mtxResult [10][10] = mult (mtxA, mtxB); Поэтому нам придётся сначала вне функции объявить результирующий массив, а затем вызвать функцию перемножения, передав результат в качестве аргумента: mult (mtxA, mtxB, mtxResult); В данном случае от использования этого приёма можно избавиться, храня результат в том виде, который функция может возвратить. Пример: Можно хранить матрицу не в виде двумерного массива, а в виде структуры или объекта: Matrix mtxResult = mult (mtxA, mtxB); Код станет менее лаконичным (из-за объявления дополнительных структур), но, зато, гораздо более красивым. Вторая причина состоит в том, что в большинстве языков программирования функция не может возвращать несколько значений. Пример: Предположим, что мы хотим на C++ написать функцию, решающую квадратное уравнение. Функция принимает в качестве аргумента коэффициенты a, b, c и возвращает три результата: число корней, x1 и x2. Однако вернуть сразу три значения в C++ невозможно: intRootsCount, numX1, numX2 = quadraticEquation (numA, numb, numC) Поэтому нам придётся часть результатов выполнения функции передать через указатель в качестве аргументов: intRootsCount = quadraticEquation (numA, numB, numC, &numX1, &numX2); Здесь, опять же, от этого приёма можно избавиться, возвращая объект или структуру, хранящую результаты выполнения функции в виде полей. Пример: Можно возвращать результаты решения квадратного уравнения в виде структуры с тремя свойствами <a id="aCiteRef-3" href="#aCiteNote-3" class="aCiteRef">[3]</a>: QuadrEqResult qerResult = quadraticEquation (numA, numB, numC); intRootsCount = qerResult.count; numX1 = qerResult.x1; numX2 = qerResult.x2; Однако и в первом, и во втором случае, при использовании структур или объектов, мы можем столкнуться с ещё одной проблемой: необходимостью отдельно описывать эти структуры или классы. Причём во многих языках, например на C++, мы не можем описать структуру или класс внутри самой функции (см. <a href="#absence-of-local-functions" title="Отсутствие локальных функций">следующий</a> раздел). Нам придётся описывать их отдельно от функции, делая их неподчиненными функции сущностями, что уродует иерархию программы. Слава богу, в других языках классы можно описывать прямо внутри функции, а, например в JavaScript можно просто возвратить объект, нигде отдельно не описывая его структуру. Пример: function quadraticEquation (numA, numb, numC) { //... return ({ count: intRootsCount, x1: intX1, x2: intX2 }); } var objResult = quadraticEquation (numA, numB, numC); intRootsCount = objResult.count; numX1 = objResult.x1; numX2 = objResult.x2; Вот это настоящая красота! Отсутствие локальных функций <a>В двух словах:</a> Локальная функция должна объявляться внутри функции, которой она логически подчиняется, а НЕ в глобальном контексте. Как уже говорилось, программные системы (как объектно-ориентированные, так и процедурные) иерархичны и делятся на вложенные друг в друга модули (впрочем, это очевидно). Каждый из модулей располагает своими локальными ресурсами, используемыми только в рамках этого модуля. Например, локальные переменные являются ресурсами модуля функции. Однако ресурсами функции являются не только переменные! Подфункции, классы, структуры и т.д. также являются полноправными ресурсами функции, подчинёнными ей, и используемыми только в её рамках. Пример: Предположим, нам надо написать функцию printDossier, печатающую досье. Сверху и снизу досье должен находиться его номер, выровненный по центру и обрамлённый декоративными свасти звёздочками: ****************** 666 ****************** Имя: Макс Отто фон Штирлиц Звание: штандартенфюрер Национальность: истинный ариец Характер: нордический, выдержанный ****************** 666 ****************** Чтобы не дублировать код, печатающий верхний, и печатающий нижний номер, мы решили вынести его в отдельную функцию printNumber. Функция, печатающая номер досье, является подчинённой по отношению к функции, печатающей всё досье, используется только в её рамках и не имеет самостоятельного смысла. Поэтому, функция printNumber должна быть локальной по отношению к printDossier, и объявляться в её теле. # #Эта функция печатает досье: # def printDossier (people): # #Эта локальная функция печатает номер досье: # def printNumber (): #Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру stringLength = 32 numberLength = len (people.number) starsCount = (stringLength - numberLength) / 2 #Печатаем звёздочки слева, номер досье, и звёздочки справа print ('*' * starsCount) + people.number + ('*' * starsCount) # #Печатаем досье # #Печатаем номер сверху printNumber () #Печатаем: имя, звание, национальность и характер print 'Имя: '+ people.name print 'Звание: '+ people.rank print 'Национальность: '+ people.race print 'Характер: '+ people.character #Печатаем номер снизу printNumber () Поэтому: Объявление функций, структур и т.д. вне функции, которой они иерархически подчиняются — очень плохой приём. Это уродует иерархию программы, делая подчинённую функцию независимой, и иерархически неподчиненной родительской функции сущностью. Это нарушает принцип сокрытия информации, вынося наружу детали внутренней реализации родительской функции — подчинённую функцию. Это затрудняет понимание кода, засоряя глобальный контекст лишними сущностями. Это приводит к возникновению ошибок, делая возможным случайный вызов подчинённой функции не в родительском, а в глобальном контексте. Но многие языки программирования, например C++, не поддерживают локальные функции, классы, структуры и т.д. Пример: Поскольку C++ не поддерживает локальные функции, функцию, печатающую номер досье, нам придётся сделать глобальной, независимой и иерархически неподчинающейся функции, печатающей всё досье: /* Эта функция печатает номер досье. Мы вынуждены сделать её глобальной, хотя она является деталью внутренней реализации функции printDossier и не имеет самостоятельного смысла. */ void printNumber () { //Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру int stringLength = 32; int numberLength = strlen (people.number); int starsCount = (stringLength - numberLength) / 2; //Печатаем звёздочки слева, номер досье, и звёздочки справа for (int i = 0; i < starsCount; i++) cout<<"*"; cout<<people.number; for (int i = 0; i < starsCount; i++) cout<<"*"; } /* Эта функция печатает досье */ void printDossier (People people) { /* Печатаем досье */ //Печатаем номер сверху printNumber (); //Печатаем: имя, звание, национальность и характер cout<< "Имя: " << people.name; cout<< "Звание: " << people.rank; cout<< "Национальность: " << people.race; cout<< "Характер: " << people.character; //Печатаем номер снизу printNumber (); } Язык Pascal/Delphi поддерживает локальные функции, но, заставляет их объявлять только в начале функции. Это не так страшно, как объявлять только в начале все переменные, но тоже, иногда, бывает достаточно некрасиво. Пример: Предположим, у нас есть функция из нескольких блоков кода, каждый из которых выполняет отдельный шаг всего алгоритма. Мы решили переписать один их блоков в рекурсивной форме. Для этого мы переделали его в рекурсивную локальную функцию… после чего вынуждены перетащить этот блок кода (ставший теперь локальной функцией) с того места, где он используется, в начало основной функции. program main (); function block3 (param: integer): integer; // (4)</body>
Если это всё в iconv то на выходе такое:
[spoiler]<body screen_capture_injected="true">10 уродских приёмов программирования — Алик Кириллович <a href="http://www.alik.su"> Алик Кириллович </a> Хрупкая красота программного кода: десять приёмов программирования, способных её разрушить 26 мая 2009 г. Здесь — полная авторская версия статьи, опубликованной в журнале «Хабрахабр» в сокращённом виде из-за ограничений на размер материала. Тема красоты кода для меня очень важна, поэтому буду благодарен всем, кто поставит ссылочку, и за любой другой «пиар» статьи. Если интересно — можете <a href="http://feeds.feedburner.com/alik-kirillovich" class="aRSS">подписаться на RSS</a>. Для меня программирование — это не только технология, но и, во многом — искусство. И, поэтому, большое значение имеет красота кода. Последние несколько лет я собирал приёмы программирования, разрушающие в программном коде его утончённую красоту: <a href="#all-varibles-declaration-in-program-heading"> Объявление всех переменных в начале программы; </a> <a href="#func-result-via-argument"> Возврат результата функции через её параметр; </a> <a href="#absence-of-local-functions"> Отсутствие локальных функций; </a> <a href="#absence-of-elseif"> Отсутствие else if; </a> <a href="#parallel-arrays"> Использование параллельных массивов; </a> <a href="#array-length-in-separate-variable"> Обязательное хранение размера массива в отдельной переменной; </a> <a href="#getproperty-and-setproperty"> Доступ к свойствам объекта через obj.getProperty() и obj.setProperty(value); </a> <a href="#recursion-to-factorials"> Использование рекурсии для вычисления факториалов и Чисел Фибоначчи; </a> <a href="#absence-of-named-arguments"> Отсутствие именованных параметров функции; </a> <a> Невозможность объявления объектов «на лету». </a> Объявление всех переменных в начале программы <a>В двух словах:</a> Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы. Все программные системы иерархичны. Программы делятся на пакеты, пакеты — на классы, классы разбиваются на отдельные функции. Данные, относящиеся к тому или иному модулю программы, принято объявлять в начале этого модуля. Локальные переменные объявляются в начале функции; свойства, относящиеся ко всему классу, объявляются в начале определения класса и т.д. Однако функции не являются последним уровнем в иерархии программы. Любая нетривиальная функция состоит из блоков, реализующих отдельны шаги выполнения алгоритма. Тех самых блоков, которые никак не обособляются в коде, разве что отделяются друг от друга парочкой пустых строк. Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные «локальные» переменные! Которые объявляются в начале этого блока и используются только в его пределах. Пример: Предположим, нам надо написать функцию сортировки массива методом слияния. Алгоритм <a href="http://en.wikipedia.org/wiki/Merge_sort" title="Merge sort в Wikipedia">сортировки слиянием</a> состоит из следующих шагов: Если длина массива = 2, то: просто меняем местами эти два элемента в случае неправильного их расположения. Если длина массива > 2, то: разбиваем массив на две половины: левый и правый подмассивы; отдельно сортируем каждый подмассив (рекурсивно); сливаем эти два отсортированных подмассива в один отсортированный результирующий массив. Блоки кода, реализующие шаги этого алгоритма, являются неотъемлемыми частями функции, и не имеют отдельного самостоятельного смысла (может быть, за исключением, блока слияния, который, в принципе, можно преобразовать в отдельную функцию). Однако, они являются полноправными элементами иерархии программы, и решают свои собственные маленькие подзадачи. Поэтому: В начале функции объявляются переменные, относящиеся ко всей функции, например: intLeftIndex, intRightIndex, intLength. А переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока, например: elTemp объявляется в начале блока сортировки массива из 2-х элментов; intCenterIndex — в начале блока разбиения; arSortedSubarray и intLeftElIndex — в начале блока слияния. /* Эта функция реализует сортировку слиянием */ function mergeSort (arArryay, intLeftIndex, intRightIndex) { /* Здесь объявляются переменные, относящиеся ко ВСЕЙ ФУНКЦИИ. Переменные, относящиеся к отдельным её блокам, объявляются в начале соответствующего блока. */ intLeftIndex = intLeftIndex || 0; intRightIndex = intRightIndex || arArryay.length-1; var intLength = intRightIndex - intLeftIndex + 1; if (intLength == 2) { /* БЛОК КОДА: сортировка массива из 2-х элементов */ if (arArryay [intRightIndex] < arArryay [intLeftIndex]) { var elTemp = arArryay [intRightIndex]; arArryay [intRightIndex] = arArryay [intLeftIndex]; arArryay [intLeftIndex] = elTemp; } } else if (intLength > 2) { /* БЛОК КОДА: разбиение массива на два подмассива */ var intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1; var intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1; var intRightSubarrayLength = intRightIndex - intCenterIndex; /* БЛОК КОДА: рекурсивная сортировка каждого подмассива */ mergeSort (arArryay, intLeftIndex, intCenterIndex); mergeSort (arArryay, intCenterIndex+1, intRightIndex); /* БЛОК КОДА: слияние */ var arSortedSubarray = []; var intLeftElIndex = intLeftIndex; var intRihtElIndex = intCenterIndex+1; while (intLeftElIndex <= intCenterIndex || intRihtElIndex <= intRightIndex) { if (intRihtElIndex <= intRightIndex && (arArryay [intRihtElIndex] <= arArryay [intLeftElIndex] || intLeftElIndex > intCenterIndex)) { arSortedSubarray [arSortedSubarray.length] = arArryay [intRihtElIndex]; intRihtElIndex++; } if (intLeftElIndex <= intCenterIndex && (arArryay [intLeftElIndex] <= arArryay [intRihtElIndex] || intRihtElIndex > intRightIndex)) { arSortedSubarray [arSortedSubarray.length] = arArryay [intLeftElIndex]; intLeftElIndex++; } } for (var i = 0; i < intLength; i++) { arArryay [intLeftIndex + i] = arSortedSubarray [i]; } } } И поэтому: Объявление всех переменных в начале функции — страшное зло <a id="aCiteRef-1" href="#aCiteNote-1" class="aCiteRef">[1]</a>. Это приводит к смешению переменных, относящихся ко всей функции, с переменными, относящимися только к её отдельному блоку. Это разрывает блок на две части: объявления данных (в начале функции) и использования этих данных (в самом блоке). Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает. Пример: В случае объявления всех переменных в начале функции, переменные, относящиеся ко всей функции, перемешаются с переменными, имеющими смысл только внутри отдельных её блоков. function mergeSort (arArryay, intLeftIndex, intRightIndex) { /* Угадайте: какие из этих переменных относятся ко всей функции, а какие — к отдельным её блокам? */ var intLength, elTemp, intCenterIndex, intLeftSubarrayLength, intRightSubarrayLength, arSortedSubarray, intLeftElIndex, intRihtElIndex, i; intLeftIndex = intLeftIndex || 0; intRightIndex = intRightIndex || arArryay.length-1; intLength = intRightIndex - intLeftIndex + 1; if (intLength == 2) { if (arArryay [intRightIndex] < arArryay [intLeftIndex]) { elTemp = arArryay [intRightIndex]; arArryay [intRightIndex] = arArryay [intLeftIndex]; arArryay [intLeftIndex] = elTemp; } } else if (intLength > 2) { intCenterIndex = intLeftIndex + Math.ceil (intLength / 2) - 1; intLeftSubarrayLength = intCenterIndex - intLeftIndex + 1; intRightSubarrayLength = intRightIndex - intCenterIndex; mergeSort (arArryay, intLeftIndex, intCenterIndex); mergeSort (arArryay, intCenterIndex+1, intRightIndex); arSortedSubarray = []; intLeftElIndex = intLeftIndex; intRihtElIndex = intCenterIndex+1; //...код сокращён... } } Пример: Вы только представьте: У нас есть функция в 300 строк кода <a id="aCiteRef-2" href="#aCiteNote-2" class="aCiteRef">[2]</a>. Где-нибудь на 200-й строке нам надо поменять две переменные местами. Для этого мы лезем на 200 сток выше в начало функции, объявляем переменную temp, которая не имеет никакого отношения ко всей функции, а используется только один раз в одном месте, потом опять возвращаемся к 200-й строке и меняем переменные местами… По-моему, это просто кошмар. Хуже всего, что существуют языки, которые считают себя умнее разработчика и заставляют объявлять все переменные в начале функции. Например, такой уважаемый язык как Pascal/Delphi. Чего я ему простить не могу… Возврат результата функции через её параметр <a>В двух словах:</a> Функция должна возвращать результат, зависящий от её параметров, а НЕ принимать результат в качестве аргумента. Понятие функции (как в математике, так и в программировании) имеет чёткий смысл: вычисление результата, зависящего от аргументов. В нормальном программном коде ясно видно, что является результатом, а что аргументами: результат = функция (аргумент1, аргумент2). Однако часто встречается приём, при котором возвращаемое значение передаётся в качестве аргумента функции: функция (аргумент1, аргумент2, &результат). Этот приём ужасен. При его использовании не видно, от чего функция зависит, а что возвращает. Чаще всего, в применении этого приёма виноваты не сами разработчики, а языки программирования. Существуют две основные причины, по которым языки заставляют нас прибегать к этому приёму. Первая причина состоит в том, что в некоторых языках функции не могут создавать и возвращать сложные объекты. Пример: Предположим, что мы хотим на C++ написать функцию, перемножающую две матрицы и возвращающую получившуюся матрицу в качестве результата. Матрицы мы решили представлять в виде двумерного массива. Но мы не можем объявить в функции результирующий двумерный массив, а затем вернуть его: int mtxResult [10][10] = mult (mtxA, mtxB); Поэтому нам придётся сначала вне функции объявить результирующий массив, а затем вызвать функцию перемножения, передав результат в качестве аргумента: mult (mtxA, mtxB, mtxResult); В данном случае от использования этого приёма можно избавиться, храня результат в том виде, который функция может возвратить. Пример: Можно хранить матрицу не в виде двумерного массива, а в виде структуры или объекта: Matrix mtxResult = mult (mtxA, mtxB); Код станет менее лаконичным (из-за объявления дополнительных структур), но, зато, гораздо более красивым. Вторая причина состоит в том, что в большинстве языков программирования функция не может возвращать несколько значений. Пример: Предположим, что мы хотим на C++ написать функцию, решающую квадратное уравнение. Функция принимает в качестве аргумента коэффициенты a, b, c и возвращает три результата: число корней, x1 и x2. Однако вернуть сразу три значения в C++ невозможно: intRootsCount, numX1, numX2 = quadraticEquation (numA, numb, numC) Поэтому нам придётся часть результатов выполнения функции передать через указатель в качестве аргументов: intRootsCount = quadraticEquation (numA, numB, numC, &numX1, &numX2); Здесь, опять же, от этого приёма можно избавиться, возвращая объект или структуру, хранящую результаты выполнения функции в виде полей. Пример: Можно возвращать результаты решения квадратного уравнения в виде структуры с тремя свойствами <a id="aCiteRef-3" href="#aCiteNote-3" class="aCiteRef">[3]</a>: QuadrEqResult qerResult = quadraticEquation (numA, numB, numC); intRootsCount = qerResult.count; numX1 = qerResult.x1; numX2 = qerResult.x2; Однако и в первом, и во втором случае, при использовании структур или объектов, мы можем столкнуться с ещё одной проблемой: необходимостью отдельно описывать эти структуры или классы. Причём во многих языках, например на C++, мы не можем описать структуру или класс внутри самой функции (см. <a href="#absence-of-local-functions" title="Отсутствие локальных функций">следующий</a> раздел). Нам придётся описывать их отдельно от функции, делая их неподчиненными функции сущностями, что уродует иерархию программы. Слава богу, в других языках классы можно описывать прямо внутри функции, а, например в JavaScript можно просто возвратить объект, нигде отдельно не описывая его структуру. Пример: function quadraticEquation (numA, numb, numC) { //... return ({ count: intRootsCount, x1: intX1, x2: intX2 }); } var objResult = quadraticEquation (numA, numB, numC); intRootsCount = objResult.count; numX1 = objResult.x1; numX2 = objResult.x2; Вот это настоящая красота! Отсутствие локальных функций <a>В двух словах:</a> Локальная функция должна объявляться внутри функции, которой она логически подчиняется, а НЕ в глобальном контексте. Как уже говорилось, программные системы (как объектно-ориентированные, так и процедурные) иерархичны и делятся на вложенные друг в друга модули (впрочем, это очевидно). Каждый из модулей располагает своими локальными ресурсами, используемыми только в рамках этого модуля. Например, локальные переменные являются ресурсами модуля функции. Однако ресурсами функции являются не только переменные! Подфункции, классы, структуры и т.д. также являются полноправными ресурсами функции, подчинёнными ей, и используемыми только в её рамках. Пример: Предположим, нам надо написать функцию printDossier, печатающую досье. Сверху и снизу досье должен находиться его номер, выровненный по центру и обрамлённый декоративными свасти звёздочками: ****************** 666 ****************** Имя: Макс Отто фон Штирлиц Звание: штандартенфюрер Национальность: истинный ариец Характер: нордический, выдержанный ****************** 666 ****************** Чтобы не дублировать код, печатающий верхний, и печатающий нижний номер, мы решили вынести его в отдельную функцию printNumber. Функция, печатающая номер досье, является подчинённой по отношению к функции, печатающей всё досье, используется только в её рамках и не имеет самостоятельного смысла. Поэтому, функция printNumber должна быть локальной по отношению к printDossier, и объявляться в её теле. # #Эта функция печатает досье: # def printDossier (people): # #Эта локальная функция печатает номер досье: # def printNumber (): #Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру stringLength = 32 numberLength = len (people.number) starsCount = (stringLength - numberLength) / 2 #Печатаем звёздочки слева, номер досье, и звёздочки справа print ('*' * starsCount) + people.number + ('*' * starsCount) # #Печатаем досье # #Печатаем номер сверху printNumber () #Печатаем: имя, звание, национальность и характер print 'Имя: '+ people.name print 'Звание: '+ people.rank print 'Национальность: '+ people.race print 'Характер: '+ people.character #Печатаем номер снизу printNumber () Поэтому: Объявление функций, структур и т.д. вне функции, которой они иерархически подчиняются — очень плохой приём. Это уродует иерархию программы, делая подчинённую функцию независимой, и иерархически неподчиненной родительской функции сущностью. Это нарушает принцип сокрытия информации, вынося наружу детали внутренней реализации родительской функции — подчинённую функцию. Это затрудняет понимание кода, засоряя глобальный контекст лишними сущностями. Это приводит к возникновению ошибок, делая возможным случайный вызов подчинённой функции не в родительском, а в глобальном контексте. Но многие языки программирования, например C++, не поддерживают локальные функции, классы, структуры и т.д. Пример: Поскольку C++ не поддерживает локальные функции, функцию, печатающую номер досье, нам придётся сделать глобальной, независимой и иерархически неподчинающейся функции, печатающей всё досье: /* Эта функция печатает номер досье. Мы вынуждены сделать её глобальной, хотя она является деталью внутренней реализации функции printDossier и не имеет самостоятельного смысла. */ void printNumber () { //Вычисляем количество звёздочек слева и справа, так, чтобы номер оказался по центру int stringLength = 32; int numberLength = strlen (people.number); int starsCount = (stringLength - numberLength) / 2; //Печатаем звёздочки слева, номер досье, и звёздочки справа for (int i = 0; i < starsCount; i++) cout<<"*"; cout<<people.number; for (int i = 0; i < starsCount; i++) cout<<"*"; } /* Эта функция печатает досье */ void printDossier (People people) { /* Печатаем досье */ //Печатаем номер сверху printNumber (); //Печатаем: имя, звание, национальность и характер cout<< "Имя: " << people.name; cout<< "Звание: " << people.rank; cout<< "Национальность: " << people.race; cout<< "Характер: " << people.character; //Печатаем номер снизу printNumber (); } Язык Pascal/Delphi поддерживает локальные функции, но, заставляет их объявлять только в начале функции. Это не так страшно, как объявлять только в начале все переменные, но тоже, иногда, бывает достаточно некрасиво. Пример: Предположим, у нас есть функция из нескольких блоков кода, каждый из которых выполняет отдельный шаг всего алгоритма. Мы решили переписать один их блоков в рекурсивной форме. Для этого мы переделали его в рекурсивную локальную функцию… после чего вынуждены перетащить этот блок кода (ставший теперь локальной функцией) с того места, где он используется, в начало основной функции. program main (); function block3 (param: integer): integer; // (4)</body>
То есть если присмотреться стопориться на "--¬"
Конечно можно было бы убрать какими либо методами эти символы, но код должен будет отрабатывать на многих сайтах, и если например он китайский, то опять будет затык на иероглифах. Когда уже сделают PHP6 с людской поддержкой utf8