Original: http://www.allegro.com/papers/htpp.html
за
Стеном Сілером
Allegro Consultants, Inc.
Ця стаття присвячена тому, як написати якісний код Pascal. Особлива увага приділяється аспектам кодування написання коду Pascal.
Приклад програми: roman.p.txt, програма для перетворення римських цифр в арабські цифри.
Приклад поганого коду: trim, фактичний реальний зразок поганого коду.
Як попросити допомоги програмування: тут.
Примітка: цей документ використовує Pascal /іХ, Pascal на комп’ютері HP 3000, але майже всі точки застосовні до будь-якого варіанту Pascal, і найбільш застосовні також і до інших мов програмування.
Я хочу, щоб обговорення в цій статті, щоб покрити чотири “кити” програмування: філософію, продуктивність, проблеми і портативність. Я думав, намагаючись розділити цей папір на чотири глави, по одному для кожного з “кита”, але зрозумів, що для багатьох тим, що я б поставив в одному розділі, однаково хороші аргументи можуть бути підняті для передачі його в інший. Таким чином, замість того, щоб мати одну главу за “кита”, я організував цей папір у розділи: стиль, варіанти кодування, проблеми із продуктивністю та портативність.
У кожному розділі 4 “кити” використовуються в якості принципів, що лежать в основі набору керівних принципів. Ці пропоновані правила не повинні розглядатися, як якщо б вони були 10 (або 12% або $ A) заповідей. Вони мої правила, ті, які я використовував (в різних формах для різних мов) в написанні понад два мільйони рядків коду. Керівництво повинно зростати, як це роблять люди. Я можу подивитися на програми, які я написав в 1970 році і побачити зміни в стилі в наступні роки. Незважаючи на зміни, загальний вигляд залишився колишнім.
Основна мета даної роботи полягає в заохоченні читачів думати про різні аспекти стилю і створювати свої власні принципи кодування. Це важливе підгрунтя для написання якісних програм у Pascal або будь-якою мовою.
Я написав понад мільйон рядків високого рівня вихідного коду мови з 1970 року; це, звичайно ж, включає деякі порожні рядки, рядки коментарів і лінії, клоновані з інших файлів. Проте, на цьому тлі я вивчив, що “вигляд” (або зовнішній вигляд) програми критично важливий для розуміння того, що робить код.
Основні розділи:
Вказівки:
№ 1-1. Немає такого поняття, як “технологічна програма”. Таким чином, ваші вказівки застосовні до всього, що ви пишете. Особливо до “разових” або “технологічних” програм.
Коментар:
№ 1-1. Я помітив, що майже завжди повторно використовую свої так звані разові або технологічні програми. І, якщо зрізати кути на використання моїх вказівок, я завжди повинен буду платити за це пізніше.
Один з ключів до успішної реалізації програми є правильний вибір імен.
Успіх Паскаля програми полягає в хорошому розумінні даних програма маніпулює. Це розуміння знаходить своє відображення у виборі структур даних і змінних, які використовуються програмою.
Протягом даній роботі фраза “імена”, коли використовується без класифікаторі, відноситься до імен змінних, констант, типів, процедур і функцій.
Вказівки:
№ 1-11. Використовуйте іменники для змінних, констант і типів; використовувати дієслівні для процедур і функцій.
№ 1-12. У кінці назви типу зазвичай має бути “_type”.
№ 1-13. Імена повинні бути повністю в нижньому регістрі, із нижнім підкресленням (“_”) між словами (наприклад: card_count).
Коментар:
№ 1-11. У наступному прикладі одного рядка, має бути достатньо, щоб показати важливість даної вказівки:
if card_count = 52 then
“card_count” – це ціла змінна чи функція? Різниця має вирішальне значення для розуміння програми. Єдиним орієнтиром читач може можливо мати його/її розуміння англійської мови. (Правила капіталізації не вистачило б!)
Важливо пам’ятати, що інші люди будуть читати код, коли-небудь. (“Інші” люди включає в себе вам день після того, як ви написали код!) Вони будуть потрібні “покажчики”, щоб допомогти їм правильно інтерпретувати код, який вони читають.
Відповідно до вказівки № 1-11, “card_count” є змінною. Якщо він мав бути функцією, то його ім’я повинне було бути щось на зразок “count_the_cards”.
№ 1-12. Деякі програмісти випереджають “t_”, “typ_”, or “type_”. Інші додають “_t”, “_typ” або “_type”. Кілька заблудлих душ були настільки захоплені цією ідеєю, що вони випереджали “t_” і додавали “_type” до кожного типу.
Перевага додавання, замість Попереджання, є те, що змінні, пов’язані з тієї ж самої речі буде сортувати разом, в алфавітному перерахування змінних.
Основна ідея полягає в тому, щоб чітко диференціювати імена певних користувачем типів з змінних, а також процедури (або функції).
№ 1-13. Це керівництво відображає той факт, що ми носії англійської мови. Якби ми були говорять німецькою мовою, наші програми, ймовірно, слід використовувати великі літери на початку кожної змінної. Зверніть увагу, наскільки важче читати це?
Я бачу три причини, за якими з великої літери (або все) деяких типів імен. Ніхто не варто:
- ви вибрали погані імена для ваших змінних, і думати, що капіталізація буде компенсувати. … Цього не буде.
- ви не впевнені, що компілятор знає, що ім’я змінної, якщо вона не капіталізується в певному стилі.
Сюрприз: компілятор розумніший, ніж ви! - (в основному для програмістів C) ви хочете, здатність ретельно заплутати читача, маючи дві різні змінні, які відрізняються тільки в разі їх імен (наприклад: int foo; char FOO).
…більш почесному шляху до безпеки праці існують.
Під час писання у C, я пишу свої імена макросів великими буквами (“#define FAIL(s)…”), тому що це в переважній більшості випадків найпопулярніший спосіб зробити це. Тим не менш, це не без докорів жалю. Ви втрачаєте деяку гнучкість, коли ви будете слідувати цій конвенції.
Так, якщо змінити просту змінну на зразок “lines_cached” бути визначити (можливо, тому що я тепер стежити за кількістю кешованих ліній на основі за щось), я не можу просто сказати: #define lines_cached cached_lines_count [clc_simple] Я маю сказати #define LINES_CACHED cached_lines_count [clc_simple] *і* зробити велику заміну “lines_cached” на “LINES_CACHED“.
У повністю малими дні мого програмування C, я міг би переключити щось від простої змінної, щоб бути більш складним, без видимих змін в іншій частині коду. З іншого боку, зараз, коли я бачу LINES_CACHED, це нагадування, що це трохи більш складне поняття, ніж “lines_cached“.
Назви типів повинні бути описовим, і повинні бути чітко помітні як типи, так що вони виділяються при використанні типу примусу або ключових виразів С++ виразів.
Вказівки:
№ 1-21. Універсальні типи, які упаковані масиви напівкоксу повинні бути названі “рас” з кількістю символів.
type pac8 = packed array [1..8] of char; pac80 = packed array [1..80] of char;
№ 1-22. Типи, які є масиви повинні мати “array” у назві. (Виняток: назви типу pac## (див. № 1-21).)
№ 1-23. Типи-вказівники повинні мати “ptr” у назві. Довгі вказівники (64 біти) повинні мати “lptr” у назві. (У Pascal/iX, звичайні вказівники завширшки 32 біти…”довгий” вказівник – 64 біти, що може вказувати на будь-яке місце в пам’яті.)
№ 1-24. Записи повинні мати імена полів з префікса, який відображає, частиною яких записів вони є.
№ 1-25. Прості типи не повинні, як правило, відображати свій “розмір”.
№ 1-26. Намагайтеся уникати використання “істина” і “брехня” для функціональних результатів. Використовуйте натомість тип “good_failed_type”.
Коментар:
№ 1-21. Мова Pascal має особливі правила для змінних, які упаковані масиви нагару з нижньою межею 1. Ці спеціальні правила були додані в спробі зробити стандартний Pascal придатним для обробки тексту. В результаті, більшість реалізацій Pascal зверніться до розділу “РАС” в якості змінної (або типу), які представляють собою упакований масив напівкоксу, з нижньою межею 1. Отже, це зручно, щоб відобразити цю стандартизацію в наших власних типів.
Зверніть увагу, що ця директива каже, родові типи. Коли я використовую TurboIMAGE (DBMS на HP 3000) і хочу створити тип, який відповідає елементу IMAGE, Я спробую дати типу ім’я, схоже на ім’я елемента:
{IMAGE items: } { cust_name x20;} type cust_name_type = packed array [1..20] of char;
У наведеному вище прикладі, я використовував “20” в оголошенні типу тільки тому, що був безпосередньо очевидним значення (з огляду на коментар про деталі зображення). Я ніколи б не використовувати константу “20” пізніше в програмі. Якщо мені потрібно послатися на “20”, я б або використовувати Pascal/iX конструкції “sizeof (cust_name_type)” або додавав би “const cust_name_len = 20” і використовувати константу оголосити запис.
№ 1-24. Це керівництво робить поля записи негайно ідентифікувати, коли вони використовуються в “з” заявою. Приклад:
поганий:
type hpe_status = record {name matches MPE/iX's usage} info : shortint; subsys : shortint; end; hpe_status_ptr_type = ^ hpe_status; ... var hs_status_ptr : hpe_status_ptr_type; ... with hs_status_ptr^ do begin info := my_info; subsys := my_subsys; end;
гарний:
type hpe_status = record {name matches MPE/iX's usage} hs_info : shortint; hs_subsys : shortint; end; ... with hs_status_ptr^ do begin hs_info := my_info; hs_subsys := my_subsys; end;
У поганому прикладі читач, який не знайомий з кодом, не знатиме, що поля покажчика змінюються. У хорошому прикладі це очевидно.
№ 1-25. У SPL (подібна до ALGOL мова програмування) це досить часто, щоб використовувати суфікс або префікс для позначення “тип” змінної. (Приклад: подвійне ktr’d). З обмеженнями Pascal за типами відповідності це набагато менш необхідно.
№ 1-26. “істина” та “брехня” мало що означають самі по собі. Приклади:
поганий:
function open_files : boolean; ... open_files := false; ... if open_files then ...
гарний:
type good_failed_type = (failed, good); function open_files : good_failed_type; ... open_files := good; ... if open_files = failed then ...
У поганому прикладі читач не має ні найменшого уявлення про те чи “open_files” повертається справжнє значення є хорошим або поганим.
Це керівництво є ще більш важливим при програмуванні в середовищі кількох мов, тому що різні мови (і різні операційні системи) мають дивні ідеї про те, чи є “0” (або “брехня“) означає, що добре чи погано.
У своєму програмуванні C я використовую:
#ifdef GOOD #undef GOOD #endif #ifdef FAILED #undef FAILED #endif #define GOOD 1 #define FAILED 0
Там є “undef“, тому що, з дивовижно поганий прийняття рішень, хто бере участь в BSD або розробки Mac вирішив, що файл /usr/include/protocols/talkd.h повинен мати макрос FAILED, зі значенням 2. (Або, на HP-UX, file /usr/include/sio/scsi_meta.h має GOOD як 0, а файл /usr/include/sys/netio.h має FAILED як 3.) (Є й інші погані приклади ГАРНИЙ і НЕГІДНИЙ!)
Файли, що надаються за допомогою постачальника ОС в якому разі не можна кооптувати “відомого” слова, як send, get, put, FAILED або free. Такі слова просто занадто загальний, щоб мати будь-яке значення і самі по собі. На жаль, ми застрягли з багатьма такими словами вже використовується.
Впорядкування вихідного коду може істотно вплинути на читаність програми. Замовлення рекомендації діляться на три області: типи та константи, змінні і процедури та функції.
(Файли “include” важче категоризувати …іноді вони повинні бути на першому місці, іноді вони повинні бути після всіх типів/змінних.)
Вказівки:
№ 1-31. Прості константи повинні бути надані на початку області декларації, а потім за типами, структурованих констант, а потім за допомогою змінних. У кожній групі, ідентифікатори повинні бути розташовані в певному порядку. Якщо інший порядок не уявляє себе, алфавітний порядок повинен бути використаний. Приклад:
const max_queues = 10; type aardvark_quantity_type = 0..9; queue_name_type = packed array [1..8] of char; queue_names_type = array [1..max_queues] of queue_name_type; zoo_fed_arrdvarks_type = array [aardvark_quantity_type] of integer;
№ 1-32. Здебільшого в одному випадку потрібно “type” і “var”, а також до двох випадків “const” (один для простих констант, і один для структурованих констант).
№ 1-33. Ідентифікатори в області декларації (константи, типи, змінні) повинні бути оголошені по одному в рядку, в деякому порядку. Алфавітний порядок за замовчуванням. Якщо інше впорядкування використовується, воно повинно бути пояснено з коментарем.
№ 1-34. Ідентифікатори в області декларації повинні бути введені таким чином, щоб символи “=” вирівнювалися за константами й типами, а символи “:” – за змінними та полями в записах.
№ 1-35. Типи записів, які містять вкладені записи ніколи не повинні бути оголошені як “анонімні типи”. Приклади поганих і хороших практик:
поганий:
type payment_record_type = record pay_date : date_type; pay_time : time_type; pay_style : (paid_cash, paid_check, paid_charge); end;
гарний:
type payment_style_type = (paid_cash, paid_check, paid_charge); payment_record_type = record pay_date : date_type; pay_time : time_type; pay_style : payment_style_type; end;
№ 1-36. “Хрустких” записів слід уникати, якщо потреба в герметичній упаковці полів не перекриває втрати продуктивності, які вони викликають.
Pascal/iX підтримує “хрусткі записи”, розширення за межі простого “упакованої записи”. У хрустіли записи, немає * немає * невикористовувані біти. Якщо оголосити хрустів запис з 1 бітове поле, а потім 8-бітового поля, а потім 1 бітове поле, то вся запис буде займати 10 біт, а 8-бітове поле потребують додатковий код для завантаження/зберігання.
№ 1-37. Великі змінні зовнішнього блоку (> 256 байтів) має бути оголошено в минулому, навіть якщо це порушує директиву № 1-33. (Крім того, великі локальні змінні повинні бути оголошені перші.) (Примітка: це підказка продуктивності Pascal/iX … інші комп’ютери на інших машинах, ймовірно, зробити щось по-іншому!)
№ 1-38. Процедури і функції перемішані (тобто: не відокремлюйте процедури від функцій).
№ 1-39. Процедури (і функції) повинні бути оголошені в певному порядку (алфавітний за замовчуванням).
№ 1-40. Більше одного рівня вкладеної процедури слід уникати.
№ 1-41. Посилання іntrinsic повинні бути оголошені один раз, на зовнішньому рівні, після того, як всі константи, типи і змінні, а також перед будь-якими “зовнішніми”, “вперед” або фактичних процедур. (“Внутрішня” є посиланням на свого роду попередньо скомпільовані “зовнішньої” процедури декларування, підтримується більшістю мов на HP 3000.)
№ 1-42. Посилання іntrinsic повинні бути в алфавітному порядку, влаштованої власними файлами. Приклад:
Function ascii : shortint; intrinsic; Function binary : shortint; intrinsic; Procedure quit; intrinsic;
У наведеному вище прикладі, два простору були введені після того, як слово “Function“, так що імена всіх вбудованих функцій будуть приведені у відповідність, незалежно від того, чи були вони функції або процедури. Дуже шкода, що Pascal робить нас довести компілятором, що ми знаємо функціональний тип кожної внутрішньої.
Зверніть увагу на великі літери “P” і “F” в наведеному вище прикладі. Це один екземпляр дуже корисною дисципліни кодування, що пояснюється у вказівці № 1-45 нижче.
№ 1-43. Усі процедури і функції повинні бути оголошені з оголошеннями «вперед», які знаходяться в алфавітному порядку.
№ 1-44. Типи, які є “наповнювачі” повинні бути оголошені як “ціле” або “байт” (де “байт” оголошений як 0..255) замість того, щоб як “напівкоксу” для своїх базових типів.
№ 1-45. “Вперед“, “зовнішні” і “внутрішні” процедура (і функція) декларації повинні першої літери “процедури” і “функції”.
У моєму програмування C, у мене є один майстер-файл включає (“stddefs.h”, який встановлює прапори, які підходять для поточної операційної системи (наприклад, #define HAVE_STRTOLL 1, якщо платформа має функцію strtoll), а потім OS/постачальника за умови, включає в себе (наприклад, stdlib.h, а потім мій другий включає в себе (наприклад, “sstypes.h”,), то всі мої глобальні типи, Перерахування, змінні, визначає фрагментів коду, «вперед» декларації функції, то всі мої функції. Коментар:
№ 1-34. Я взагалі вирівнювати “=” для типів і consts в колонці 31, від “:” для полів в межах типу також в колонці 31, а “:” для змінних в колонці 19. Якщо змінні мають досить довгі імена, я будете часто просто вирівняти “:” в колонці 31. Я застосовую це вирівнювання до “:” використовується в оголошеннях параметрів, таким чином, що ім’я змінної починається в колонці ??:
procedure parse_gribbitz ( a_token : str80; var status : boolean; anyvar stuff : char) option default_parms ( stuff := nil);
У наведеному вище прикладі, зверніть увагу, що імена параметрів збігаються. (Очевидно, що це не завжди можливо, особливо якщо параметр $ вирівнювання $ інформація, зазначена.) (Примітка: “anyvar” розглядається знову в 2-9 нижче)
№ 1-35. Змінні (і поля записів), які анонімні типи ніколи не можуть бути передані по посиланню (як “змінна” параметрів) до звичайних процедур. Дійсно, анонімні записи, як правило, не можуть бути передані за значенням в процедури.
№ 1-36. Доступ поля “хрустів” записів може зайняти до трьох разів кількість інструкцій, які потрібні були б, якщо запис хрустіли. Розглянемо наступні два типи:
type bad_type = crunched record bad_misc : shortint; {Bytes 0, 1} bad_status : integer; {Bytes 2, 3, 4, 5} end; {Total size: 6 bytes} good_type = record good_misc : shortint; {Bytes 0, 1} good_status : integer; {Bytes 4, 5, 6, 7} end; {Total size: 8 bytes}
Коли відкрито доступ до поля “bad_status”, Pascal/iX видає три інструкції (LDH, LDH і DEP). Коли відкрито доступ до поля “good_status”, Pascal/iX видає одну інструкцію (LDW).
№ 1-37. Pascal/iX може ефективно отримати доступ тільки перші 8192 байт глобальних або * останній * 8192 байт локальних змінних. Pascal/iX виділяє змінні в (приблизно) першого видно, першого виділеного способом. Таким чином, для глобальних змінних, поміщаючи маленькі перший і великі другий має тенденцію бути більш ефективним. Для локальних змінних, поклавши великі першими, і маленьких другий має тенденцію бути більш ефективним.
Оскільки Pascal/iX виділяє зовнішній блок (“глобальний”) змінні в зворотному порядку, ніж локальні змінні, то правило слідувати:
- У зовнішньому блоці оголошувати невеликі змінні по-перше, і ваші великі змінні другого;
- У процедурах/функціях оголошувати великі змінні по-перше, і малі змінні другий.
Розгляньте такі приклади:
поганий:
var {outer-block variables} big_array : array [0..9999] of integer; {40,000 bytes} ktr : integer; ... procedure foo; var ktr2 : integer; big_array_2 : array [0..9999] of integer; {40K bytes}
гарний:
var {outer-block variables} {small variables...} ktr : integer; {big variables...} big_array : array [0..9999] of integer; ... procedure foo; var {big variables...} big_array_2 : array [0..9999] of integer; {40K bytes} {small variables...} ktr2 : integer;
У поганому прикладі Pascal/iX використовуватиме дві інструкції “ktr” і “ktr2”. У гарному прикладі Pascal/iX використовуватиме одну інструкцію, щоб отримати доступ до “ktr” і “ktr2”.
№ 1-38. Розмежування функцій Pascal у порівнянні з процедурами. На жаль, в кращому випадку. Ми не повинні заохочувати дизайнерів мови, щоб увічнити цей недолік.
№ 1-39. Твердження Pascal/iX “$locality” може бути використаний, щоб сказати компоновщик групи зазначених процедур разом, незалежно від їх порядку в вихідному коді.
№ 1-40. Pascal дозволяє процедури, які будуть оголошені в рамках процедур, оголошених в рамках процедур, які…
Вкладені процедури заплатити штраф продуктивність під час виконання, коли вони доступ до змінних глобальні до них, які є локальними для оточуючих процедур.
Процедури вкладені більш ніж в цілому два глибоких (процедури i.e.: “Зовнішньому рівні” і однієї внутрішньої процедури), як правило, має на увазі, що існують і інші проблеми проектування.
Деякі отладчики мають труднощі з установкою точок зупину на вкладених процедур.
№ 1-43. Ця додаткова робота часто окупається добре при написанні “модуль”, який буде пов’язаний з іншими програмами. Я часто поклав всі мої «вперед» декларацій в файл, а потім використовувати такі QEDIT команди для створення файлу “зовнішнього” заяви для інших модулів до $ключають:
t myfile.forward c "forward"(S)"external"@ k myfile.external
(QEDIT – широковживаний редактор на HP 3000.)
#1-44. Команда Debug/iX Format Virtual (FV) буде виробляти набагато більш читабельним вихід для випадкових даних, коли базовий тип числовий замість символу. (Debug/iX це відладчик в комплекті з MPE/iX на HP 3000.)
#1-45. За допомогою цього посібника, і пов’язаний з одним в наступному розділі, командного рядка QEDIT:
l "procedure" (s)
будуть перераховані в першому рядку кожної процедури декларації. Зверніть увагу, що тільки фактичне оголошення буде відображатися, а не “вперед” або “зовнішні” заяви, так як вони були оголошені з великою буквою “П” у “процедури”.
Примітка: не кажіть редактор автоматично весь текст на підвищену ви шукати (наприклад: установити вікно (UP) у QEDIT), оскільки це поразка мети даного керівництва.
В даному розділі розглядаються стилі кодування для виконуваного коду частина програми Pascal.
Вказівки:
№ 1-50. Весь код повинен бути в нижньому регістрі.
№ 1-51. Коментарі повинні бути англійською мовою, так і повинно бути в змішаному випадку, як це практикується на англійській мові.
№ 1-52. Коментарі повинні відображатися в одному з двох стилів, в залежності від їх розміру:
- вирівняний по вибраній колонці, праворуч від коду (так очей може розрізняти між кодом і коментарями).
- 6 вирівняний простору праворуч від поточного коду відступу, з нового рядка вище і нижче.
Приклад:
{the following loop looks for a null character} null_index := -1; {-1 will mean "not found"} test_inx := 0; {index of first char} done := (len = 0); {don't loop if no data} while not done do begin {see if current character is null...} if buf [test_inx] = chr (0) then begin {found a null!} null_index := test_inx; {remember location} done := true; {terminate loop} end else begin {incr inx, check end} test_inx := test_inx + 1; if test_inx >= len then {inx is 0-based} done := true; end; end; {while not done}
№ 1-54. “{” І “}” символи використовуються для запуску і припинити коментарі, ніколи “(*” і “*)” пар.
№ 1-55. “{“, Як правило, не слід пробіл, а також не “}”, як правило, передує пробіл (якщо це не вирівняти його з до рядка в “}”).
№ 1-56. Лінії не повинні бути не довше 72 байт, навіть якщо Pascal/iX дозволяє довші вхідні лінії.
№ 1-57. Порожні рядки нічого не варті під час виконання, і слід використовувати рясно окремих ділянок коду. Приклад:
if ktr > max_ktr then max_ktr := ktr; {remember new high water} done := false; while not done do begin ...
№ 1-58. Заява “кінець” не повинен мати коментар на ньому. Pascal ніколи не перевіряє, що ваш коментар відповідає дійсності, у всякому разі.
№ 1-59. Основною одиницею відступів 3, а не 4 або 2.
№ 1-60. Відступ “починаються” з 3 місця більше, ніж на початку попереднього рядка. Код після того, як “почати” (аж до «кінця») знаходиться на тому ж рівні, що і “почати”.
№ 1-61. Продовження лінії відступом 6 більше, ніж на початку першого рядка.
№ 1-62. “, А потім” з “якщо / то” заяву, як правило, на тій же лінії, що і решта логічного виразу, а не на наступному рядку сам по собі (якщо необхідно для інтервалу, а потім вона відступом 6), і ніколи на в тому самому рядку, як заяву після “тоді”.
№ 1-63. “Інакше якщо” конструкція може розглядатися як якби це був новий Pascal конструкція: “ElseIf”. (Тобто.: “Якщо” слід за “інше” на тому самому рядку.)
№ 1-64. “Goto 999” є прийнятним методом розгалуження до кінця процедури (або функції).
№ 1-65. Ніякі інші “GOTO” s не потрібні.
№ 1-66. Намагайтеся тримати процедури нижче п’яти сторінок (300 рядків).
№ 1-67. Ніколи не використовуйте слово “процедура” або “функції” в коментарі точно в нижньому регістрі. Замість цього слід використовувати “звичайні” або “процедури” або “функції”.
№ 1-68. Завжди завершувати процедуру або функцію з коментарем форми:
end {nameofroutine proc};
№ 1-69. Залишається пробіл між ім’ям процедури/функції і “(” параметра списку.
№ 1-70. Залишається пробіл після кожної коми в списку параметрів.
№ 1-71. Поміщений прогалини навколо операторів (наприклад.: “: =”, “+”, “-“), а в передній частині лівої дужки (“[“).
Коментар:
№ 1-52. Уніфіковані коментарі зробити код акуратніше погляд. Така практика дозволяє читачеві легко прочитати код або коментарі.
№ 1-55. Практика завжди після “{” з простором і, що передував “}” відходи цінне простір на лінії.
№ 1-56. Довгі рядки не будуть перераховані добре на більшості терміналів, вони не є прийнятними для всіх редакторів.
№ 1-58. Я поставив коментар на “кінець” заяву, коли він більш ніж приблизно 10 рядків з відповідного “починаються”.
№ 1-60. Значення “3” і судову заборону проти “подвійного відступу” економить простір і робить результат більш зручним для читання. Розглянемо наступні два приклади:
поганий:
for i := 1 to 10 do begin buf [i] := 0; foo [i] := 0; end; if ktr = 0 then begin if not done then begin ...
гарний:
for i := 1 to 10 do begin buf [i] := 0; foo [i] := 0; end; if ktr = 0 then begin if not done then begin ...
Багато програмістів Pascal бачили “подвійного відступу” стиль, тому що професор відповідає UCSD Паскаль (на початку 1970-х років) використовували цей стиль. Зверніть увагу, що він був в першу чергу вчитель, не програміст.
№ 1-61. Приклад:
if (card_count = max_card_count) and all_cards_accounted_for then begin ...
Мета відступу рядки продовження, щоб дати зрозуміти читачеві, що до лінії триває до наступного рядка. Якщо ніяких додаткових відступи не використовується, то стає важко визначити різницю між наступним затвердженням і продовженням поточного оператора.
Коли у мене є комплекс “і / або”, я намагаюся, щоб зробити його доступним для читання при виконанні рядка продовження:
поганий:
if ((card_count = prior_card_count) and ((number_of_cards_left > cards_for_book)) then begin
гарний:
if ( (card_count = prior_card_count) and (number_of_cards_left > cards_for_book) ) then begin
У поганому прикладі зверніть увагу, як “почати” розмивається “і” починаючи з тієї ж самої колонці.
№ 1-62. Слово «тоді» є синтаксичний цукор: він відгодовує лістинг, і не має значення викупної. Коли читач бачить “якщо”, він або вона автоматично знає, що “потім” настає час, в кінці кінців. Один тільки відступи буде досить, щоб сказати читачеві, який був знайдений “, то” заяву. Приклади:
поганий:
if card_count = max_card_count then done := true else ...
гарний:
if card_count = max_card_count then done := true else ...
У наведеному вище прикладі поганий, читач повинен подумки просіяти через надлишок словоблудства (“тоді”) в передній частині “зроблено: =”, щоб зрозуміти, афекти “істинного” вираз булевої. У хороший приклад, читання в лівій частині лістингу досить.
№ 1-63. “Інакше якщо” конструкції, як правило, знаходяться в одній з двох ситуацій:
- “вибігання“, “якщо / то / інше”. Для цієї конструкції, то “ще, якщо” на тій же лінії є природним читаність імпульс.
- вкладена “якщо / то / інше”. Для цієї конструкції, то “інше” з “якщо” з відступом на наступному рядку природно.
Приклад “вибігання”, “якщо / то / інше”:
if token_check ('EXIT') then wrapup else if token_check ('LIST') then do_list else if token_check ('PRINT') then do_print else writeln ('Unknown command: ', token);
Примітка: в наведеному вище стилі, я буду часто ставити 5 зайві пробіли перед першим “маркера перевірки”, так що QEDIT командного рядка LIST “Маркер перевірки” покаже всі три “token_check” фрази добре вирівняно.
Приклад вкладеного “якщо / то / інше”:
if card_count = max_card_count then if done then ... else discard_current_card else if current_card = joker then try_best_wildcard else calculate_score;
Стиль, який я не рекомендую, полягає в наступному:
if token_check ('EXIT') then wrapup else if token_check ('LIST') then do_list else ...
Вище стиль має наслідки різких читаність, якщо несвоєчасне “виштовхування сторінки” відбувається в перерахуванні між “ще” лінії і наступний “якщо” лінії.
№ 1-64. Pascal відсутня оператор “виходу”. Обидва C і SPL мають деяку форму “повернення з цієї процедури прямо зараз” заяви. Це єдине місце, де я використовую “GOTO” на мові Pascal.
№ 1-67. Це керівництво означає, що редактор “знайти” і “список” команди шукають “процедури” і “функції” ніколи не буде випадково знайти рядки коментаря натомість. (Дивись також № 1-45).
№ 1-68. Це робить його дуже легко знайти кінець процедури (и) будь-(або все) з командою “знайти”.
№ 1-69/70/71 Прогалини зробити код більш зручним для читання, так само, як вони роблять англійська зручнішим для читання. Зверніть увагу на пробіл після коми в попередньому реченні. Приклад:
поганий:
fid:=fopen(filename,3,0);
гарний:
fid := fopen (filename, 3, 0);
У цьому розділі розглядаються вибору, зробленого в письмовій формі виконуваного коду.
Вказівки
№ 2-1. Вирішіть, якщо ваш стиль повинен мати функції, які повертають помилки або процедури, які мають параметри стану (або обидва), а потім дотримуватися його.
№ 2-2. Не використовуйте “з” для простого розіменування покажчика. Тільки використовувати “з”, якщо індексація в масив.
№ 2-3. Намагайтеся уникати циклів “repeat” за допомогою циклів “while” замість цього.
№ 2-4. Намагайтеся уникати використання “Escape” виходить за рамки блоку “намагаються/відновити”.
№ 2-5. Уникайте “рядок” з на користь РАС. РАС є упакований масив Char з нижньою межею 1.
№ 2-6. Використання розширень Pascal/iХ, коли це можливо, якщо портативність не є головною метою.
№ 2-7. “Спробувати/відновити” побудувати в Pascal/iХ дуже корисно для лову помилок: як несподіваний і навмисне. (Спробуйте/відновити помилка лову механізм кілька схожий зловити/кидком знайти в деяких інших мовах)
№ 2-8. Використовуйте $ type_coercion ‘representation’ $. Ніколи не використовуйте несумісний рівень типу примусу.
№ 2-9. “Anyvar” тип параметра корисний. Розгляньте можливість використання його, коли ви хочете, щоб передавати різні типи змінних в одній процедурі.
№ 2-10. “Uncheckable_anyvar” варіант для процедури слід використовувати щоразу, коли оголошуються параметри “anyvar”, якщо ви спеціально не хочете Pascal/іХ передати прихований параметр “фактичний розмір”.
№ 2-11. При використанні “anyvar”, переконайтеся, що формальний тип параметра відповідає обмеженням вирівнювання очікуваних реальних типів. Тобто: якщо формальний параметр оголошений як “ціле”, то Pascal/іХ буде вважати, що всі адреси, що передаються в цей параметр кратні 4. Використання “напівкоксу” (або інший байтовим вирівнюванням типу) в якості формального типу параметра якщо ви хочете передати будь-які адреси безпечно.
№ 2-12. Опція процедура “default_parms” слід розглядати як засіб створення довгий фактичний параметр перераховує коротше (для звичайних випадків).
Коментар:
№ 2-1. Іноді я повертаюся швидкий загальний результат з “хорошою невдалої типу“, і докладний помилка в параметрі стану. Приклад:
function open_files (var status : hpe_status) : good_failed_type; ... if open_files (status) = failed then report_status (status);
№ 2-2. Паскаль забезпечує “з” заявою, що може, в деяких випадках, забезпечити компілятор з натяком про те, як оптимізувати інструкції випускаються для вашого коду. Крім того, “з” оператор може зберегти подальше друкувати.
Прикладом даремного “з” є:
var ptr : hpe_status_ptr_type; ... with ptr^ do begin hs_info := 0; hs_subsys := 0; end;
Це “марно”, тому що компілятор і оптимізатор, ймовірно, зробити так само добре роботу випускати оптимальний код, якби ми сказали:
ptr^.hs_info := 0; ptr^.hs_subsys := 0;
Зверніть увагу, що код взяв п’ять рядків за допомогою “з” заяву, і два рядки без нього.
І, нарешті, той факт, що “hs_info” і “hs_subsys” насправді є поля записи, на яку вказує “ptr” кілька затемнюється, коли “з” використовується.
Прикладом корисною “з” є:
var statuses : array [0..9] of hpe_status_type; ... with statuses [k] do {optimize hs_@ fields} begin hs_info := 0; hs_subsys := 0; end;
Я вважаю цей приклад “корисним”, тому що оптимізатор довелося б більше працювати, намагаючись скласти оптимальний код еквівалент, ні з твердженнями:
statuses [k].hs_info := 0; statuses [k].hs_subsys := 0;
У вищенаведених прикладах хочеться вирівняти “:=”s якомога правіше “:=” блоку операторів присвоювання. Раніше я часто робив це, коли у мене було чотири або більше аналогічних завдань в ряд.
Перевага підвищується читаність, тому що ми робимо це очевидно, що дані пов’язані між собою (через вирівняний “: =” s). Недолік полягає в тому, що проста команда QEDIT призначена для відображення, де поле “hs_info” змінюється (наприклад: LIST “hs_info: =”) зазнає невдачі. Я виявив, що можливість пошуку в обмін на присвоєнь переважили-це дані, пов’язані вигоди, для мене.
№ 2-3. Коли цикл “repeat” зустрічається, читач не знатиме, що умова завершення, поки ще багато рядків вихідного тексту програми не зчитуються. Це означає, що він або вона не зможе перевірити, чи правильно встановлені умови закінчення. А цикл “while” дозволяє уникнути цієї проблеми, так як умова закінчення чітко вказано у верхній частині циклу.
Цикл “repeat” зазвичай може бути легко змінений на цикл “while”:
до:
repeat begin ... end until buf [inx] = 0;
після:
done := false; while not done do begin ... done := (buf [inx] = 0); end; {while not done}
№ 2-4. “non-local escape” коштує тисячі циклів процесорного часу для виконання. Коротше кажучи, ніколи не плануєте використовувати цю конструкцію як звичайний спосіб повернення з процедури. Приклади:
поганий:
procedure do_work; {note: no status parameter!} ... if problem then escape (i_failed); ...
гарний:
procedure do_work (var status : hpe_status_type); label 999; ... if problem then begin status := my_failure_status; goto 999; {exit} end; ... 999: end {do_work proc};
№ 2-5. Рядки приховати величезну кількість повільних і кілька некоректної згенерованого компілятором коду. Об’єднання рядків, зокрема, може призвести до “витоку пам’яті”, де ваш процес запускається з купи простору. PAC є хаотичним, щоб впоратися, але набагато більш ефективним. Це продуктивності в порівнянні з естетикою розміняти.
№ 2-6. Pascal/іХ дуже корисний мову саме тому, що вона має велике тіло розширень для стандартного Pascal. Якщо ви уникаєте їх допомогою, ви б краще програмування в ANSI C або C ++.
№ 2-7. “try/recover” не гарантується, що всі помилки, що виникають в ній. Непередбачені помилки (наприклад.: Невірний індекс, поганий віртуальний адреса) посилатися на операційну систему під назвою рутинну trap_handler, яка буде “ходити” назад через ваші маркери стека шукає найостанніший “try/recover” блоку. Ця “прогулянка” може потерпіти невдачу, якщо ваш стек пошкоджений, і “try/recover” не буде знайдений. Якщо це станеться, і якщо відповідний обробник пастки (наприклад: XCODETRAP) ні озброєний, ваш процес буде перерваний.
№ 2-8. Тип примус є одним з кращих розширень в Pascal/іХ. Це забезпечує контрольований спосіб перевизначення перевірки типів Pascal. Директива $ type_coercion каже Pascal/іХ який рівень примусу типу ви хочете, щоб у вашій програмі. існує близько п’яти різних рівнів. Рівень я настійно рекомендую це уявлення. Цей рівень дозволяє вираз одного типу може бути примушений (розглядається як) інший тип, якщо, і тільки якщо ці два типи точно такий же розмір (в одиницях біт, а не байт).
Несумісний рівень говорить Pascal/іХ, що не повинно бути ніяких обмежень на тип примусу. Це призводить до цікавих помилок в програмах. Деякі збої MPE/iX можна простежити за допомогою такого роду примусу типу неправильно. Наступні приклади демонструє, як несумісні можуть приховати помилки.
припустимий:
var big_ptr : globalanyptr; my_address : integer; small_ptr : localanyptr;
поганий:
$type_coercion 'noncompatible'$ my_address := integer (big_ptr); {will get half of data!} my_address := integer (small_ptr); {will get 32 bit value}
гарний:
$type_coercion 'representation'$ my_address := integer (big_ptr); {will get syntax error} my_address := integer (small_ptr); {will get 32 bit value}
У поганому прикладі примус big_ptr призводить до установки my_address на верхні 32 біта big_ptr (тобто: id простору), мовчки втрачаючи нижні 32 біти адреси. У хорошому прикладі Pascal/іХ буде генерувати помилку синтаксису на спробі примусити 64-бітове вираз (big_ptr) в 32-бітове значення (ціле число).
№ 2-9. “Anyvar” є розширенням Pascal/іХ з “var”. Коли формальний параметр оголошений як “anyvar”, компілятор дозволяє будь-якої змінної бути передані в якості фактичного параметра. Без такої функції, і без об’єктно-орієнтованого Pascal, ви могли б не написати одну процедуру, яка буде дорівнює нулю (стирають) довільну змінну (див. нижче, наприклад).
За замовчуванням, якщо параметр оголошений як anyvar, Pascal/іХ буде проходити на адресу фактичного параметра * і * прихований ціле за значенням, яке записує розмір фактичного параметра. Наступний приклад показує, що передається для формального параметра, як: “anyvar foo : integer”, і впливають на “sizeof (foo)” в рамках процедури:
Тип фактичного параметра | Приховане поле розміру | ключове слово С++ (щось) |
char | 1 | 1 |
shortint | 2 | 2 |
integer | 4 | 4 |
longint | 8 | 8 |
real | 4 | 4 |
longreal | 8 | 8 |
hpe_status (see #1-24) | 4 | 4 |
packed array [1..80] of char | 80 | 80 |
№ 2-10. Я вживаю “anyvar” досить часто. Один із прикладів:
procedure zero_var (anyvar foo : char); {Purpose: zero every byte in the parameter} var bytes_left : integer; byte_ptr : ^char; begin $push, range off$ byte_ptr := addr (foo); bytes_left := sizeof (foo); {Note: gets actual size!} while bytes_left > 0 do begin {zero one byte} byte_ptr^ := chr (0); bytes_left := bytes_left - 1; byte_ptr := addtopointer (byte_ptr, 1); end; $pop$ {range} end {zero_var proc};
Зверніть увагу на коментар на $pop$ … що дозволяє мені згадати, які варіанти $pop$ нібито відновлення.
У Pascal/iX $push$ зберігає стан більшості опцій компілятора і $pop$ відновлює їх. Так, $push, range off$ … $pop$ тимчасово вимикає параметр “range”, потім відновлює його до попереднього стану… що значно відрізняється від просто включите його, коли “готово”!
Звичайно, Pascal/іХ дозволяє ще більш швидкий спосіб обнулення змінної, яка відбувається добре працювати з відзначаються параметрами anyvar. Весь код вище процедури (між “begin” і “end”) може бути замінений:
fast_fill (addr (foo), 0, sizeof (foo));
№ 2-12. У наступному прикладі, процедура, що більшість користувачів назвали б з “помилковим” у другому параметрі, показали корисність “default_parms”:
const print_with_cr = 0; print_without_cr = 1; procedure print_msg ( msg : str80; cr_or_no_cr : integer) option default_parms ( cr_or_no_cr := print_with_cr); var cctl_val : shortint; begin if cr_or_no_cr = print_without_cr then cctl_val := octal ('320') else cctl_val := 0; print (msg, -strlen (msg), cctl_val); {Note: ignoring errors from print intrinsic} end {print_msg}; ... print_msg ('Starting...', print_without_cr); print_msg ('Ending'); {does a CR/LF at end}
Зауважте, я б не використовував “default_parm” для параметра, який опускається менше, ніж приблизно у 75% випадків.
Два найбільших проблем із продуктивністю в програмах Pascal/іХ використовують вбудовану I/O, а також з використанням рядків.
Вказівки:
№ 3-1. Уникайте Pascal I/O. Натомість використовуйте intrinsic.
№ 3-2. Уникайте Pascal I/O. Натомість використовуйте intrinsic. Це варто повторити двічі!
№ 3-3. Уникайте рядків в областях критичної продуктивності.
№ 3-4. Відключайте перевірку діапазону ($range off$) тільки тоді, коли ви впевнені, що ваша програма працює правильно.
№ 3-5. Використовуйте оптимізатор Pascal/iX ($optimize on$).
Коментар:
№ 3-1. Якщо ви інкапсулювали ваші виклики I/O, то лежать в їх основі реалізація може бути легко змінена, щоб використовувати MPE вбудовані функції. Це також сприяє переносимості різних операційних систем і різних мов. Процедура “print_msg” в коментарі № 2-12 наведено приклад.
Друга причина для виключення Pascal I/O конструкції є ефективність. Підпрограми Pascal/iX I/O є вкрай неефективними. Даний посібник діє для більшості мов.
№ 3-3. Строкові вираження викликають компілятор випускати багато дзвінків на “помічник” підпрограм. Замість того, щоб виділяти одну робочу область в стеці, ці процедури виділення (і скасування розподілу) багато областей роботи на вашій купі. Це може бути дорогим активність, і може привести до втрати купи простору, і в кінцевому підсумку завершення процесу.
№ 3-5. Якщо ваша програма працює правильно неоптимізованими, і має проблему оптимізовану, тобто, ймовірно, один (або більше) неініціалізованих змінних. Другий найбільш поширеною проблемою є використання покажчиків таким чином, що оптимізатор не очікує (особливо при доступі до локальних змінних через покажчики).
(back to Table of Contents)
Переносимість програм, написаних на Pascal/іХ може бути підвищена з декількома методами. Майте на увазі, однак, що більшість інших реалізацій Pascal не так багатий, як Pascal/іХ. Delphi і Turbo Pascal (на IBM PC-сумісних) забезпечують деякі з тих же функцій, як Pascal/іХ.
№ 4-1. Уникайте цих розширень Pascal/іХ: розширюваний, тільки для читання, anyvar, варіант, uncheckable_anyvar, default_parms, globalanyptr.
№ 4-2. Уникайте більше $ директив (наприклад.: $еxtnaddr$).
№ 4-3. Використовуйте тип примусу тільки для типів однакових розмірів. (Хороший рада, навіть якщо ви ніколи не збираєтеся порту ваш код!) Більшість Pascal на базі ПК мають форму типу примусу. Вони можуть посилатися на нього як “типу лиття”.
№ 4-4. Уникайте “хрустів” записи. Навіть більшість C мови не мають функціональний еквівалент. Це включає в себе уникаючи “$HP3000_16$”.
№ 4-5. Інкапсулюють “дивні” конструкції, де це можливо.
№ 4-6. Інкапсулюють виклики I/O. Це не мова портативність питання так як операційна система і/або проблеми з продуктивністю.
№ 4-7. Тримайте ваші рядки вихідного коду коротких (72 символів або менше в кожному рядку).
№ 4-8. Використовуйте визначені користувачем типи, як “int16” і “int32” замість “ShortInt” або “integer”. Примітка: це надзвичайно важливо для програмістів C!
№ 4-9. Майте на увазі, що поля в записах можуть бути упаковані по-різному на різних машинах.
№ 4-10. Стандартний Pascal не дозволяє покажчиків, щоб вказати на змінні. (Вони можуть вказувати тільки на купу.)
Найкоротший підсумок цього паперу, ймовірно: ви можете судити про книгу по її обкладинці. Якщо програма виглядає красиво, це, ймовірно, приємно.