텍스트 편집기에서와 같이 실행 취소 / 다시 실행 기능을 구현하는 방법에 대해 생각해보십시오. 어떤 알고리즘을 사용해야하며 무엇을 읽을 수 있는지. 감사.
텍스트 편집기에서와 같이 실행 취소 / 다시 실행 기능을 구현하는 방법에 대해 생각해보십시오. 어떤 알고리즘을 사용해야하며 무엇을 읽을 수 있는지. 감사.
답변:
실행 취소 유형의 두 가지 주요 구분에 대해 알고 있습니다.
텍스트 편집기의 경우 이러한 방식으로 상태를 생성하는 것은 계산 집약적이지 않지만 Adobe Photoshop과 같은 프로그램의 경우 계산 집약적이거나 불가능할 수 있습니다. 예를 들어 -Blur 작업의 경우 De-Blur 작업을 지정 하지만 데이터가 이미 손실 되었기 때문에 원래 상태 로 되돌릴 수 없습니다. 따라서 상황에 따라 논리적 역행 동의 가능성과 그 가능성에 따라이 두 가지 광범위한 범주 중에서 선택한 다음 원하는 방식으로 구현해야합니다. 물론 자신에게 맞는 하이브리드 전략을 가질 수 있습니다.
또한 Gmail과 마찬가지로 작업 (메일 보내기)이 처음에 수행되지 않기 때문에 시간 제한적으로 실행 취소가 가능한 경우도 있습니다. 그래서, 당신은 거기에서 "실행 취소"하는 것이 아니라 단지 액션 자체를 "하지 않는"것입니다.
필자는 처음부터 두 개의 텍스트 편집기를 작성했으며 둘 다 매우 원시적 인 형태의 실행 취소 / 다시 실행 기능을 사용합니다. "기본"이란 기능이 구현하기 매우 쉬웠지만 매우 큰 파일 (예 : >> 10MB)에서는 비 경제적이라는 것을 의미합니다. 그러나 시스템은 매우 유연합니다. 예를 들어 무제한 수준의 실행 취소를 지원합니다.
기본적으로 다음과 같은 구조를 정의합니다.
type
TUndoDataItem = record
text: /array of/ string;
selBegin: integer;
selEnd: integer;
scrollPos: TPoint;
end;
그런 다음 배열을 정의하십시오.
var
UndoData: array of TUndoDataItem;
그런 다음이 배열의 각 구성원은 텍스트의 저장된 상태를 지정합니다. 이제 텍스트를 편집 할 때마다 (문자 키 내리기, 백 스페이스 내리기, 키 내리기, 잘라 내기 / 붙여 넣기, 마우스로 이동 한 선택 등) 1 초의 타이머를 (다시) 시작합니다. 트리거되면 타이머는 현재 상태를 UndoData
배열 의 새 구성원으로 저장합니다 .
실행 취소 (Ctrl + Z), 나는 상태로 편집기를 복원 UndoData[UndoLevel - 1]
하고 감소 UndoLevel
하나. 기본적으로 UndoLevel
는 UndoData
배열 의 마지막 멤버의 인덱스와 같습니다 . 다시 실행 (Ctrl + Y 또는 Shift + Ctrl + Z) 할 때 편집기를 상태로 복원합니다.UndoData[UndoLevel + 1]
UndoLevel
1 씩 증가시킵니다 . 물론 편집 타이머가 배열 UndoLevel
의 길이 (마이너스 1)와 같지 않을 때 트리거되는 경우 Microsoft Windows 플랫폼에서 일반적으로 사용되는 것처럼 UndoData
이 배열의 모든 항목을. UndoLevel
올바르게-Microsoft Windows 접근 방식의 단점은 많은 변경 사항을 실행 취소 한 다음 실수로 버퍼를 편집하면 이전 콘텐츠 (취소되지 않은)가 영구적으로 손실된다는 것입니다. 이 어레이 축소를 건너 뛸 수 있습니다.
예를 들어 이미지 편집기와 같은 다른 유형의 프로그램에서 동일한 기술을 적용 할 수 있지만 물론 완전히 다른 UndoDataItem
구조를 사용합니다. 많은 메모리를 필요로하지 않는 고급 접근 방식 은 실행 취소 수준 간의 변경 사항 만 저장 하는 것입니다 (즉, "alpha \ nbeta \ gamma"및 "alpha \ nbeta \ ngamma \ ndelta"를 저장하는 대신). "알파 \ n 베타 \ n 감마"및 "추가 \ n 델타"를 저장하십시오. 각 변경 사항이 파일 크기에 비해 작은 매우 큰 파일에서는 실행 취소 데이터의 메모리 사용량이 크게 줄어들지 만 구현하기가 까다 롭고 오류가 발생할 가능성이 더 높습니다.
이를 수행하는 방법에는 여러 가지가 있지만 명령 패턴을 살펴볼 수 있습니다. 명령 목록을 사용하여 작업에서 뒤로 (실행 취소) 또는 앞으로 (다시 실행) 이동합니다. C #의 예는 여기 에서 찾을 수 있습니다 .
조금 늦었지만 여기에 있습니다. 특별히 텍스트 편집기를 언급하면 다음은 편집중인 모든 것에 적용 할 수있는 알고리즘을 설명합니다. 관련된 원칙은 각 변경 사항을 다시 생성하기 위해 자동화 할 수있는 작업 / 지침 목록을 유지하는 것입니다. 원본 파일을 변경하지 말고 (비어 있지 않은 경우) 백업용으로 보관하십시오.
원본 파일에 대한 변경 사항의 앞뒤 링크 목록을 유지합니다. 이 목록은 사용자가 실제로 변경 사항 을 저장할 때까지 임시 파일에 간헐적으로 저장 됩니다.이 경우 변경 사항을 새 파일에 적용하고 이전 파일을 복사하고 동시에 변경 사항을 적용합니다. 그런 다음 원본 파일의 이름을 백업으로 바꾸고 새 파일의 이름을 올바른 이름으로 변경하십시오. (저장된 변경 목록을 유지하거나 삭제하고 후속 변경 목록으로 바꿀 수 있습니다.)
연결 목록의 각 노드에는 다음 정보가 포함됩니다.
delete
뒤에insert
insert
삽입 된 데이터 인 경우 인 경우 delete
삭제 된 데이터입니다.을 구현하려면 Undo
'현재 노드'포인터 또는 색인을 사용하여 연결 목록의 끝에서 뒤로 작업합니다. 변경된 위치 insert
에서 연결 목록을 업데이트하지 않고 삭제를 수행합니다. 그리고 그것은 어디 delete
링크 된 목록 버퍼의 데이터에서 데이터를 삽입합니다. 사용자의 각 '실행 취소'명령에 대해이 작업을 수행하십시오. Redo
'현재 노드'포인터를 앞으로 이동하고 노드별로 변경을 실행합니다. 사용자가 실행 취소 후 코드를 변경하는 경우 'current-node'표시기 뒤의 모든 노드를 tail까지 삭제하고 tail을 'current-node'표시기와 동일하게 설정합니다. 그런 다음 사용자의 새로운 변경 사항이 꼬리 뒤에 삽입됩니다. 그게 다입니다.
내 두 센트는 작업을 추적하기 위해 두 개의 스택을 사용하고 싶다는 것입니다. 사용자가 일부 작업을 수행 할 때마다 프로그램은 해당 작업을 "수행 된"스택에 배치해야합니다. 사용자가 이러한 작업을 실행 취소하려면 "수행 된"스택에서 "리콜"스택으로 작업을 팝하면됩니다. 사용자가 이러한 작업을 다시 실행하려면 "재 호출"스택에서 항목을 팝하고 "수행 된"스택으로 다시 푸시합니다.
도움이 되었기를 바랍니다.
작업을 되돌릴 수있는 경우. 예를 들어 1을 추가하고 플레이어를 움직이게하는 등 명령 패턴을 사용하여 실행 취소 / 다시 실행을 구현하는 방법을 확인 합니다. . 링크를 따라 가면 그 방법에 대한 자세한 예를 찾을 수 있습니다.
그렇지 않은 경우 @Lazer에서 설명한대로 저장된 상태 를 사용 합니다.
기존 실행 취소 / 다시 실행 프레임 워크의 예를 연구 할 수 있습니다. 첫 번째 Google 히트 곡은 codeplex (.NET 용)입니다. 입니다. 다른 프레임 워크보다 더 좋은지 더 나쁜지는 모르겠습니다.
목표가 애플리케이션에 실행 취소 / 다시 실행 기능을 포함하는 것이라면 애플리케이션 종류에 적합한 기존 프레임 워크를 선택하는 것이 좋습니다.
자신 만의 실행 취소 / 재실행을 빌드하는 방법을 배우고 싶다면 소스 코드를 다운로드하고 패턴과 연결 방법을 자세히 살펴볼 수 있습니다.
메멘토 패턴 이 만들어졌습니다.
이를 직접 구현하기 전에 이것은 매우 일반적이며 코드가 이미 존재합니다. 예를 들어 .Net에서 코딩하는 경우 IEditableObject 를 사용할 수 있습니다.
토론에 덧붙여, 직관적 인 것에 대한 생각을 바탕으로 UNDO 및 REDO를 구현하는 방법에 대한 블로그 게시물을 작성했습니다. http://adamkulidjian.com/undo-and-redo.html
기본 실행 취소 / 다시 실행 기능을 구현하는 한 가지 방법은 메모 및 명령 디자인 패턴을 모두 사용하는 것입니다.
Memento 는 예를 들어 나중에 복원 할 개체의 상태를 유지하는 것을 목표로합니다. 이 유품은 최적화 목적에서 가능한 한 작아야합니다.
명령 필요할 때 물체 (명령)의 패턴 캡슐화 명령어 실행한다.
이 두 가지 개념을 기반으로 TypeScript로 코딩 된 다음과 같은 기본 실행 취소 / 다시 실행 기록을 작성할 수 있습니다 ( 프런트 엔드 라이브러리 Interacto 에서 추출 및 조정 됨 ).
이러한 역사는 두 가지 스택에 의존합니다.
알고리즘 내에서 주석이 제공됩니다. 실행 취소 작업 에서 다시 실행 스택을 지워야합니다! 그 이유는 애플리케이션을 안정된 상태로두기 때문입니다. 과거로 돌아가서 수행 한 일부 작업을 다시 실행하면 미래를 바꿀 때 이전 작업이 더 이상 존재하지 않게됩니다.
export class UndoHistory {
/** The undoable objects. */
private readonly undos: Array<Undoable>;
/** The redoable objects. */
private readonly redos: Array<Undoable>;
/** The maximal number of undo. */
private sizeMax: number;
public constructor() {
this.sizeMax = 0;
this.undos = [];
this.redos = [];
this.sizeMax = 30;
}
/** Adds an undoable object to the collector. */
public add(undoable: Undoable): void {
if (this.sizeMax > 0) {
// Cleaning the oldest undoable object
if (this.undos.length === this.sizeMax) {
this.undos.pop();
}
this.undos.push(undoable);
// You must clear the redo stack!
this.clearRedo();
}
}
private clearRedo(): void {
if (this.redos.length > 0) {
this.redos.length = 0;
}
}
/** Undoes the last undoable object. */
public undo(): void {
const undoable = this.undos.pop();
if (undoable !== undefined) {
undoable.undo();
this.redos.push(undoable);
}
}
/** Redoes the last undoable object. */
public redo(): void {
const undoable = this.redos.pop();
if (undoable !== undefined) {
undoable.redo();
this.undos.push(undoable);
}
}
}
Undoable
인터페이스는 매우 간단합니다 :
export interface Undoable {
/** Undoes the command */
undo(): void;
/** Redoes the undone command */
redo(): void;
}
이제 애플리케이션에서 작동하는 실행 취소 가능한 명령을 작성할 수 있습니다.
예를 들어 (여전히 Interacto 예제를 기반으로 함) 다음과 같은 명령을 작성할 수 있습니다.
export class ClearTextCmd implements Undoable {
// The memento that saves the previous state of the text data
private memento: string;
public constructor(private text: TextData) {}
// Executes the command
public execute() void {
// Creating the memento
this.memento = this.text.text;
// Applying the changes (in many
// cases do and redo are similar, but the memento creation)
redo();
}
public undo(): void {
this.text.text = this.memento;
}
public redo(): void {
this.text.text = '';
}
}
이제 명령을 실행하고 UndoHistory 인스턴스에 추가 할 수 있습니다.
const cmd = new ClearTextCmd(...);
//...
undoHistory.add(cmd);
마지막으로 실행 취소 버튼 (또는 바로 가기)을이 기록에 바인딩 할 수 있습니다 (다시 실행에도 동일).
이러한 예는 Interacto 설명서 페이지 에 자세히 설명되어 있습니다.