TDD люблять підхід до алгоритмічних задач


10

Мені один не вдався в алгоритмічному тесті з Codility, тому що я намагався знайти краще рішення, і врешті-решт у мене нічого не було.

Тож змусило мене задуматися, чи можу я використовувати підхід, подібний до TDD? Тобто якщо я зазвичай можу розробити рішення поступово подібним чином?

Якби я писав алгоритм сортування, я міг би перейти від стандартного Bubblesort до двостороннього бульбашка, але тоді щось більш просунуте, як Quicksort, було б «квантовим стрибком», але, принаймні, я мав би тестові дані, які я можу легко перевірити.

Інші поради щодо таких тестів? Одне, що я би зробив наступного разу - це використовувати більше методів / функцій, ніж внутрішні петлі. Наприклад, при сортуванні вам часто потрібен своп. Якби це метод, мені просто потрібно було б змінити код виклику. Я міг би навіть мати більш просунуті рішення як похідні класи.

Під "алгоритмічними" проти "нормальними" проблемами я маю на увазі проблеми, де важлива складність у часі. Тож замість того, щоб здавати більше тестів, як у TDD, ви зробите це "краще".

Під "схожим на TDD" я маю на увазі:

  1. Напишіть відносно автоматичний тест, щоб заощадити час на збільшення ручних тестів.
  2. Поступовий розвиток.
  3. Регресійне тестування, здатність визначати, чи порушується код або принаймні, якщо функціональність змінюється між кроками.

Я думаю, що це потрібно легко зрозуміти, якщо порівнювати

  1. Написання сортування оболонки безпосередньо
  2. Стрибки з пухирця до швидкості (Total rewrite)
  3. Поступово переходить від одностороннього сортування міхура до сортування оболонки (Якщо можливо).

Що ви маєте на увазі під "схожим на TDD"? Ви, очевидно, можете спробувати використати TDD для розробки функції сортування, а потім скористатися одиничними тестами для перевірки функції, яка все ще працює, коли ви заміните алгоритм сортування на більш ефективний, але це здається, що у вас є інше питання?
Док Браун

"Поступово" :-) - Дивіться останнє речення, додане "Так замість цього ..."
Олав

2
Впевнені, що ви можете спробувати вирішити багато проблем із робочим (але не дуже ефективним) рішенням, а потім вдосконалити його. Це жодним чином не обмежується алгоритмічними проблемами або проблемами програмування, і це не має багато спільного з TDD. Чи відповідає це на ваше запитання?
Док Браун

@DocBrown Ні - див. Приклад Bubblesort / Quicksort. TDD "працює" добре, оскільки поступовий підхід добре працює для багатьох типів проблем. Алгоритмічні проблеми можуть бути різними.
Олав

Отже, ви мали на увазі "можна вирішувати питання проектування алгоритму поступово" (так само як TDD - поступовий підхід), а не "TDD", правда? Будь ласка, поясніть.
Док Браун

Відповіді:


9

Дивіться також спробу Рона Джеффріса створити розв'язувач судоку з TDD , який, на жаль, не спрацював.

Алгоритм вимагає значного розуміння принципів проектування алгоритму. З цими принципами дійсно можна поступово йти з планом, як це робив Пітер Норвіг .

Насправді, для алгоритмів, що вимагають нетривіальних зусиль проектування, майже завжди зусилля мають приросту. Але кожен "приріст", який є крихітним в очах дизайнера алгоритмів, виглядає як квантовий стрибок (запозичити вашу фразу) людині, яка не мала такої ж експертизи чи знань з цією сімейством алгоритмів.

Ось чому основна освіта теорії CS у поєднанні з безліччю практики програмування алгоритмів є однаково важливою. Знаючи, що певна "техніка" (невеликі будівельні блоки алгоритмів) існує довгий шлях до здійснення цих поступових квантових стрибків.


Однак є кілька важливих відмінностей між поступовим прогресом в алгоритмах і TDD.

Одна з різниць згадувала JeffO : Тест, що підтверджує правильність вихідних даних, є окремим від тесту, який стверджує ефективність між різною реалізацією одного і того ж алгоритму (або різними алгоритмами, що намагаються дати одне і те ж рішення).

У TDD додається нова вимога у вигляді тесту, і цей тест спочатку не повинен проходити (червоний). Тоді вимогу виконують (зелений). Нарешті код відновлюється.

У розробці алгоритму вимога зазвичай не змінюється. Тест на перевірку правильності результатів пишеться спочатку або незабаром після завершення чергової (дуже впевненої, але повільної) реалізації алгоритму. Цей тест на правильність даних рідко змінюється; ніхто не змінює його на збій (червоний) як частина обряду TDD.

Однак у цьому аспекті аналіз даних суттєво відрізняється від розробки алгоритму, оскільки вимоги до аналізу даних (як вхідних наборів, так і очікуваних результатів) визначаються лише в людському розумінні. Таким чином, вимоги часто змінюються на технічному рівні. Ця швидка зміна ставить аналіз даних десь між розробкою алгоритму і загальною розробкою програмного забезпечення - хоча все ще важкий алгоритм, вимоги також змінюються "занадто швидко" на смак будь-якого програміста.

Якщо вимога змінюється, вона зазвичай вимагає іншого алгоритму.

У розробці алгоритму зміна (підтягування) тесту порівняння продуктивності на збій (червоний) є нерозумною - це не дає тобі уявлення про потенційні зміни в алгоритмі, які б покращили продуктивність.

Тому в розробці алгоритму і тест на правильність, і тест на продуктивність не є тестами TDD. Натомість обидва є регресійними тестами . Зокрема, тест регресії на правильність заважає вносити зміни в алгоритм, які порушують його правильність; тест на ефективність заважає вносити зміни в алгоритм, які змусять його працювати повільніше.

Ви все ще можете включити TDD як особистий стиль роботи, за винятком того, що ритуал "червоний - зелений - рефактор" не є строго необхідним або особливо корисним для продуманого процесу розробки алгоритму.

Я б стверджував, що вдосконалення алгоритму насправді є результатом здійснення випадкових (не обов'язково правильних) перестановок на діаграми потоку даних поточного алгоритму або їх змішування та співставлення між відомими раніше реалізаціями.


TDD використовується, коли є кілька вимог, які можна поступово додавати до вашого тестового набору.

Крім того, якщо ваш алгоритм керується даними, кожен фрагмент тестових даних / тестових випадків можна додавати поступово. TDD також буде корисна. З цієї причини "TDD-подібний" підхід "додавання нових тестових даних - поліпшення коду, щоб він обробляв ці дані правильно - рефактор" також буде працювати для роботи з аналітикою відкритого типу, в якій цілі алгоритмів описані в людині -центричні слова та його міра успіху також судяться у визначених людиною термінах.

Це спрямовано на те, щоб навчити спосіб зробити його менш непосильним, ніж намагатися задовольнити всі (десятки чи сотні) вимог за одну спробу. Іншими словами, TDD увімкнено, коли ви можете продиктувати, що певні вимоги або цілі розтягування можна тимчасово ігнорувати, коли ви реалізуєте деякі ранні проекти вашого рішення.

TDD не замінює інформатику. Це психологічна милиця, яка допомагає програмістам подолати шок від необхідності задоволення багатьох вимог одночасно.

Але якщо у вас вже є одна реалізація, яка дає правильний результат, TDD вважає свою мету виконаною і код готовий для передачі (рефакторингу або іншому користувачеві-програмісту). У певному сенсі він спонукає вас не передчасно оптимізувати свій код, об'єктивно подаючи сигнал про те, що код "досить хороший" (пройти всі тести на правильність).


У TDD також є акцент на "мікропотребах" (або прихованих якостях). Наприклад, перевірка параметрів, твердження, викид та обробка виключень тощо. TDD допомагає забезпечити правильність контурів виконання, які часто не виконуються у звичайному процесі виконання програмного забезпечення.

Окремі типи коду алгоритму містять і ці речі; вони піддаються TDD. Але оскільки загальний робочий процес алгоритму не є TDD, такі тести (на перевірку параметрів, твердження та викид та обробку винятків), як правило, пишуть після того, як код реалізації вже був (принаймні частково) написаний.


Що означають перші два цитовані слова у вашому дописі («дядько Боб»)?
Роберт Харві

@RobertHarvey За словами дядька Боба, TDD може бути використаний для пошуку алгоритму. За іншим світильником, він не працює. Я подумав, що слід згадати і те, і інше (тобто, коли хтось згадає про один приклад, один також зобов'язаний згадати інший приклад), щоб люди отримували збалансовану інформацію про позитивні та негативні приклади.
rwong

ДОБРЕ. Але ти розумієш мою плутанину? Ваш перший абзац, як видається, цитує когось, що вимовляє слова "дядько Боб". Хто це говорить?
Роберт Харві

Замовлення @RobertHarvey виконано.
rwong

2

Для вашої проблеми у вас є два тести:

  1. Тест, щоб переконатися, що алгоритм все ще є точним. наприклад, чи правильно відсортовано?
  2. Тест порівняння продуктивності - покращилась продуктивність. Це може стати складним, тому це допомагає запустити тест на одній машині, однакових даних та обмежити використання будь-яких інших ресурсів. Спеціальна машина допомагає.

Що робити тестування чи повне покриття тесту, є дискусійним, але я вважаю, що складні частини вашої програми, які потребують точної настройки (змінити багато), є ідеальними кандидатами для автоматизованого тестування. Ці частини заявки зазвичай ідентифікуються дуже рано. Використання TDD-підходу з цією деталлю мало б сенс.

Уміння вирішувати складні проблеми не повинно перешкоджати небажанням спробувати різні підходи. Автоматизований тест повинен допомогти у цій галузі. Принаймні, ти будеш знати, що не робить це гірше.


1

Тож замість того, щоб здавати більше тестів, як у TDD, ви зробите це "краще".

Типу.

Ви можете протестувати на час виконання та складність. Тести виконання потрібно буде трохи прощати, щоб дозволити суперечки щодо системних ресурсів. Але, багатьма мовами, ви також можете змінювати або вводити методи та рахувати фактичні виклики функцій. І якщо ви впевнені, що ваш поточний алгоритм є недостатньо оптимальним, ви можете запровадити тест, який просто вимагає sort()викликати compare()менше разів, ніж наївне виконання.

Або ви можете порівняти sort()за двома наборами і переконатися, що вони масштабуються у compare()викликах за вашою цільовою складністю (або, якщо ви очікуєте певної невідповідності).

І якщо ви теоретично можете довести, що набір розміру Nповинен вимагати суворої кількості N*log(N)порівнянь, можливо, буде розумним обмежити ваші вже працюючі sort()до N*log(N)викликів compare()...

Однак ...

Виконання вимог щодо продуктивності чи складності не гарантує, що основна реалізація є {AlgorithmX} . І я б заперечував, що це нормально нормально. Це не має значення для того, який алгоритм використовується, якщо ви потрапили на реалізацію відповідає вашим вимогам, включаючи будь-які важливі вимоги щодо складності, продуктивності та ресурсів.

Але, якщо ви хочете, щоб певний алгоритм був використаний, вам потрібно буде бути більш втомливим і отримати набагато більше в глибині. Такі речі ..

  • Забезпечення того , що буде здійснено саме очікувану кількість дзвінків compare()та swap()(або що завгодно)
  • Обгортання всіх основних функцій / методів та забезпечення, передбачуваним набором даних, забезпечення того, що дзвінки відбуваються в точно очікуваному порядку
  • Вивчення робочого стану після кожного з N кроків, щоб переконатися, що він змінився точно так, як очікувалося

Але знову ж таки, якщо ви намагаєтеся переконатися, що {AlgorithmX} використовується зокрема, є певні особливості {AlgorithmX}, які вам важливі для перевірки, ніж те, чи було {AlgorithmX} насправді використано наприкінці ...


Також майте на увазі, TDD не заперечує необхідності дослідження, мислення або планування в розробці програмного забезпечення. Це також не заперечує необхідності мозкового штурму та експериментів, навіть якщо ви не зможете легко стверджувати про свій гуглінг та дошку у вашому автоматизованому наборі тестів.


Відмінний момент щодо можливості встановлення меж за кількістю операцій. Я заперечую, що це сила (глузування, заглушки та шпигуни), те, що можна використовувати в TDD, що також можна використовувати в інших видах тестів.
rwong

Я не бачу великого сенсу писати тести перед кодом у тестовому сценарії. Зазвичай це останні критерії після виконання функціональних критеріїв. Іноді ви можете робити випадкові цифри перший і найгірший випадок після, але все ж тест на написання зайняв би багато часу порівняно з деякими розумними роздруківками. (Маючи певну статистику, ви, ймовірно, можете написати якийсь загальний код, але не під час тесту). У реальному світі, я думаю, ви хотіли б знати, якщо продуктивність раптово знижується в певних випадках.
Олав

Якщо ви подивитесь на codility.com/programmers/task/stone_wall, ви б знали, що у вас складність більше N, за винятком особливих випадків, коли вам доводиться працювати, наприклад, за дуже довгі проміжки.
Олав

@Olav "тест із написання тексту зайняв би тривалий час порівняно з деякими розумними роздруківками" ... зробити один раз ... е-е .. можливо , але теж дуже дискусійно. Зробити це неодноразово при кожній збірці? ... Точно ні.
svidgen

@Olav "У реальному світі я думаю, ви хотіли б знати, якщо продуктивність раптово знижується в певних випадках". У службах наживо ви можете використовувати такі, як New Relic, щоб контролювати загальну продуктивність - не лише певні методи. І в ідеалі ваші тести підкажуть, коли критичні для продуктивності модулі та методи не виправдають очікувань перед розгортанням.
svidgen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.