
에 대한 후속 질문입니다.\csname…\endcsname의 \전역 변형, 거기에 설명된 문제에 대한 해결책을 요구하지만 설명을 요구하지는 않습니다.
문제의 핵심은
{ \gdef\foo{...} }
와는 다르게 행동하는 것 같다
{ \expandafter\gdef\csname foo\endcsname{...} }
여기서 후자는 "[the] save_stack에 유지 항목을 추가합니다."
그래서 나는 알고 싶습니다:
- 두 구성의 동작에 차이가 있는 이유는 무엇입니까? 이것은 구현 방식의 부작용입니까
\csname...\endcsname
, 아니면 의도적인 의미상의 차이입니까(말할 수 있는 한). {retaining ...}
연결된 질문에 언급된 로그 파일의 항목은 정확히 무엇을 의미합니까? 그룹이 폐쇄된 후에도 지역 정의를 유지해야 하는 이유는 무엇입니까?
답변1
이 질문을 해주셔서 감사합니다. 나는 그 예를 잘 이해하지 못했습니다.다른 질문읽었을 때, 이 질문이 나로 하여금 다시 보게 된 계기가 되었고 이제 이해가 된 것 같습니다.
기본적으로 예제에서 명확하지 않은 점은 이러한 문제가 여러 개 있을 때 문제가 적용된다는 것입니다.\expandafter\gdef\csname foo\endcsname{...}
내부에 이러한 구성이 여러 개 있을 때 적용된다는 것입니다.같은그룹(또는 해당 그룹 내에 중첩된 그룹), 즉 저장 스택은 그룹 내에 있는 동안에만 커집니다. "retaining…" 메시지는 그룹을 나갈 때(스택이 팝될 때) 인쇄되지만 이 메시지 자체는 문제가 되지 않습니다. 이는 스택이 더 일찍 성장했다는 증거로만 사용됩니다.
해당 단락은 아마도 혼란스러울 수 있으므로 저장 스택을 처음부터 이해해 보겠습니다. :-)
1.다음 예를 고려하십시오.
\def\a{hello}
{
\def\a{world}
}
여기에서 내부 그룹의 TeX가 \a
(로) 재정의되는 것을 확인하면 world
이전 값(을 포함하는 토큰 목록에 대한 참조 hello
)을 저장 스택에 저장합니다. 그런 다음 그룹 끝에 도달하면 스택을 팝하여 의 정의를 복원합니다 hello
. 이것이 저장 스택의 명백한 이유이며 다음은 해당 추적 출력입니다. and 및 also가 있고 내부 그룹이 라인 10에서 시작한다고 가정합니다 \tracingrestores=2
( \tracinggroups=2
가령 \tracingassigns=2
). 또한 줄바꿈을 제거하기 위해 출력을 수정했습니다. 전에 {into…}
:
{changing \a=undefined}{into \a=macro:->hello}
{entering simple group (level 1) at line 10}
{changing \a=macro:->hello}{into \a=macro:->world}
{restoring \a=macro:->hello}
{leaving simple group (level 1) entered at line 10}
2.이제 동일한 예를 고려해 보겠습니다.없이상단 \def\a{hello}
에:
{
\def\a{world}
}
— 다시 한 번, TeX가 \def\a{world}
그룹 내부를 볼 때 이전 의미를 저장해야 합니다 \a
. 우연히 정의되지 않았지만 그룹을 떠난 후에도 다시 정의되지 않아야 하므로 TeX는 "정의되지 않음" 의미를 저장 스택에 넣어야 합니다. 추적 출력은 다음과 같습니다.
{entering simple group (level 1) at line 10}
{changing \a=undefined}{into \a=macro:->world}
{restoring \a=undefined}
{leaving simple group (level 1) entered at line 10}
삼.이제 비슷한 예를 살펴보겠습니다.
{
\let\a=\relax
\gdef\a{world}
}
여기서 TeX가 첫 번째 를 볼 때 \let\a=\relax
이전 예에서와 마찬가지로 이전 의미("정의되지 않음")를 저장 스택에 저장해야 합니다. 그런 다음 을 보면 \gdef
저장 스택에 아무 것도 넣을 필요가 없습니다. 마지막으로 그룹의 끝에 도달하여 스택(현재 에 대한 "정의되지 않음" 의미가 포함되어 있음 \a
) 을 팝하기 시작하면 \a
이제 전역 정의가 있으므로 "정의되지 않음" 의미를 무시하고 전역 정의를 유지합니다. 이는 추적 출력을 설명합니다.
{entering simple group (level 1) at line 10}
{changing \a=undefined}{into \a=\relax}
{globally changing \a=\relax}{into \a=macro:->world}
{retaining \a=macro:->world}
{leaving simple group (level 1) entered at line 10}
4.마지막으로 다음 예를 고려해보세요.
{
\expandafter\gdef\csname a\endcsname{world}
}
이는 이전 사례와 완전히 동일한 것으로 밝혀졌습니다. 이는 TeX가 \expandafter
일시적으로 를 통과하여 \gdef
다음 토큰( )에 대해 작업을 시작하기 때문입니다. \csname
이는 매크로가 아직 정의되지 않은 경우 에 대한 정의와 함께 \csname a\endcsname
매크로를 생성하는 작업을 의미합니다 (이것이 작동 방식입니다). 그 후에야 (이전 일시적으로 전달된)에 작용하고 ( ) 뒤에 오는 새로운 정의로 재정의됩니다 . 따라서 추적 출력은 이전과 동일합니다.\a
\relax
\csname
\gdef
\a
world
{entering simple group (level 1) at line 10}
{changing \a=undefined}{into \a=\relax}
{globally changing \a=\relax}{into \a=macro:->world}
{retaining \a=macro:->world}
{leaving simple group (level 1) entered at line 10}
그게 전부입니다. 특정 질문에 답변하려면:
\csname … \endcsname
(1) 구현 방식의 부작용 일뿐입니다 . 즉, 그것은 첫 번째\let
토큰입니다\relax
. 그러나 이는 잘 문서화되어 있으므로 의미론의 일부이기도 합니다(모두가 기대하는 것처럼).(2a)
{retaining ...}
로그 파일의 항목은 다음을 의미합니다(의 301페이지 참조).TeXbook) 그더 일찍\relax
저장 스택에 있는 정의, 즉 에 의해 설정되기 전의 매크로 값이 무엇이든\csname … \endcsname
때문에 무시되었습니다\gdef
(그리고 전역 정의는 유지되었습니다).(2b) “그룹이 폐쇄된 후에도 지역적 정의를 유지하는 것이 왜 필요한가요?” — 그렇지도 않고 보관되지도 않습니다. 대신에 당신이 보는 것은언제그룹이 닫히면 동일한 그룹 내에서 만들어진 로컬 정의로 인해 이전에 저장된 모든 정의가 이제 검사되고, 어느 지점에든 전역 정의가 있는 경우 이제 삭제됩니다. 그룹이 끝나면 저장 스택은 비어 있게 됩니다(또는 그룹에 들어갈 때와 동일한 크기를 가집니다).
좀 더 구체적으로, 다음은 문제와 해결 방법의 예입니다.그 질문. 질문자는 대략 다음과 같은 방식으로 그룹 내에서 (본질적으로) 많은 매크로를 정의했습니다.
{
\expandafter\gdef\csname A\endcsname{I'm A}
\expandafter\gdef\csname B\endcsname{I'm B}
\expandafter\gdef\csname C\endcsname{I'm C}
}
등등. 위의 예제 3과 4에서 본 것처럼 이는 다음과 동일합니다.
{
\let\A=\relax \gdef\A{I'm A}
\let\B=\relax \gdef\B{I'm B}
\let\C=\relax \gdef\C{I'm C}
}
등등. 따라서 위의 각 정의는 저장 스택에 하나의 항목을 배치하고(제어 시퀀스 이름이 by 로 \let
지정 되기 전의 의미 ) 그룹의 끝에서만 이러한 모든 항목이 팝됩니다. 따라서 그러한 정의가 너무 많으면; "저장 크기"가 부족해집니다.\relax
\csname ... \endcsname
그만큼처음 게시된 답변\csname … \endcsname
(Steven B. Segletes 작성) 각 실행이 최상위 수준(저장 스택에 아무것도 저장되지 않는 수준)에서 실행되는 것과 동등한 작업을 수행할 것을 제안했습니다 .
그만큼두 번째 게시된 답변(Marcel Krüger 작성)은 다음과 같은 작업을 제안했습니다.
{
\begingroup\expandafter\endgroup\expandafter\gdef\csname A\endcsname{I'm A}
\begingroup\expandafter\endgroup\expandafter\gdef\csname B\endcsname{I'm B}
\begingroup\expandafter\endgroup\expandafter\gdef\csname C\endcsname{I'm C}
}
여기서 정의는 즉시 종료되는 그룹 내에서 발생하므로 각 스택이 즉시 팝됩니다. 추적 출력은 다음과 같습니다(모듈로 줄 바꿈).
{entering simple group (level 1) at line 10}
{entering semi simple group (level 2) at line 11}
{changing \A=undefined}{into \A=\relax}
{restoring \A=undefined}
{leaving semi simple group (level 2) entered at line 11}
{globally changing \A=undefined}{into \A=macro:->I'm A}
{entering semi simple group (level 2) at line 12}
{changing \B=undefined}{into \B=\relax}
{restoring \B=undefined}
{leaving semi simple group (level 2) entered at line 12}
{globally changing \B=undefined}{into \B=macro:->I'm B}
{entering semi simple group (level 2) at line 13}
{changing \C=undefined}{into \C=\relax}
{restoring \C=undefined}
{leaving semi simple group (level 2) entered at line 13}
{globally changing \C=undefined}{into \C=macro:->I'm C}
{leaving simple group (level 1) entered at line 10}
확장성 등에 관심이 없고 저장 스택만 이해하려는 경우 제안된 솔루션은 다음과 같습니다.
{
{\let\A=\relax} \gdef\A{I'm A}
{\let\B=\relax} \gdef\B{I'm B}
{\let\C=\relax} \gdef\C{I'm C}
}
그러면 저장 스택이 계속 커지지 않는 이유를 알 수 있습니다.