이런, 브라이언, 당신의 질문을 더 일찍 봤으면 좋았을 텐데요. (좋든 나쁘 든) 거의 나의 "발명품"이기 때문에 도움을 드릴 수있을 것입니다.
삽입 됨 : 내가 할 수있는 가장 짧은 설명은 정상적인 실행이 공을 공중에 던지고 잡는 것과 같으면 차등 실행은 저글링과 같다는 것입니다.
@windfinder의 설명이 나와 다르며 괜찮습니다. 이 기술은 머리를 감싸는 것이 쉽지 않으며, 작동하는 설명을 찾는 데 약 20 년이 걸렸습니다. 여기에 또 다른 기회를 드리겠습니다.
우리 모두는 컴퓨터가 프로그램을 따라 가며 입력 데이터를 기반으로 조건부 분기를 수행하고 작업을 수행하는 간단한 아이디어를 이해합니다. (단순한 구조화 된 goto-less, return-less 코드 만 처리한다고 가정합니다.)이 코드에는 일련의 명령문, 기본 구조화 된 조건, 단순 루프 및 서브 루틴 호출이 포함됩니다. (지금은 값을 반환하는 함수는 잊어 버리세요.)
이제 동일한 코드를 서로 잠금 단계에서 실행하고 메모를 비교할 수있는 두 대의 컴퓨터를 상상해보십시오. 컴퓨터 1은 입력 데이터 A로 실행되고 컴퓨터 2는 입력 데이터 B로 실행됩니다. 이들은 단계별로 나란히 실행됩니다. IF (test) .... ENDIF와 같은 조건문이 나오면 테스트가 참인지에 대한 의견이 다르면 거짓이면 테스트를 말하는 사람이 ENDIF로 건너 뛰고 기다립니다. 따라 잡을 여동생. (이것이 코드가 구조화 된 이유이므로 자매가 결국 ENDIF에 도달 할 것이라는 것을 알고 있습니다.)
두 컴퓨터가 서로 대화 할 수 있기 때문에 메모를 비교하고 두 입력 데이터 세트와 실행 이력이 어떻게 다른지 자세히 설명 할 수 있습니다.
물론 차등 실행 (DE)에서는 한 대의 컴퓨터로 두 대를 시뮬레이션합니다.
이제 한 세트의 입력 데이터 만 가지고 있지만 그것이 시간 1에서 시간 2로 어떻게 변경되었는지 확인하고 싶다고 가정합니다. 실행중인 프로그램이 직렬 변환기 / 역 직렬 변환기라고 가정합니다. 실행할 때 현재 데이터를 직렬화 (쓰기)하고 과거 데이터 (마지막으로 기록한 데이터)를 역 직렬화 (읽기)합니다. 이제 지난번 데이터와 이번 데이터의 차이점을 쉽게 확인할 수 있습니다.
쓰고있는 파일과 읽고있는 이전 파일이 함께 큐 또는 FIFO (선입 선출)를 구성하지만 이는 매우 깊은 개념이 아닙니다.
그래픽 프로젝트에서 작업하는 동안 사용자가 "기호"라고하는 작은 디스플레이 프로세서 루틴을 구성 할 수있었습니다.이 루틴은 파이프, 탱크, 밸브 등의 다이어그램과 같은 것을 페인트하기 위해 더 큰 루틴으로 조립할 수 있습니다. 전체 다이어그램을 다시 그릴 필요없이 자체적으로 점진적으로 업데이트 할 수 있다는 점에서 다이어그램을 "동적"으로 만들고 싶었습니다. (하드웨어는 오늘날의 표준에 비해 느 렸습니다.) 저는 (예를 들어) 막대 차트 막대를 그리는 루틴이 이전 높이를 기억하고 점차적으로 자체적으로 업데이트 할 수 있다는 것을 깨달았습니다.
이것은 OOP처럼 들리지 않습니까? 그러나 "개체"를 "만들기"보다는 다이어그램 프로 시저의 실행 순서에 대한 예측 가능성을 활용할 수 있습니다. 막대의 높이를 순차적 인 바이트 스트림으로 쓸 수 있습니다. 그런 다음 이미지를 업데이트하기 위해 다음 업데이트 단계를 준비하기 위해 새 매개 변수를 쓰는 동안 이전 매개 변수를 순차적으로 읽는 모드에서 프로 시저를 실행할 수 있습니다.
이것은 어리석은 것처럼 보이며 절차에 조건이 포함 되 자마자 중단되는 것처럼 보입니다. 왜냐하면 새 스트림과 이전 스트림이 동기화되지 않기 때문입니다. 그러나 조건부 테스트의 부울 값도 직렬화하면 동기화 상태로 돌아갈 수 있다는 사실이 나에게 떠 올랐습니다 . 간단한 규칙 ( "지우기 모드 규칙")을 준수 한다면 이것이 항상 효과 가 있다는 것을 스스로 확신하고 증명하는 데 시간이 걸렸 습니다.
결과적으로 사용자는 디스플레이가 아무리 복잡하거나 구조적으로 변하더라도 동적으로 업데이트되는 방식에 대해 걱정할 필요없이 이러한 "동적 기호"를 디자인하고 더 큰 다이어그램으로 조합 할 수 있습니다.
그 당시에는 시각적 개체 간의 간섭에 대해 걱정해야했기 때문에 하나를 지워도 다른 개체가 손상되지 않습니다. 그러나 이제는 Windows 컨트롤에서이 기술을 사용하고 Windows에서 렌더링 문제를 처리하도록합니다.
그래서 그것은 무엇을 성취합니까? 즉, 컨트롤을 그리는 절차를 작성하여 대화 상자를 만들 수 있으며 실제로 컨트롤 개체를 기억하거나 점진적으로 업데이트하거나 조건에 따라 표시 / 사라짐 / 이동하는 것에 대해 걱정할 필요가 없습니다. 그 결과 대화 소스 코드가 훨씬 더 작고 단순 해졌으며, 동적 레이아웃, 컨트롤 수 변경, 컨트롤 배열 또는 그리드 등은 사소한 일입니다. 또한 편집 필드와 같은 컨트롤은 편집중인 응용 프로그램 데이터에 간단하게 바인딩 될 수 있으며 항상 정확할 것이므로 이벤트를 처리 할 필요가 없습니다. 응용 프로그램 문자열 변수에 대한 편집 필드를 입력하는 것은 한 줄 편집입니다.
제가 가장 설명하기 어려운 것은 소프트웨어에 대해 다르게 생각해야한다는 것입니다. 프로그래머는 소프트웨어의 객체-액션 뷰에 너무 굳게 결합되어 객체가 무엇인지, 클래스가 무엇인지, 디스플레이를 "구축"하는 방법, 이벤트를 처리하는 방법을 알고 싶어합니다. 폭탄을 터뜨리는 것입니다. 제가 전달하고자하는 것은 정말로 중요한 것은 무엇을 말해야 하는가라는 것입니다."여기에서 변수 A, 거기에서 변수 B, 아래에서 변수 C를 편집하고 싶다"라고 말하면 DSL (domain-specific language)을 구축하고 있다고 상상해보십시오. 그러면 마법처럼 처리됩니다. . 예를 들어, Win32에는 대화 상자를 정의하기위한이 "자원 언어"가 있습니다. 충분히 멀리 가지 않는 것을 제외하고는 완벽하게 좋은 DSL입니다. 기본 절차 언어에 "살아 있지"거나 이벤트를 처리하거나 루프 / 조건 / 서브 루틴을 포함하지 않습니다. 그러나 그것은 의미가 있으며 Dynamic Dialogs는 작업을 완료하려고합니다.
따라서 다른 사고 방식은 다음과 같습니다. 프로그램을 작성하려면 먼저 적절한 DSL을 찾고 (또는 발명) 가능한 한 많은 프로그램을 코딩합니다. 하자 가 단지 구현을 위하여 존재하는 모든 객체와 작업을 처리합니다.
차등 실행을 실제로 이해하고 사용하려면 몇 가지 까다로운 문제가 있습니다. 한때 Lisp 매크로로 코딩 했는데,이 까다로운 부분이 사용자를 위해 처리 될 수 있지만 "일반"언어에서는 함정을 피하기 위해 프로그래머 규율이 필요합니다.
너무 길어서 죄송합니다. 내가 말이 안된다면 지적 해 주시면 고맙게 생각하고 고칠 수 있습니다.
추가 :
에서 자바 스윙 , 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 행은 UI의 전체 내용을 정의합니다. 30-48 행은 단일 환자의 기록을 편집하기위한 컨트롤을 정의합니다. 프로그램의 형태는 마치 디스플레이를 한 번만 만드는 것처럼 제 시간에 이벤트를 거의 알리지 않습니다. 그런 다음 주제가 추가 또는 제거되거나 다른 구조적 변경이 발생하면 DE가 대신 증분 업데이트를 수행한다는 점을 제외하고는 처음부터 다시 생성되는 것처럼 간단히 다시 실행됩니다. 장점은 프로그래머가 UI의 점진적 업데이트를 수행하기 위해주의를 기울이거나 코드를 작성할 필요가 없으며 정확하다는 것입니다. 이 재실행이 성능 문제로 보일 수 있지만 그렇지 않습니다.
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 }
추가됨 : Brian이 좋은 질문을했고 그 대답은 여기 본문에 있다고 생각했습니다.
@Mike : "if (deButton (50, 20,"Add ")) {"문이 실제로 무엇을하는지 명확하지 않습니다. deButton 기능은 무엇을합니까? 또한 FOR / END 루프가 일종의 매크로 등을 사용하고 있습니까? – 브라이언.
@Brian : 예, FOR / END 및 IF 문은 매크로입니다. SourceForge 프로젝트에는 완전한 구현이 있습니다. deButton은 버튼 컨트롤을 유지합니다. 사용자 입력 동작이 발생하면 코드는 "control event"모드에서 실행됩니다. 여기서 deButton은 눌 렸음을 감지하고 TRUE를 반환하여 눌렀 음을 나타냅니다. 따라서 "if (deButton (...)) {... 액션 코드 ...}는 클로저를 만들거나 이벤트 핸들러를 작성할 필요없이 버튼에 액션 코드를 첨부하는 방법입니다. DD_THROW는 작업이 응용 프로그램 데이터를 수정했을 수 있으므로 작업이 수행 될 때 패스를 종료하는 방법이므로 루틴을 통해 "제어 이벤트"전달을 계속하는 것은 유효하지 않습니다. 이것을 이벤트 핸들러 작성과 비교하면 작성하는 시간을 절약 할 수 있습니다. 그리고 그것은 당신이 제어의 수를 가질 수 있습니다.
추가됨 : 죄송합니다. "유지"라는 단어가 의미하는 바를 설명해야합니다. 프로 시저가 처음 실행될 때 (SHOW 모드에서), deButton은 버튼 컨트롤을 생성하고 FIFO에서 해당 ID를 기억합니다. 후속 패스 (UPDATE 모드에서)에서 deButton은 FIFO에서 ID를 가져 와서 필요한 경우 수정 한 다음 FIFO에 다시 넣습니다. ERASE 모드에서는 FIFO에서 읽어서 파괴하고 다시 넣지 않으므로 "가비지 수집"을 수행합니다. 따라서 deButton 호출은 컨트롤의 전체 수명을 관리하여 응용 프로그램 데이터와 일치하도록 유지합니다. 이것이 바로 "유지 관리"라고 말하는 이유입니다.
네 번째 모드는 EVENT (또는 CONTROL)입니다. 사용자가 문자를 입력하거나 버튼을 클릭하면 해당 이벤트를 포착하여 기록한 다음 EVENT 모드에서 deContents 프로 시저를 실행합니다. deButton은 FIFO에서 버튼 컨트롤의 ID를 가져오고 이것이 클릭 된 컨트롤인지 묻습니다. 그렇다면 TRUE를 반환하므로 액션 코드를 실행할 수 있습니다. 그렇지 않으면 FALSE를 반환합니다. 반면에는 deEdit(..., &myStringVar)
이벤트가 해당 이벤트인지 감지하고, 그렇다면 편집 컨트롤로 전달한 다음 편집 컨트롤의 내용을 myStringVar에 복사합니다. 이 처리와 일반 UPDATE 처리 사이에서 myStringVar는 항상 편집 컨트롤의 내용과 동일합니다. 이것이 "바인딩"이 수행되는 방법입니다. 스크롤 막대, 목록 상자, 콤보 상자, 응용 프로그램 데이터를 편집 할 수있는 모든 종류의 컨트롤에도 동일한 개념이 적용됩니다.
내 Wikipedia 편집에 대한 링크는 다음과 같습니다. http://en.wikipedia.org/wiki/User:MikeDunlavey/Difex_Article