Як працює диференційне виконання?


83

Я бачив кілька згадувань про це в Stack Overflow, але вдивляючись у Вікіпедію (відповідну сторінку з тих пір було видалено) і в демонстраційному демонстраційному діалоговому вікні MFC нічого не допомогло мені просвітлити. Хтось може пояснити це? Вивчення принципово іншої концепції звучить приємно.


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


3
Дякуємо за інтерес, Брайане. Для мене цікаво, що щось просте здається невтішним. Для мене найкрасивіші речі - це просто. Піклуватися.
Mike Dunlavey

1
Думаю, я пропускаю щось важливе. Зараз я думаю: "Це просто". Якби я насправді це розумів, я думаю, я б подумав: "Це просто. І справді дивно і корисно".
Брайан

6
... Я все ще бачу людей, які представляють MVC, ніби це найбільше, і я вважаю, що я волію піти на пенсію, ніж робити це знову.
Mike Dunlavey

1
... для "скасування" ви серіалізуєте / десеріалізуєте дані і відкручуєте файл, який є XOR з двох, який здебільшого дорівнює нулю, так легко стискається. Використовуйте це для відновлення попередніх даних. Тепер узагальнюємо на довільну структуру даних.
Mike Dunlavey

1
Не хочу додавати до свого робочого навантаження, @MikeDunlavey, але якщо ви його пропустили, Source Forge впав з ласки завдяки сумнівним бізнес-практикам. Github.com - це місце, де сьогодні висять круті діти. У них є справді приємний клієнт Windows для W7 на desktop.github.com
професор Фалькен,

Відповіді:


95

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

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

Пояснення @ windfinder відрізняється від мого, і це нормально. За допомогою цієї техніки непросто обернути голову, і мені знадобилося 20 років (не зважаючи на це), щоб знайти пояснення, які працюють. Дозвольте мені зробити ще один постріл тут:

  • Що це?

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

А тепер уявіть, як два комп’ютери виконують один і той самий код між собою і можуть порівнювати нотатки. Комп’ютер 1 працює із вхідними даними A, а Комп’ютер 2 - із вхідними даними B. Вони виконують покроково поруч. Якщо вони приходять до умовного висловлювання типу IF (тест) .... ENDIF, і якщо у них різниця в думках щодо того, чи є тест істинним, то той, хто говорить тест, якщо false, переходить до ENDIF і чекає його сестру наздогнати. (Ось чому код структурований, тому ми знаємо, що сестра врешті-решт дістанеться до ENDIF.)

Оскільки два комп'ютери можуть спілкуватися між собою, вони можуть порівнювати нотатки та давати детальне пояснення того, як відрізняються два набори вхідних даних та історії виконання.

Звичайно, при диференціальному виконанні (DE) це робиться з одним комп'ютером, імітуючи два.

ЗАРАЗ, припустимо, у вас є лише один набір вхідних даних, але ви хочете побачити, як вони змінювались час від часу до часу 2. Припустимо, що програма, яку ви виконуєте, є серіалізатором / десериалізатором. Під час виконання ви обидва серіалізуєте (виписуєте) поточні дані та десеріалізуєте (читаєте) минулі дані (які були записані востаннє, коли ви це робили). Тепер ви можете легко зрозуміти, які відмінності існують між тим, які дані були минулого разу, і якими вони є цього разу.

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

  • Для чого це добре?

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

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

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

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

У ті часи мені довелося турбуватися про втручання між зоровими об’єктами, щоб стирання одного не зашкодило іншим. Однак зараз я використовую техніку з елементами керування Windows, і я дозволяю Windows піклуватися про проблеми з візуалізацією.

То чого він досягає? Це означає, що я можу побудувати діалогове вікно, написавши процедуру розфарбовування елементів керування, і мені не доведеться турбуватися про те, щоб насправді запам’ятати об’єкти керування або мати справу з поступовим їх оновленням, або змусити їх з’являтися / зникати / рухатись, як того вимагають умови. Результатом є набагато менший та простіший вихідний код діалогового вікна приблизно на порядок, і такі речі, як динамічне розміщення або зміна кількості елементів керування або наявність масивів або сіток елементів керування, є тривіальними. Крім того, такий елемент керування, як поле Редагування, може бути тривіально прив'язаний до даних програми, яку він редагує, і він завжди буде доказово правильним, і мені ніколи не доведеться мати справу з його подіями. Введення поля редагування для змінної рядка програми є однорядковим редагуванням.

  • Чому це важко зрозуміти?

Найважче мені пояснити, що це вимагає іншої думки про програмне забезпечення. Програмісти настільки міцно прив'язані до об'єктного погляду програмного забезпечення, що вони хочуть знати, що це за об'єкти, які класи, як вони "будують" дисплей і як вони обробляють події, що для цього потрібна вишня бомба, щоб вирвати їх з неї. Я намагаюся донести, що насправді важливо те, що вам потрібно сказати?Уявіть, що ви створюєте специфічну для домену мову (DSL), де все, що вам потрібно зробити, це сказати їй "я хочу відредагувати змінну A тут, змінну B там, а змінну C там", і це магічним чином подбало б про вас . Наприклад, у Win32 існує така "мова ресурсів" для визначення діалогів. Це цілком хороший DSL, за винятком того, що він заходить недостатньо далеко. Він не "живе в" основній процедурній мові, не обробляє для вас події, не містить циклів / умов / підпрограм. Але це добре, і "Динамічні діалоги" намагаються закінчити роботу.

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

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

Вибачте, що я такий затятий. Якщо у мене немає сенсу, я був би вдячний, якщо б ви вказали на це, і я можу спробувати це виправити.

Додано:

У Java Swing є приклад програми під назвою TextInputDemo. Це статичне діалогове вікно, що займає 270 рядків (не враховуючи список із 50 станів). У динамічних діалогових вікнах (у MFC) це близько 60 рядків:

#define NSTATE (sizeof(states)/sizeof(states[0]))
CString sStreet;
CString sCity;
int iState;
CString sZip;
CString sWholeAddress;

void SetAddress(){
    CString sTemp = states[iState];
    int len = sTemp.GetLength();
    sWholeAddress.Format("%s\r\n%s %s %s", sStreet, sCity, sTemp.Mid(len-3, 2), sZip);
}

void ClearAddress(){
    sWholeAddress = sStreet = sCity = sZip = "";
}

void CDDDemoDlg::deContentsTextInputDemo(){
    int gy0 = P(gy);
    P(www = Width()*2/3);
    deStartHorizontal();
    deStatic(100, 20, "Street Address:");
    deEdit(www - 100, 20, &sStreet);
    deEndHorizontal(20);
    deStartHorizontal();
    deStatic(100, 20, "City:");
    deEdit(www - 100, 20, &sCity);
    deEndHorizontal(20);
    deStartHorizontal();
    deStatic(100, 20, "State:");
    deStatic(www - 100 - 20 - 20, 20, states[iState]);
    if (deButton(20, 20, "<")){
        iState = (iState+NSTATE - 1) % NSTATE;
        DD_THROW;
    }
    if (deButton(20, 20, ">")){
        iState = (iState+NSTATE + 1) % NSTATE;
        DD_THROW;
    }
    deEndHorizontal(20);
    deStartHorizontal();
    deStatic(100, 20, "Zip:");
    deEdit(www - 100, 20, &sZip);
    deEndHorizontal(20);
    deStartHorizontal();
    P(gx += 100);
    if (deButton((www-100)/2, 20, "Set Address")){
        SetAddress();
        DD_THROW;
    }
    if (deButton((www-100)/2, 20, "Clear Address")){
        ClearAddress();
        DD_THROW;
    }
    deEndHorizontal(20);
    P((gx = www, gy = gy0));
    deStatic(P(Width() - gx), 20*5, (sWholeAddress != "" ? sWholeAddress : "No address set."));
}

Додано:

Ось приклад коду для редагування масиву пацієнтів лікарні приблизно в 40 рядків коду. Рядки 1-6 визначають "базу даних". Рядки 10-23 визначають загальний зміст інтерфейсу користувача. Рядки 30-48 визначають елементи керування для редагування запису окремого пацієнта. Зверніть увагу, що форма програми майже не зауважує подій у часі, ніби все, що їй потрібно було зробити, це створити дисплей один раз. Потім, якщо об'єкти додаються або видаляються або відбуваються інші структурні зміни, вони просто виконуються повторно, ніби вони відтворюються з нуля, за винятком того, що DE викликає натомість поступове оновлення. Перевага полягає в тому, що вам, програмісту, не потрібно приділяти ніякої уваги або писати будь-який код, щоб виконувати поступові оновлення інтерфейсу, і вони гарантовано правильні. Може здатися, що це повторне виконання буде проблемою продуктивності, але це не так,

1  class Patient {public:
2    String name;
3    double age;
4    bool smoker; // smoker only relevant if age >= 50
5  };
6  vector< Patient* > patients;

10 void deContents(){ int i;
11   // First, have a label
12   deLabel(200, 20, “Patient name, age, smoker:”);
13   // For each patient, have a row of controls
14   FOR(i=0, i<patients.Count(), i++)
15     deEditOnePatient( P( patients[i] ) );
16   END
17   // Have a button to add a patient
18   if (deButton(50, 20, “Add”)){
19     // When the button is clicked add the patient
20     patients.Add(new Patient);
21     DD_THROW;
22   }
23 }

30 void deEditOnePatient(Patient* p){
31   // Determine field widths
32   int w = (Width()-50)/3;
33   // Controls are laid out horizontally
34   deStartHorizontal();
35     // Have a button to remove this patient
36     if (deButton(50, 20, “Remove”)){
37       patients.Remove(p);
37       DD_THROW;
39     }
40     // Edit fields for name and age
41     deEdit(w, 20, P(&p->name));
42     deEdit(w, 20, P(&p->age));
43     // If age >= 50 have a checkbox for smoker boolean
44     IF(p->age >= 50)
45       deCheckBox(w, 20, “Smoker?”, P(&p->smoker));
46     END
47   deEndHorizontal(20);
48 }

Додано: Брайан задав гарне запитання, і я вважав, що відповідь належить у основному тексті тут:

@Mike: Мені незрозуміло, що насправді робить вислів "if (deButton (50, 20," Add ”)) {". Що робить функція deButton? Крім того, ваші цикли FOR / END використовують якийсь макрос чи щось інше? - Брайан.

@Brian: Так, оператори FOR / END та IF - це макроси. Проект SourceForge має повну реалізацію. deButton підтримує кнопку управління. Коли відбувається будь-яка дія введення користувачем, код запускається в режимі "події управління", в якому deButton виявляє, що його було натиснуто, і означає, що він натиснув, повернувши TRUE. Таким чином, "if (deButton (...)) {... код дії ...} - це спосіб прикріплення коду дії до кнопки без необхідності створювати закриття або писати обробник події. DD_THROW є спосіб завершення пропуску, коли виконується дія, оскільки дія може мати змінені дані програми, тому неприпустимо продовжувати проходження "контрольної події" через процедуру. Якщо ви порівняєте це із написанням обробників подій, це заощадить вам написання таких, і це дозволяє вам мати будь-яку кількість елементів керування.

Додано: Вибачте, я повинен пояснити, що я маю на увазі під словом "підтримує". Коли процедура виконується вперше (у режимі SHOW), deButton створює елемент керування кнопкою та запам'ятовує його ідентифікатор у FIFO. На наступних передачах (в режимі UPDATE) deButton отримує ідентифікатор з FIFO, модифікує його, якщо потрібно, і повертає в FIFO. У режимі ERASE він зчитує його з FIFO, знищує і не повертає назад, тим самим «збираючи сміття». Отже, виклик deButton управляє всім часом керування, зберігаючи його у відповідності з даними додатків, саме тому я кажу, що він його «підтримує».

Четвертий режим - ПОДІЯ (або КОНТРОЛЬ). Коли користувач вводить символ або натискає кнопку, ця подія ловиться і записується, а потім процедура deContents виконується в режимі EVENT. deButton отримує ідентифікатор свого елемента керування кнопкою з FIFO і запитує, чи це елемент управління, на який клацнули. Якщо воно було, воно повертає TRUE, щоб можна було виконати код дії. Якщо ні, він просто повертає FALSE. З іншого боку, deEdit(..., &myStringVar)виявляє, чи подія була призначена для неї, і якщо це так, передає її елементу керування редагуванням, а потім копіює вміст елемента керування редагуванням у myStringVar. Між цією та звичайною обробкою UPDATE myStringVar завжди дорівнює вмісту елемента керування редагуванням. Ось як робиться "прив'язка". Ця ж ідея стосується смуг прокрутки, списків, комбінованих вікон та будь-якого елемента керування, що дозволяє редагувати дані програми.

Ось посилання на мою редакцію у Вікіпедії: http://en.wikipedia.org/wiki/User:MikeDunlavey/Difex_Article


4
І вибачте, що переслідуєте вас відповідями, але якщо я це правильно розумію, ви в основному переносите свої розрахунки все ближче і ближче до процесора і подалі від вихідного обладнання. Це неймовірне розуміння, оскільки ми вкладаємо багато запасів у ідею, що програмуючи об’єкти та змінні, їх досить легко перевести в найкращий машинний код, щоб досягти того самого результату, що, звичайно, зовсім не так! Хоча ми можемо оптимізувати код під час компіляції, неможливо оптимізувати дії, що залежать від часу. Відмовтеся від залежності від часу і змусіть примітивів виконати свою роботу !
sova

2
@Joey: Тепер, коли ви це вже згадуєте, ідея структури управління, яка запускає FIFO, і паралельних ко-підпрограм, що запускають чергу роботи, має багато спільного.
Mike Dunlavey

2
Цікаво, наскільки Differential Execution наближений до підходу, що використовується бібліотекою response.js.
Брайан

2
@Brian: При обробці інформації, response.js використовує функцію diff для надсилання покрокових оновлень у браузер. Я не можу сказати, чи функція diff насправді настільки здатна, як диференційне виконання. Як він може обробляти довільні зміни, і він претендує на спрощення прив'язки. Чи це робиться з такою ж мірою, я не знаю. У будь-якому випадку, я думаю, що це на правильному шляху. Кілька відео тут.
Mike Dunlavey

2
@MikeDunlavey, я пишу свої інструменти з комбінацією OpenGL / IMGUI та реактивного програмування над шарами Model, Model-View та View. Я більше ніколи не повернусь до старого стилю. Дякуємо за посилання на ваші відео.
Ктуту

13

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

Основний потік такий:

Start loop:
for each element in the datastructure: 
    if element has changed from oldDatastructure:
        copy element from datastructure to oldDatastructure
        execute corresponding subroutine (display the new button in your GUI, for example)
End loop:
Allow the states of the datastructure to change (such as having the user do some input in the GUI)

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


12

Подумайте, як працює монітор:

Він оновлюється на частоті 60 Гц - 60 разів на секунду. Мерехтіння флікер флікера 60 разів, але ваші очі повільно і не може реально сказати. Монітор показує все, що є у вихідному буфері; він просто перетягує ці дані кожні 1/60 секунди незалежно від того, що ви робите.

Чому б ви хотіли, щоб ваша програма оновлювала весь буфер 60 разів на секунду, якщо зображення не повинно змінюватися так часто? Що робити, якщо ви змінили лише один піксель зображення, вам слід переписати весь буфер?


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

Монітор відокремлений від комп'ютера та логіки (програм). Він зчитує з вихідного буфера з якою швидкістю оновлює екран. Ми хочемо, щоб наш комп’ютер припинив синхронізацію та перемальовування без потреби. Ми можемо вирішити це, змінивши спосіб роботи з буфером, що можна зробити різними способами. Його техніка реалізує чергу FIFO, яка затримується - вона зберігає те, що ми щойно надіслали в буфер. Затримана черга FIFO не містить піксельних даних, вона містить "примітиви фігури" (які можуть бути пікселями у вашому додатку, але це також можуть бути лінії, прямокутники, прості для малювання речі, оскільки це просто фігури, відсутність зайвих даних дозволено).

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

Я оцінюю це двома різними способами :

Перший: Майк Данлавей використовує розширення з умовною заявою. Черга FIFO містить багато інформації ("попередній стан" або поточний вміст на моніторі чи пристрої опитування на основі часу). До цього потрібно лише додати стан, який ви хочете відобразити на екрані наступним чином.

Умовний біт додається до кожного слота, який може містити примітив у черзі FIFO.

0 means erase
1 means draw

Однак ми маємо попередній стан:

Was 0, now 0: don't do anything;
Was 0, now 1: add it to the buffer (draw it);
Was 1, now 1: don't do anything;
Was 1, now 0: erase it from the buffer (erase it from the screen);

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

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

Метод перемальовування всього екрану неймовірно дорогий, а є й інші програми, де це розуміння надзвичайно цінне.

Ми ніколи не «рухаємо» об’єкти по екрану. "Переміщення" - це дорога операція, якщо ми збираємося імітувати фізичну дію "переміщення", коли розробляємо код для чогось на зразок монітора комп'ютера. Натомість об’єкти в основному просто мерехтять і вимикаються на моніторі. Кожного разу, коли об’єкт рухається, це новий набір примітивів, а старий набір примітивів мерехтить.

Кожного разу, коли монітор витягує з буфера, ми маємо записи, які виглядають так

Draw bit    primitive_description
0           Rect(0,0,5,5);
1           Circ(0,0,2);
1           Line(0,1,2,5);

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

Скажімо, у нас є список усіх можливих графічних примітивів, які здатна генерувати наша програма, і що ми пов’язуємо кожен примітив із набором умовних операторів

if (iWantGreenCircle && iWantBigCircle && iWantOutlineOnMyCircle) ...

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

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

Тепер для будь-якого стану програми ми можемо просто оцінити всі умови та вивести на екран блискавично! (Ми знаємо наші примітиви фігури та залежні від них оператори if).

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

Урок, наскільки я розумію, полягає у розподілі праці таким чином, щоб ви давали кожній частині системи (не обов’язково лише комп’ютеру та монітору) щось, що вона може зробити добре. "Комп'ютерне мислення" можна зробити з точки зору таких понять, як предмети ... Комп'ютерний мозок із задоволенням спробує і продумає все це для вас, але ви можете значно спростити завдання, якщо зможете дати комп'ютеру подумати умови data_update та умовні_evals. Наші людські абстракції понять у коді є ідеалістичними, а у випадку з внутрішньою програмою методи введення трохи надмірно ідеалістичні. Коли все, що вам потрібно - це результат (масив пікселів з правильними значеннями кольору), і у вас є машина, яка може легко зробити виплюньте масив, який стає великим кожні 1/60 секунди, спробуйте усунути якомога більше квіткових думок із мозку комп’ютера, щоб ви могли зосередитись на тому, що ви насправді хочете: синхронізувати свої графічні оновлення з вашими (швидкими) входами та природна поведінка монітора.

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


2
++ Я ціную ваш погляд на це. Для мене спочатку це була спроба робити описувані програмою дисплеї на повільних пристроях (думаю, віддалені текстові термінали на 9600 бодів), де в основному це робило б автоматизовану різницю та передавала мінімальні оновлення. Тоді на мене натиснули, чому б просто не кодувати це грубою силою. Відповідь: тому що якщо форма поверхні коду виглядає так, ніби це проста фарба , вона коротша, майже не містить помилок, тому робиться за частку часу розробки. (Це те, що я вважаю перевагою DSL.)
Майк Данлавей

... Звільнені зусилля щодо розробки можуть бути інвестовані в більш складні та динамічні дисплеї, які користувачі знайдуть чуйними та приємними. Таким чином, ви отримуєте більше інтерфейсу для грошей розробника.
Mike Dunlavey

... Приклад: цей додаток приблизно 10 років тому: pharsight.com/products/prod_pts_using_dme.php
Mike Dunlavey

1
Це дало мені зрозуміти ... коли ви говорили про комп’ютерні ігри. Насправді, багато ігор кодуються так, як Майк робить користувальницький інтерфейс. Список оновлень, який переглядається з кожним кадром.
Проф. Фалькен,

Начебто пов’язаний приклад з деяким із того, що ви сказали, виявляє, чи утримується клавіша / кнопка, або щойно її відпустили. Легко зрозуміти, натиснута кнопка чи ні. Це значення true / false з вашого API низького рівня. Щоб знати, чи утримується клавіша, потрібно знати, в якому стані вона була раніше. Якщо вона від 0-> 1, її просто натиснули. якщо це 1-> 1, він утримується, якщо це від 1-> 0, то ви щойно звільнили.
Джошуа Хеджес

3

Я вважаю, що ця концепція дуже схожа на державні машини класичної цифрової електроніки. Особливо ті, які пам’ятають свої попередні результати.

Машина, наступний вихід якої залежить від поточного входу та попереднього виходу відповідно до (ВАШ КОД ТУТ). Цей поточний вхід - це не що інше, як попередній вихід + (КОРИСТУВАЧ, ВЗАЄМОДІЮЙТЕ ТУТ).

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

Потім з’єднайте машини на своїй поверхні, дозвольте їм ділитися нотатками згідно (ВАШ КОД ТУТ), і тепер ми робимо це розумним. Це стане інтерактивною обчислювальною системою.

Отже, вам просто потрібно надати свою логіку у двох місцях у вищенаведеній моделі; про решту піклується сама конструкція машини. Ось що добре в цьому.


1
Здається, я пам’ятаю, що я мав на увазі апаратну модель, коли це мені спало на думку.
Mike Dunlavey
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.