균형 잡힌 토큰 목록을 비교하는 확장 가능한 표준 TeX 전용 방법

균형 잡힌 토큰 목록을 비교하는 확장 가능한 표준 TeX 전용 방법

확장 가능을 제외하고 유사한 작업을 수행하는 (합리적으로) 효율적인 매크로가 있습니까 \long\def\comparets#1#2{\def\aa{#1}\def\bb{#2}\ifx\aa\bb true\else false\fi} (즉 \newcomparets{<tokens1>}{<tokens2>}, inside 를 포함하여 'true' 또는 'false'로 확장됨 \edef)? 나는 '순수한' TeX(즉, e-TeX와 같은 확장이 없는) 솔루션을 찾고 있습니다. 매크로를 살펴봤지만 l3tle-TeX를 사용하는 것 같습니다. 솔루션은 임의의 토큰 시퀀스(다양한 '재미있는 공백'과 중괄호 및 임의 제어 시퀀스를 포함하는 토큰 포함)와 함께 작동해야 합니다. 여러 단계를 수행하지 않고는 이 작업을 수행할 수 있는 방법을 찾을 수 없는 것 같습니다.

답변1

내 질문에 대한 답변으로 이것을 게시하는 것이 실제로 답변이 아니기 때문에 정결한 것인지 확실하지 않지만 (할 수 있다고 가정하더라도) 그렇게 플래그를 지정하지 않을 것이므로 원래 문제를 해결하는 영감이 번쩍이는 사람이 있다면, 나는 그것을 진정한 대답으로 기꺼이 표시하겠습니다.

이제 매크로에 대해 알아보겠습니다. 그 모양에 대해 미리 사과드립니다. 수년에 걸쳐 제가 작성한 다양한 코드에서 가져온 것(그리고 이름을 다시 변경한 것)이므로 스타일이 약간... 절충적이라고 해야 할까요? 아래에서 많은 부분을 최적화할 수 있지만 나중에 설명할 것처럼 다중 패스 문제가 남아 있으므로 이를 해결할 수 있는 기발한 방법이 있는 사람이 있으면 알려주시기 바랍니다.

몇 가지 주의 사항이 있습니다.

1) 실제 비교 매크로는 누락되었으며 분석 부분만 존재합니다. 이 부분은 '접두사 확장 가능' 방식(예: 트릭과 함께 작동 \romannumeral-1)으로 모든 토큰을 식별하기에 충분한 정보가 포함된 카테고리 11 및 12 토큰 문자열을 전달합니다. 해당 카테고리, 문자 코드(있는 경우), 중괄호인지 여부, 문자 코드 등의 순서입니다. 원하는 경우 이러한 문자열을 직접 비교할 수 있습니다.

2) 음, 1)은 두 가지 측면에서 선의의 거짓말입니다.

a) 매개변수로 가져올 수 있는 모든 토큰(예: 공백, 중괄호 아님)은 \meaning t ... e로 묶인 문자열로 대체됩니다(t와 e는 모두 카테고리 11입니다). 문자 코드가 32가 아닌 카테고리 10 토큰이 이 카테고리에 속한다는 점에 유의하세요. \yygrabtokenraw더 나은 분석을 제공하기 위해 조정될 수 있습니다(임의의 균형 잡힌 토큰 목록을 비교하는 것이 목표이지만 주의 깊게 작성된 몇 가지 조건으로 귀결되는 경우 필수). -1일 수도 있으므로 만으로는 \string충분하지 않습니다 \escapechar.

b) '최상위' 재귀 단계가 누락되었습니다. 여기서 주요 문제는 문자 코드 32의 중괄호입니다. 시퀀스의 길이가 알려지고 \string각 시퀀스를 모두 확인하거나 알아낼 수 있는 마지막 단계에서 처리됩니다 \meaning. 글쎄, 카테고리 코드 32가 있고 둘 다 일반 공간으로 변환 \meaning하기 때문에 그렇게 빠르지는 않습니다( 두 개의 공백으로 끝나서 도움이 되지 않음). 이는 해결하기 위해 발명된 문제 중 하나입니다. 그러므로 우리는 그것들을 어떻게 잡을 것인지 결정해야 합니다. 코드에서 보장하는 것은 모든 여는 중괄호가 문자 코드 32( 또는 ) 또는 32 이외의 문자 코드( , ) 로 올바르게 식별된다는 것입니다 . 이를 수행하는 코드는 중괄호를 안전하게 사용하기 위해 뒤따르는 일부 닫는 중괄호(문자 코드)를 엉망으로 만들어서 첫 번째 것 뒤에 오는 '마커'를 신뢰할 수 없습니다(그러나 다른 , 또는 가 발견되면 이는 중괄호입니다). 문자 코드 32). 다음 반복에서는 다음 중괄호를 엉망으로 만들지 않고 '해독'된 중괄호를 잡을 수 있습니다. 더 많은 패스(안타깝게도 닫는 중괄호 수만큼) 후에 모든 것이 해결될 수 있습니다. 누구든지 관심이 있다면 매크로를 완성하여 이를 수행할 수 있습니다. Knuth가 각각 점으로 끝나는 경우에만 ...\string\meaning\detokenizeo1ec1eo2ec2ec2eo1eo1eo2e\meaning

3) 코드는 '확장 전파'에 많은 시간을 소비합니다. 일반적인 상황은 다음과 같습니다 \somemacro{<long list of benign tokens>}{\string}. \string다른 일이 발생하기 전에 여기를 확장해야 하므로 를 에 \somemacro삽입하는 데 많은 시간을 소비합니다 . 매우 길면 실패하므로 모든 것을 숫자로 코딩하는 것은 도움이 되지 않습니다. (후속 조치를 통해) 사용이 가능 하지만 이 경우 TeX의 해시 테이블을 오염시키는 것이 불안합니다.\expandafter<long list ...>\romannumeral<long list ...>\csname <long ...>\endcsname\expandafter

\meaning매크로는 첫 번째 단계에서 '재미있는 공간'을 식별하려고 시도하며 이는 이하 에서 유일한 용도입니다 \yymatchblankspace . 하나만으로 할 수 있습니다 \string.

매크로에 대한 테스트 케이스가 마지막에 포함되어 있습니다. 내가 어리석은 것을 간과했다면 사과드립니다(Joseph Wright와 다른 사람들이 의심할 때 나도 의심하는 경향이 있습니다).

편집하다:여기에 문제가 있을 수 있는 다른 모든 것 외에도 \long명확성을 위해 모든 정의 앞에 생략했으므로 a가 \par이를 망칠 것입니다.

확장하려면더 나은 분석 제공위: 병리학적 사례(예: \escapechar=-1 \let\#=#)를 해결하려면 여러 개의 매크로(예: 문자당 하나(또는 두 개) ) 또는 모든 어려운 작업을 수행하는 것처럼 하나의 ed \expandafter\def\csname match#\endcsname #1\##{...}% last '#' is \catcode 13가 있는 몇 개의 매크로 ('를 재귀적으로 삽입하여)를 준비할 수 있습니다. 잠재적인 '구분 기호'에 있는 '토큰을 잡았습니다.) 옵션 사이(공간에 대한 거래 시간)도 가능합니다. 물론 '매크로가 너무 많다'는 점은 우려할 만한 일입니다. 이에 대한 나의 (불완전한) 견해는 다음과 같습니다. '그렇게 많은 레지스터를 감당할 수 있다면 특별한 '조건'도 감당할 수 있습니다.\def\def\maintest #1<a list of all active characters and single letter cs's>{...}\catcode

나는 두렵다확장 전파위에서 언급한 문제는 단순히 TeX에서 재귀를 수행하는 데 드는 비용입니다. 이 문제는 \yysx ?where 를 사용하여 토큰을 인코딩(첫 번째 패스 동안)함으로써 어느 정도 완화될 수 있습니다 \def\yysx#1#2{\expandafter\space\expandafter\yysx\expandafter#1\romannumeral-1#2}. 이렇게 하면 항목 \romannumeral-1목록 앞의 항목은 \yysx ?그대로 유지하면서 목록 끝까지 확장을 '전달'합니다.

'보조기 후처리'는 그런 느낌이에요~해야 한다피할 수 있다.

마지막으로 '왜 e-TeX가 없나요?'라는 질문을 여러 번 받았습니다. 이곳이 이에 대해 논의하기에 적절한 장소인지는 확신할 수 없지만 이를 피해야 할 (아마도 주관적인) 이유가 있습니다. 그러한 선호 사항을 논의할 수 있는 더 좋은 장소를 제안해 주실 수 있는 분이 계시다면 감사하겠습니다.

% helper macros (to build test cases, etc); @ is a letter

\def\yyreplacestring#1\in#2\with#3{%
      \expandafter\def\expandafter\r@placestring\expandafter##\expandafter1\the#1##2\end{%
          \def\r@placestring{##2}% is this the string at the very end?
          \ifx\r@placestring\empty % then it is the one we inserted, report
              \errmessage{string <\the#1> not present in \the#2}% do not change the register if the string is not there
          \else % remove the extra copy of #1\end at the end
              \expandafter#2\expandafter\expandafter\expandafter
                  {\expandafter\r@plac@string\expandafter{\the#3}{##1}##2\end}%
      \fi}% end of \r@placestring definition
      \expandafter\def\expandafter\r@plac@string
          \expandafter##\expandafter1%
          \expandafter##\expandafter2%
          \expandafter##\expandafter3%
          \the#1\end{##2##1##3}%
      \expandafter\expandafter\expandafter\r@placestring\expandafter\the\expandafter#2\the#1\end
}

\newtoks\toksa
\newtoks\toksb
\newtoks\toksc
\newtoks\toksd

\def\yybreak#1#2\yycontinue{\fi#1}

\def\eatone#1{}
\def\eatonespace#1 {}
\def\identity#1{#1}
\def\yyfirstoftwo#1#2{#1}
\def\yysecondoftwo#1#2{#2}
\def\yysecondofthree#1#2#3{#2}
\def\yythirdofthree#1#2#3{#3}

% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence

\def\yypreparsetokensequenc@#1#2#3{%
    \yystringempty{#2}{#1{#3}}{\yypreparsetokensequen@@{#1}{#2}{#3}}%
}

\def\yypreparsetokensequen@@#1#2#3{% remaining sequence is nonempty
    \yystartsinbrace{#2}{\yydealwithbracedgroup{#1}{#2}{#3}}{\yypreparsetokensequ@n@@{#1}{#2}{#3}}%
}

\def\yydealwithbracedgroup#1#2#3{% the first token of the remaining sequence is a brace
    \iffalse{\fi\yydealwithbracedgro@p#2}{#1}{#3}%
}

\def\yydealwithbracedgro@p#1{%
    \yypreparsetokensequenc@{\yyrepackagesequence}{#1}{}%
}

% #1 -- parsed sequence
% this is a sequence to `propagate expansion' into the next parameter.
% the same can be achieved by packaging the whole sequence with a 
% \csname ... \endcsname pair and using a simple \expandafter
% maybe that would be a better idea ...

\def\yyrepackagesequence#1{%
    \yyrepackagesequenc@{}#1\end
}

% #1 -- `packaged' sequence (\expandafter\expandafter\expandafter ? ...)
% #2 -- the next category 12 character or \end

\def\yyrepackagesequenc@#1#2{%
    \ifx#2\end
        \yybreak{\yyrepackagesequ@nc@{#1\expandafter\expandafter\expandafter}}%
    \else
        \yybreak{\yyrepackagesequenc@{#1\expandafter\expandafter\expandafter#2}}%
    \yycontinue
}

% #1 -- `packaged' sequence (\expandafter\expandafter\expandafter ? ...)
% this macro is followed by the remainder of the original sequence with a so far
% unmatched right brace, the `call stack' and the parsed sequence.

\def\yyrepackagesequ@nc@#1{%
    \expandafter\expandafter\expandafter\yyrepackagesequ@nc@swap#1{\expandafter\eatone\string}%
}

% #1 -- parsed sequence without packaging

\def\yyrepackagesequ@nc@swap#1#{%
    \yyrepackagesequ@nc@sw@p{#1}%
}

% #1 -- parsed `inner' sequence
% #2 -- remainder of the original sequence
% #3 -- `call stack'
% #4 -- parsed sequence so far

\def\yyrepackagesequ@nc@sw@p#1#2#3#4{%
    \yypreparsetokensequenc@{#3}{#2}{#4[#1]}%
}

% `braced group' thread ends here

% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence

\def\yypreparsetokensequ@n@@#1#2#3{% the remaining group in #2 is nonempty and does not start with a brace
    \yystartsinspace{#2}{\yyconsumetruespace{#1}{#2}{#3}}{\yypreparsetokenseq@@n@@{#1}{#2}{#3}}%
}

\def\yyconsumetruespace#1#2#3{%
    \expandafter\yyconsumetruespac@swap\expandafter{\eatonespace#2}{#1}{#3.}%
}

\def\yyconsumetruespac@swap#1#2#3{%
    \yypreparsetokensequenc@{#2}{#1}{#3}%
}

% `group starting with a true (character code 32, category code 10) space' thread ends here

% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence

\def\yypreparsetokenseq@@n@@#1#2#3{% a nonempty group, that does not start with a brace or a true space
    \yymatchblankspace{#2}{\yyrescanblankspace{#2}{#1}{#3}}{\yypreparsetokens@q@@n@@{#1}{#2}{#3}}%
}

% #1 -- remaining sequence
% #2 -- `call stack'
% #3 -- `parsed' sequence

\def\yyrescanblankspace#1#2#3{%
    \expandafter\expandafter\expandafter
        \yyrescanblankspac@swap
    \expandafter\expandafter\expandafter{\expandafter\yynormalizeblankspac@\meaning#1}{#2}{#3*}%
}

\def\yyrescanblankspac@swap#1#2#3{%
    \yystartsinspace{#1}{%
        \expandafter\yyrescanblankspac@sw@p\expandafter{\eatonespace#1}{#2}{#3}%
    }{%
        \expandafter\yyrescanblankspac@sw@p\expandafter{\eatone#1}{#2}{#3}%
    }%
}

\def\yyrescanblankspac@sw@p#1#2#3{%
    \yypreparsetokensequenc@{#2}{#1}{#3}%
}

% `group starting with a blank space' ends here

% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence

\def\yypreparsetokens@q@@n@@#1#2#3{% nonempty group starting with a non blank, non brace token
    \expandafter\yypreparsetokens@q@@n@@swap\expandafter{\eatone#2}{#1}{#30}%
}

\def\yypreparsetokens@q@@n@@swap#1#2#3{%
    \yypreparsetokensequenc@{#2}{#1}{#3}%
}

% #1 -- string of category code 12 or 10 characters
% #2 -- string of category code 12 or 10 characters

\def\yycomparesimplestrings#1#2{%
    \yystringempty{#1}{%
        \yystringempty{#2}{\yyfirstoftwo}{\yysecondoftwo}%
    }{\yycomparesimplestrings@{#1}{#2}}%
}

\def\yycomparesimplestrings@#1#2{% the first string is nonempty
    \yystringempty{#2}{\yysecondoftwo}{\yycomparesimplestrings@@{#1}{#2}}%
}

\def\yycomparesimplestrings@@#1#2{% both strings are nonempty
    \yystartsinspace{#1}{%
        \yystartsinspace{#2}{\yyabsorbfirstspace{#1}{#2}}{\yysecondoftwo}%
    }{%
        \yystartsinspace{#2}{\yysecondoftwo}{\yyabsorbfirstnonspace{#1}{#2}}%
    }    
}

\def\yyabsorbfirstspace#1#2{%
    \expandafter\yyabsorbfirstspac@swap\expandafter{\eatonespace#1}{#2}%
}

\def\yyabsorbfirstspac@swap#1#2{%
     \expandafter\yyabsorbfirst@swap\expandafter{\eatonespace#2}{#1}%
}

\def\yyabsorbfirstnonspace#1#2{%
    \expandafter\yyabsorbfirstnonspac@swap\expandafter{\eatone#1}{#2}%
}

\def\yyabsorbfirstnonspac@swap#1#2{%
     \expandafter\yyabsorbfirst@swap\expandafter{\eatone#2}{#1}%
}

\def\yyabsorbfirst@swap#1#2{%
     \yycomparesimplestrings{#2}{#1}%
}

% `compare strings of category code 12' thread ends here

% #1 -- remaining parsed sequence
% #2 -- analysed sequence

\def\yyanalysetokens@#1#2{%
    \yystringempty{#1}{{#2}}%
        {\yyanalysetok@ns@#1\end{#2}}%
}

\def\yyanalysetok@ns@#1#2\end{%
    \ifx#1.%
        \expandafter\yyfirstoftwo
    \else
        \expandafter\yysecondoftwo
    \fi
    {\yygrabablank{#2}}%
    {%
        \ifx#1[% not a space, an opening brace
            \expandafter\yyfirstoftwo
        \else
            \expandafter\yysecondoftwo
        \fi
        {%
            \yydisableobrace{#2}%
        }{% 
            \ifx#1]% not a space, a closing brace
                \expandafter\yyfirstoftwo
            \else
                \expandafter\yysecondoftwo
            \fi
            {%
                \yydisablecbrace{#2}%
            }{% neither space nor brace
                \yygrabtokenraw{#2}%
            }%
        }%
    }%
}

% #1 -- remaining parsed sequence
% #2 -- analysed sequence
% #3 -- next token

\def\yygrabtokenraw#1#2#3{%
    \expandafter\yyanalysetokens@swap\expandafter{\meaning#3}{#1}{#2}%
}

\def\yyanalysetokens@swap#1#2#3{%
    \yyanalysetokens@{#2}{#3t#1e}%
}

\def\yygrabablank#1#2 {%
    \yyanalysetokens@{#1}{#2s0e}%
}

% #1 -- remaining parsed sequence
% #2 -- analysed sequence

\def\yydisablecbrace#1#2{%
    \yydisablecbrac@{}#1\relax#2\end
}


\def\yydisablecbrac@#1#2{%
    \ifx#2\end
        \yybreak{\yydisablecbrac@@{#1\expandafter\expandafter\expandafter}}%
    \else
        \yybreak{\yydisablecbrac@{#1\expandafter\expandafter\expandafter#2}}%
    \yycontinue
}

\def\yydisablecbrac@@#1{%
    \expandafter\expandafter\expandafter
        \yydisablecbrace@@@#1\end
    \expandafter\expandafter\expandafter
        {\iffalse}\fi\string
}

\def\yydisablecbrace@@@#1\relax#2\end#3{%
    \yystartsinspace{#3}%
        {\expandafter\yyanalysetok@nsswap\expandafter{\eatonespace#3}{#1}{#2c1e}}%
        {\expandafter\yyanalysetok@nsswap\expandafter{\eatone#3}{#1}{#2c2e}}%
}

\def\yyanalysetok@nsswap#1#2#3{%
    \iffalse{\fi\yyanalysetokens@{#2}{#3}#1}%
}

% #1 -- remaining parsed sequence
% #2 -- analysed sequence

\def\yydisableobrace#1#2{%
    \yydisableobrac@{}#1\relax#2\end
}


\def\yydisableobrac@#1#2{%
    \ifx#2\end
        \yybreak{\yydisableobrac@@{#1\expandafter\expandafter\expandafter}}%
    \else
        \yybreak{\yydisableobrac@{#1\expandafter\expandafter\expandafter#2}}%
    \yycontinue
}

\def\yydisableobrac@@#1{%
    \expandafter\expandafter\expandafter
        \yydisableobrace@@@#1\end
    \expandafter\expandafter\expandafter
        {\iffalse}\fi\string
}

\def\yydisableobrace@@@#1\relax#2\end#3{%
    \yystartsinspace{#3}%
        {\expandafter\yyanalysetok@nsswap\expandafter{\eatonespace#3}{#1}{#2o1e}}%
        {\expandafter\yyanalysetok@nsswap\expandafter{\eatone#3}{#1}{#2o2e}}%
}

\uccode`\ =`\-

% \dotspace expands into a character code `\-, category code 10 token (funny space)

\uppercase{\def\dotspace{ }}

\toksa\expandafter\expandafter\expandafter{\expandafter\meaning\dotspace}

\toksb{-}

\toksc{#2}

\toksd\toksa

\yyreplacestring\toksb\in\toksa\with\toksc

\toksc{}
\yyreplacestring\toksb\in\toksd\with\toksc

\expandafter\def\expandafter\yymatchblankspac@\expandafter#\expandafter1\the\toksd{%
    \yystringempty{#1}{\expandafter\yysecondofthree\expandafter{\string}}%
        {\expandafter\yythirdofthree\expandafter{\string}}%
}

\edef\yymatchblankspace#1{% is it \catcode 10 token?
    \noexpand\iffalse{\noexpand\fi
    \noexpand\expandafter
    \noexpand\yymatchblankspac@
    \noexpand\meaning#1\the\toksd}%
}

% the idea behind the sequence below is that a leading character of category code 10
% is replaced either by a character of category code 10 and charachter code 32 or a character
% of category code 12 and character code other than 32
% note that while it is tempting to replace the definition below by something that ends in
% ... blank space #2{ ... with the hope of absorbing the result of \meaning in one step,
% this will not give the desired result in case of an active character,
% say, `~' that had been \let to the normal blank space

\expandafter\def\expandafter\yynormalizeblankspac@\expandafter#\expandafter1\the\toksd{}

\def\yystartsinspace#1{% is it \charcode 32, \catcode 10 token?
    \iffalse{\fi\yystartsinspac@#1 }%
}

\def\yystartsinspac@#1 {%
    \yystringempty{#1}{\expandafter\yysecondofthree\expandafter{\string}}{\expandafter\yythirdofthree\expandafter{\string}}%
}

\def\yystartsinbrace#1{%
  \iffalse{{\fi
  \if!\yytoks@mpty#1}}!%
    \expandafter\yysecondoftwo
  \else
    \expandafter\yyfirstoftwo
  \fi
}

\def\yystringempty#1{%
  \iffalse{{{\fi
  \ifcase\yytoks@mpty#1}}\@ne}\z@
    \expandafter\yyfirstoftwo
  \else
    \expandafter\yysecondoftwo
  \fi
}

\def\yytoks@mpty{%
    \expandafter\eatone\expandafter{\expandafter{%
        \ifcase\expandafter1\expandafter}\expandafter}\expandafter\fi\string
}

%% test code begins here

%\tracingmacros=3
%\tracingonline=3

\catcode`\ =13\relax%
\def\actspace{ }%
\catcode`\ =10\relax%

\catcode`\.=13\relax%
\def\actdotspace{.}%
\catcode`\.=12\relax%

\edef\makefunkydotspace{\let\expandafter\noexpand\actdotspace= \dotspace}
\edef\makefunkyspace{\let\expandafter\noexpand\actspace= \space}

\makefunkyspace
\makefunkydotspace

\catcode`\<=1
\catcode`\>=2
\uccode`\<=32
\uccode`\>=32

% inside the following sequence, < and > will become braces with character code 32 (space),
% \actspace will expand into an active character with character code 32, that has been \let to a
% character code 32, category code 10 token (space)

\uppercase{\edef\temptest{{ } \space\space\dotspace\expandafter\noexpand\actspace\expandafter\noexpand\actdotspace{<> {{}{{ u o l k kk
    \end\noexpand\fi\noexpand\else\noexpand\iffalse{}} }}}}}

%\uppercase{\edef\temptest{\dotspace E <>}}

\show\temptest

\def\displaypreparse#1{%
    \expandafter\errmessage\expandafter{\romannumeral-1\yypreparsetokensequenc@{\yyanalysetokens@}{#1}{}{}#1}%
}

\expandafter\displaypreparse\expandafter{\temptest}

\end

관련 정보