
오랜 독자이자 처음으로 포스터를 읽은 독자입니다.
목록을 반복하는 매크로를 만드는 방법에 대해 질문이 있습니다. 다른 사용자가 게시한 몇 가지 질문을 읽었지만 모두 LaTeX를 복잡한 방식으로 사용하는 것과 관련이 있습니다. 내 질문은 일반 TeX를 사용하여 목록을 반복하고 이전 목록의 각 요소를 수정하여 새 목록을 구성하는 것에 관한 것입니다. 나는 현재 TeX를 스스로 학습하고 있으며 몇 가지 기본적이지만 다재다능하고 강력한 매크로를 프로그래밍하면 TeX가 어떻게 작동하는지 더 잘 이해하는 데 도움이 될 것이라고 생각합니다. 어쨌든, 내 질문에 대한 충분한 배경 지식이 있습니다.
이것은 지금까지 내 코드입니다.
반복자를 재귀적으로 정의했습니다. 한 번에 하나의 인수를 읽고, 인수가 쉼표인지 중지 표시자(\myStop이라고 하는 자체 정의)인지 확인하고, 쉼표이면 계속 이동하고, 중지 표시자이면 중지하고, 그렇지 않으면 다음으로 요소를 대체합니다. 그 자체와 문자열(또는 토큰 목록) "+ 1".
\edef\myStop{)}
\def\Iterator#1{
\ifx\myStop#1 %do nothing {end of list}
\else %
\ifx,#1 \expandafter\IteratorIntermediateOne % currently 2 if-statements deep
\else #1 + 1, \expandafter\IteratorIntermediateOne %currently 2 if-statements deep
\fi%
\fi%
}
현재 IteratorIntermediateOne
.\expandafter\Iterator
\expandafter
\expandafter{\expandafter\Iterator}\fi\fi
이것이 내 첫 번째 질문인 것 같습니다. 중첩된 \expandafter
명령을 정의하는 방법이 있습니까?
이제 모든 내용이 컨텍스트에 포함되었으므로 모든 코드는 다음과 같습니다.
\edef\MyList{1,2,3}
\edef\myStop{)}
\def\IteratorIntermediateOne{\expandafter\Iterator}
\def\Iterator#1{%
\ifx\myStop#1 %do nothing {end of list}
\else %
\ifx,#1 \expandafter\IteratorIntermediateOne % currently 2 if-statements deep
\else #1 + 1, \expandafter\IteratorIntermediateOne %currently 2 if-statements deep
\fi%
\fi%
}
\edef\newList{\expandafter\Iterator\MyList\myStop}
면책조항: 마지막 요소 뒤에 추가 쉼표가 있다는 것을 알고 있습니다. 또한 잘못 구성된 목록을 감지할 수 있는 사례가 충분하지 않다는 것도 알고 있습니다. 나는 부적절하게 구성된 토큰 목록으로부터 매크로를 보호하는 방법을 상상할 수 있을 만큼 TeX에 대해 충분히 알지 못합니다. 따라서 경험이 많은 여러분이 이 코드를 비웃을 수밖에 없다면 사과드립니다.
좋아요, 제 또 다른 질문은 이것입니다: 자신을 호출하는 매크로를 정의하는 더 효율적인 방법이 있습니까? 반복자 매크로를 별도로 정의하고 TeX의 내장 \loop
명령을 사용하여 호출하는 것이 더 나은 방법입니까? 그렇다면 TeX의 눈, 입, 식도 및 위 과정의 맥락에서 루프 호출을 이해하는 데 어려움이 있기 때문에 누군가 내가 어떻게 할 것인지 안내해 줄 수 있습니까? 루프가 매크로를 확장하고 \repeat
위로 도달하여 매크로로 돌아가나요? 어디서도 좋은 설명을 찾을 수 없습니다.
모든 도움에 감사드립니다!
답변1
의도는 각 항목에 1을 추가하는 것 같습니다. 다음과 같이 코딩하겠습니다(etex라고 가정).
\edef\MyList{1,2,3,25,456,2}
\def\Iterator#1{\expandafter\xiterator#1\stopiteration,}
\def\xiterator#1,{\the\numexpr#1+1\relax,\xiterator}
\def\stopiteration#1\relax#2\xiterator{#1\relax}
\message{\Iterator\MyList}
\bye
메시지를 만드는 것
2,3,4,26,457,3
답변2
\input listofitems
\def\MyList{1,2,3,25,456,2}
\def\processlist#1{%
\readlist\myterms\MyList
\foreachitem\z\in\myterms{%
\ifnum\zcnt=1\else,\fi
\the\numexpr\z+1\relax
}%
}
\processlist\Mylist
\bye
실제로 필요한 경우구하다업데이트된 목록은 토큰 목록에서 수행할 수 있습니다 \mytoks
.
\input listofitems
\newtoks\mytoks
\def\MyList{1,2,3,25,456,2}
\def\processlist#1{%
\mytoks{}%
\readlist\myterms\MyList
\foreachitem\z\in\myterms{%
\ifnum\zcnt=1\else\mytoks\expandafter{\the\mytoks,}\fi
\mytoks\expandafter\expandafter\expandafter{%
\expandafter\the\expandafter\mytoks\the\numexpr\z+1\relax}
}%
}
\processlist\Mylist
List is \the\mytoks
\bye
답변3
당신은 말했다:
...경험이 많은 분들이 이 코드를 비웃을 수밖에 없다면 사과드립니다.
내가 TeX을 배우기 시작했을 때, 나는 그것이 매우 가파른 학습 곡선이라는 느낌을 받았습니다.
때때로 나는 좌절감을 느꼈습니다. ;-)
나는 당신과 같은 학습 곡선을 따르는 사람들이 매크로 프로그래밍/TeX 프로그래밍에 대한 시도를 비웃는 것이 적절한 상황에 있다고 믿지 않습니다.
나는 이성에 기초하고 그 자체로는 나쁜 행위가 아닌 방식으로 좋은 것을 달성하거나 배우려는 모든 시도가 기본적으로 존중받을 가치가 있다고 생각합니다.
아래 예제의 코드가 어떻게 작동하는지에 대해 궁금한 점이 있으면 주저하지 말고 문의하세요. 그런 다음 코드가 어떻게 작동한다고 생각하는지, 이해하는 데 어려움을 겪는 부분을 설명하는 것이 유용합니다. 내 경험에 따르면, 이는 응답자가 정확히 어떤 정보(예: TeX 기본 요소가 작동하는 방식과 TeXbook의 뒷장에서 간략하게 암시하는 "부작용" 중 프로그래밍 트릭에 사용되는 정보)를 정확히 알아내는 것을 더 쉽게 만듭니다. 이해가 부족합니다.
쉼표 목록의 항목이 공백으로 둘러싸여 있지 않고 -primitive가 \relax
쉼표 목록 내에서 발생하지 않으며 \numexpr
ε-TeX 확장을 사용할 수 있다고 가정하면 아마도 다음과 같이 할 수 있습니다.
\long\def\gobble#1{}%
\long\def\firstofone#1{#1}%
\def\Iterator#1#2,{%
% #1 - element-separator to prepend; empty in the 1st iteration;
% comma in consecutive iterations
% #2 - either current element of old list or the \relax that was
% appended for denoting the end of the list
\ifx\relax#2\expandafter\gobble\else\expandafter\firstofone\fi
{%
#1\number\numexpr#2+1\relax\Iterator{,}%
}%
}%
\def\MyList{1,2,3}
\edef\newList{\expandafter\Iterator\expandafter{\expandafter}\MyList,\relax,}
\begingroup
\tt
\string\MyList: \meaning\MyList
\string\newList: \meaning\newList
\endgroup
\bye
이 예제의 요점은 다음과 같습니다.
TeX 프리미티브 \edef
의 -definition-text 내에서 확장에 사용됩니다 . 또한 시퀀스가 추가됩니다.\newList
\expandafter
\MyList
,\relax,
이러한 방식으로 정의할 때 일부 단계 \newList
\edef
에서 정의 텍스트의 확장을 주도하면 \newList
시퀀스가 생성됩니다 \Iterator{}Comma,sparated,items,from,\MyList,\relax,
.
따라서 \relax,
목록의 끝을 표시합니다.
이제 여전히 \edef
-expansion 에 의해 구동됨 \Iterator
- (재귀적으로) 구분되지 않은 인수를 선택합니다 #1
(첫 번째 반복에서는 비어 있고 연속 반복에서는 쉼표를 보유합니다. 즉, 새 목록의 항목 앞에 추가할 구분 기호를 보유합니다). 그리고 의 쉼표 목록 #2
에서 오는 다음 항목을 보유 \myList
하거나 끝 표시자를 보유하는 쉼표로 구분된 인수 \relax
, 그리고 어떤 경우에든 다음 반복을 나타내는 토큰 시퀀스를 중괄호 안에 중첩하여 배치합니다.
- undelimited-argument
#1
, 즉 새 목록의 다음 항목 앞에 와야 하는 구분 기호, \number\numexpr#2+1\relax
쉼표로 구분된 인수로 표시되는 값에 1을 더하고 이 방식으로 새 목록의 다음 항목을 형성하는 표현식 ,- 확장에서 남은 다음 항목을 처리하기 위해 자신을 호출합니다
\myList
. 이번에는 무제한 인수 내에 쉼표를 제공하여 다음 번에 새 목록의 다음 항목 앞에 쉼표가 올 것임을 나타냅니다.
이를 통해 \ifx\relax#2
쉼표 목록/ 확장 \relax
시작 부분에 목록에 추가된 항목의 끝에 \edef
도달했는지 확인합니다. 그렇다면 다음 반복을 나타내는 중괄호 안에 중첩된 토큰 시퀀스가 "중복/제거"되어 \gobble
수행되지 않아 반복/꼬리 재귀가 종료됩니다. 그렇지 않은 경우 해당 시퀀스가 처리된 이후에 적용하여 주변 중괄호가 해당 시퀀스에서 제거됩니다 \firstofone
.
#1
새 목록의 항목 앞에 추가할 구분 기호를 보유하는 의 무제한 인수는 \Iterator
첫 번째 반복에서만 비어 있습니다. 각 연속 반복에는 해당 연속 반복의 이전 반복에서 \Iterator
-매크로 자체에 의해 토큰 시퀀스의 일부로 제공되어 다음 반복을 형성한 쉼표가 있습니다 . 이 방법으로만 새 목록의 첫 번째 항목 앞에 쉼표가 오지 않습니다.
ε-TeX 확장 기능을 사용할 수 없다면 \numexpr
음수가 아닌 정수를 증가시키는 루틴을 제공할 수 있습니다. ("실생활"에서는 패키지에 관심이 있을 수 있습니다.intcalc그리고bigintcalc.)
%------------------------------------------------------------------------------
% Expandable incrementing of non-negative integer number formed by a sequence
% of explicit catcode-12-character-tokens from the set {0,1,2,3,4,5,6,7,8,9}
%..............................................................................
% \Increment{<non-negative integer number k as sequence of explicit
% catcode-12-character-tokens from the set 0123456789>}
% ->
% <natural number (k+1) as sequence of explicit catcode-12-character-tokens
% from the set 0123456789>
% In expansion-contexts the result is delivered after two expansion-steps/is
% obtained by "hitting" \Increment with \expandafter twice.
%------------------------------------------------------------------------------
\def\Increment#1{%
\romannumeral0%
\IncrementReverse{\IncrementFork{}}{\relax}{}#1\relax
}%
\def\IncrementReverse#1#2#3#4{%
% #1 - tokens to prepend to reversed list
% #2 - tokens to append to reversed list
% #3 - reversed list constructed so far
% #4 - current element of not-reversed list
\ifx\relax#4%
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi
{#1#3#2}{\IncrementReverse{#1}{#2}{#4#3}}%
}%
\def\IncrementSelect#10123456789\relax#2#3!!{#2}%
\def\IncrementFork#1#2{%
% #1 - digits incremented so far
% #2 - current digit to increment or end-marker \relax
\IncrementSelect
#2123456789\relax{\IncrementReverse{ }{}{}#11}%
0#223456789\relax{\IncrementReverse{ }{}{}#12}%
01#23456789\relax{\IncrementReverse{ }{}{}#13}%
012#2456789\relax{\IncrementReverse{ }{}{}#14}%
0123#256789\relax{\IncrementReverse{ }{}{}#15}%
01234#26789\relax{\IncrementReverse{ }{}{}#16}%
012345#2789\relax{\IncrementReverse{ }{}{}#17}%
0123456#289\relax{\IncrementReverse{ }{}{}#18}%
01234567#29\relax{\IncrementReverse{ }{}{}#19}%
012345678#2\relax{\IncrementFork{#10}}%
0123456789#2{\IncrementReverse{ }{}{}#11\relax}%
0123456789\relax{\IncrementReverse{ }{}{}#11#2}%
!!%
}%
%%-----------------------------------------------------------------------------
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\Iterator#1#2,{%
% #1 - element-separator to prepend
% #2 - current element of old list
\ifx\relax#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{}{%
#1\Increment{#2}\Iterator{,}%
}%
}%
\def\MyList{1,2,3}
\edef\newList{\expandafter\Iterator\expandafter{\expandafter}\MyList,\relax,}
\begingroup
\tt
\string\MyList: \meaning\MyList
\string\newList: \meaning\newList
\endgroup
\bye
없이 수행되는 루틴을 원하는 경우 -expansion- 및 인수 교환 기술을 \edef
사용할 수 있습니다 . -expansion의 요지는 다음과 같습니다.\romannumeral0
\romannumeral0
- TeX는 다음에 속하는 토큰을 수집하면서 확장 가능한 토큰을 확장합니다.⟨숫자⟩- 로마숫자로 표현되는 수량.
- TeX가 수집하는 동안 발견한 첫 번째 토큰인 경우⟨숫자⟩-수량은 숫자입니다(예: )
0
에 속하는 토큰을 수집하는 프로세스입니다.⟨숫자⟩-수량은 더 많은 숫자를 모으는 과정으로 변하거나, 숫자가 아닌 무엇인가로 변해 모으는 과정을 종료시킨다. 확장형 토큰은 숫자를 수집하는 동안 확장됩니다. 숫자 시퀀스를 종료하는 공간 토큰은 더 많은 숫자를 수집하는 프로세스를 종료하고 자동으로 삭제됩니다. - 수집된 숫자가 양수가 아닌 경우 TeX는 토큰을 형성하는 토큰을 조용히 삼킬 것입니다.⟨숫자⟩-대답으로 토큰을 전달하지 않고 수량.
이는 \romannumeral
결국 양수가 아닌 숫자가 발견되는 한 TeX를 속여 많은 확장 및 인수 교환 작업을 수행하도록 사용할 수 있음을 의미합니다.
%------------------------------------------------------------------------------
% Expandable incrementing of non-negative integer number formed by a sequence
% of explicit catcode-12-character-tokens from the set {0,1,2,3,4,5,6,7,8,9}
%..............................................................................
% \Increment{<non-negative integer number k as sequence of explicit
% catcode-12-character-tokens from the set 0123456789>}
% ->
% <natural number (k+1) as sequence of explicit catcode-12-character-tokens
% from the set 0123456789>
% In expansion-contexts the result is delivered after two expansion-steps/is
% obtained by "hitting" \Increment with \expandafter twice.
%------------------------------------------------------------------------------
\def\Increment#1{%
\romannumeral0%
\IncrementReverse{\IncrementFork{}}{\relax}{}#1\relax
}%
\def\IncrementReverse#1#2#3#4{%
% #1 - tokens to prepend to reversed list
% #2 - tokens to append to reversed list
% #3 - reversed list constructed so far
% #4 - current element of not-reversed list
\ifx\relax#4%
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi
{#1#3#2}{\IncrementReverse{#1}{#2}{#4#3}}%
}%
\def\IncrementSelect#10123456789\relax#2#3!!{#2}%
\def\IncrementFork#1#2{%
% #1 - digits incremented so far
% #2 - current digit to increment or end-marker \relax
\IncrementSelect
#2123456789\relax{\IncrementReverse{ }{}{}#11}%
0#223456789\relax{\IncrementReverse{ }{}{}#12}%
01#23456789\relax{\IncrementReverse{ }{}{}#13}%
012#2456789\relax{\IncrementReverse{ }{}{}#14}%
0123#256789\relax{\IncrementReverse{ }{}{}#15}%
01234#26789\relax{\IncrementReverse{ }{}{}#16}%
012345#2789\relax{\IncrementReverse{ }{}{}#17}%
0123456#289\relax{\IncrementReverse{ }{}{}#18}%
01234567#29\relax{\IncrementReverse{ }{}{}#19}%
012345678#2\relax{\IncrementFork{#10}}%
0123456789#2{\IncrementReverse{ }{}{}#11\relax}%
0123456789\relax{\IncrementReverse{ }{}{}#11#2}%
!!%
}%
%%-----------------------------------------------------------------------------
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\def\Iterator#1,#2\relax#3#4{%
% #1 - current element of old list
% #2 - remaining elements of old list
% #3 - element-separator to prepend
% #4 - new list constructed so far
\ifx\relax#1\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{ #4}{%
\expandafter\exchange
\expandafter{%
\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\exchange
\expandafter\expandafter\expandafter{%
\Increment{#1}}{ #4#3}}}{\Iterator#2\relax{,}}%
}%
}%
\def\MyList{0,1,2,3}
\expandafter\def
\expandafter\newList
\expandafter{%
\romannumeral0\expandafter\Iterator\MyList,{\relax},\relax{}{}}%
\begingroup
\tt
\string\MyList: \meaning\MyList
\string\newList: \meaning\newList
\endgroup
\bye
답변4
처음이시므로 로 시작하시면 됩니다 expl3
.
\documentclass{article}
\usepackage{xparse,xfp}
\ExplSyntaxOn
\NewDocumentCommand{\generatelist}{mmm}
{% #1=output, #2=input, #3=iterator
\harry_list_generate:nnn { #1 } { #2 } { #3 }
}
% variables
\clist_new:N \l__harry_list_input_clist
\clist_new:N \l__harry_list_output_clist
% the main function
\cs_new_protected:Nn \harry_list_generate:nnn
{
% if the input is a single token, assume it is a control sequence
\tl_if_single:nTF { #2 }
{ \clist_set_eq:NN \l__harry_list_input_clist #2 }
{ \clist_set:Nn \l__harry_list_input_clist { #2 } }
% now \l__harry_list_input_clist contains the input
% clear the output list
\clist_clear:N \l__harry_list_output_clist
% map the input list applying the iterator to each item
\clist_map_inline:Nn \l__harry_list_input_clist
{
\clist_put_right:Nx \l__harry_list_output_clist { #3 { ##1 } }
}
% make the output list
\clist_set_eq:NN #1 \l__harry_list_output_clist
}
\ExplSyntaxOff
% two example iterators
\newcommand{\addone}[1]{\inteval{#1+1}}
\newcommand{\addhyphens}[1]{-#1-}
% a control sequence expanding to a list
\newcommand{\List}{1,2,3,41}
\generatelist{\ListA}{\List}{\addone}
\generatelist{\ListB}{1,2,3}{\addhyphens}
\show\ListA
\show\ListB
이 출력
> \ListA=macro:
->2,3,4,42.
l.50 \show\ListA
?
> \ListB=macro:
->-1-,-2-,-3-.
l.51 \show\ListB