[algorithm] 실행 취소 / 다시 실행 구현

텍스트 편집기에서와 같이 실행 취소 / 다시 실행 기능을 구현하는 방법에 대해 생각해보십시오. 어떤 알고리즘을 사용해야하며 무엇을 읽을 수 있는지. 감사.



답변

실행 취소 유형의 두 가지 주요 구분에 대해 알고 있습니다.

  • 상태 저장 :
    실행 취소의 한 범주는 실제로 기록 상태를 저장하는 곳입니다. 이 경우 발생하는 일은 모든 지점에서 상태를 메모리의 특정 위치에 저장하는 것입니다. 실행 취소를 원할 때 현재 상태를 교체하고 이미 메모리에 있던 상태로 교체하면됩니다. 예를 들어 Adobe Photoshop에서 History를 사용하거나 Google Chrome에서 닫힌 탭을 다시 여는 방법입니다.

대체 텍스트

  • 상태 생성 :
    다른 범주는 상태 자체를 유지하는 대신 작업이 무엇인지 기억하는 것입니다. 실행을 취소해야하는 경우 해당 특정 작업을 논리적으로 반대로 수행해야합니다. 간단한 예를 들어, 실행 취소를 지원하는 일부 텍스트 편집기에서 Ctrl+ 를 수행 B하면 굵은 작업으로 기억됩니다 . 이제 각 작업에는 논리적 반전 매핑이 있습니다. 따라서 Ctrl+ 를 수행하면 Z역 행동 테이블에서 조회하여 실행 취소 조치가 다시 Ctrl+ 임을 발견 B합니다. 그것이 수행되고 이전 상태를 얻습니다. 따라서 여기서 이전 상태는 메모리에 저장되지 않고 필요할 때 생성되었습니다.

텍스트 편집기의 경우 이러한 방식으로 상태를 생성하는 것은 계산 집약적이지 않지만 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하나. 기본적으로 UndoLevelUndoData배열 의 마지막 멤버의 인덱스와 같습니다 . 다시 실행 (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 #의 예는 여기 에서 찾을 수 있습니다 .


답변

조금 늦었지만 여기에 있습니다. 특별히 텍스트 편집기를 언급하면 ​​다음은 편집중인 모든 것에 적용 할 수있는 알고리즘을 설명합니다. 관련된 원칙은 각 변경 사항을 다시 생성하기 위해 자동화 할 수있는 작업 / 지침 목록을 유지하는 것입니다. 원본 파일을 변경하지 말고 (비어 있지 않은 경우) 백업용으로 보관하십시오.

원본 파일에 대한 변경 사항의 앞뒤 링크 목록을 유지합니다. 이 목록은 사용자가 실제로 변경 사항 을 저장할 때까지 임시 파일에 간헐적으로 저장 됩니다.이 경우 변경 사항을 새 파일에 적용하고 이전 파일을 복사하고 동시에 변경 사항을 적용합니다. 그런 다음 원본 파일의 이름을 백업으로 바꾸고 새 파일의 이름을 올바른 이름으로 변경하십시오. (저장된 변경 목록을 유지하거나 삭제하고 후속 변경 목록으로 바꿀 수 있습니다.)

연결 목록의 각 노드에는 다음 정보가 포함됩니다.

  • 변경 유형 : 데이터를 삽입하거나 데이터를 삭제합니다. 데이터를 “변경”한다는 것은 a delete뒤에insert
  • 파일 내 위치 : 오프셋 또는 행 / 열 쌍일 수 있습니다.
  • 데이터 버퍼 : 작업과 관련된 데이터입니다. insert삽입 된 데이터 인 경우 인 경우 delete삭제 된 데이터입니다.

을 구현하려면 Undo‘현재 노드’포인터 또는 색인을 사용하여 연결 목록의 끝에서 뒤로 작업합니다. 변경된 위치 insert에서 연결 목록을 업데이트하지 않고 삭제를 수행합니다. 그리고 그것은 어디 delete링크 된 목록 버퍼의 데이터에서 데이터를 삽입합니다. 사용자의 각 ‘실행 취소’명령에 대해이 작업을 수행하십시오. Redo‘현재 노드’포인터를 앞으로 이동하고 노드별로 변경을 실행합니다. 사용자가 실행 취소 후 코드를 변경하는 경우 ‘current-node’표시기 뒤의 모든 노드를 tail까지 삭제하고 tail을 ‘current-node’표시기와 동일하게 설정합니다. 그런 다음 사용자의 새로운 변경 사항이 꼬리 뒤에 삽입됩니다. 그게 다입니다.


답변

내 두 센트는 작업을 추적하기 위해 두 개의 스택을 사용하고 싶다는 것입니다. 사용자가 일부 작업을 수행 할 때마다 프로그램은 해당 작업을 “수행 된”스택에 배치해야합니다. 사용자가 이러한 작업을 실행 취소하려면 “수행 된”스택에서 “리콜”스택으로 작업을 팝하면됩니다. 사용자가 이러한 작업을 다시 실행하려면 “재 호출”스택에서 항목을 팝하고 “수행 된”스택으로 다시 푸시합니다.

도움이 되었기를 바랍니다.


답변

작업을 되돌릴 수있는 경우. 예를 들어 1을 추가하고 플레이어를 움직이게하는 등 명령 패턴을 사용하여 실행 취소 / 다시 실행을 구현하는 방법을 확인 합니다. . 링크를 따라 가면 그 방법에 대한 자세한 예를 찾을 수 있습니다.

그렇지 않은 경우 @Lazer에서 설명한대로 저장된 상태 를 사용 합니다.


답변

기존 실행 취소 / 다시 실행 프레임 워크의 예를 연구 할 수 있습니다. 첫 번째 Google 히트 곡은 codeplex (.NET 용)입니다. 입니다. 다른 프레임 워크보다 더 좋은지 더 나쁜지는 모르겠습니다.

목표가 애플리케이션에 실행 취소 / 다시 실행 기능을 포함하는 것이라면 애플리케이션 종류에 적합한 기존 프레임 워크를 선택하는 것이 좋습니다.
자신 만의 실행 취소 / 재실행을 빌드하는 방법을 배우고 싶다면 소스 코드를 다운로드하고 패턴과 연결 방법을 자세히 살펴볼 수 있습니다.