Xác định macro tạo macro nhận đối số

Sep 11 2020

Tôi đã sử dụng latex khá lâu rồi, nhưng chỉ giới hạn ở những điều rất cơ bản. Tôi cố gắng sử dụng các gói bất cứ khi nào có thể. Bây giờ tôi đang cố gắng tự viết một số mã để làm cho tài liệu cao su của tôi sạch hơn. Đây là những gì tôi muốn đạt được:

Khi tôi xác định:

\symb{flow}{f_#1^#2}[a,b]

Tôi muốn có thể sử dụng

\flow        % outputs $f_a^b$
\flow[x,y]   % outputs $f_x^y$

Lưu ý rằng số lượng chỉ số không nhất thiết phải là 2, nó có thể là 1 hoặc nhiều hơn 2.

Sau đây là những gì tôi có bây giờ:

\NewDocumentCommand{\symb}{m m m}
{ \expandafter\NewDocumentCommand\csname#1\endcsname{>{\SplitList{,}}O{#3}}
    { 
        % Not sure what I need to write here
    }
}

Về cơ bản, tôi muốn có thể sử dụng \symb{flow}{f_#1^#2}[a,b]để xác định một macro \flowcó đối số tùy chọn, là các chỉ số được phân tách bằng dấu phẩy của biến. Trong trường hợp đối số tùy chọn không được cung cấp, các chỉ số mặc định (trong trường hợp này là a, b) sẽ được sử dụng.

Trong python, điều này sẽ được viết là:

def symb(expr, default):
    def fn(*args):
        if len(args) == 0:
            return expr % args
        else:
            return expr % default

    return fn

flow = symb('$f_%s^%s$', (a, b))

flow()        % outputs $f_a^b$
flow(x, y)    % outputs $f_x^y$

Trả lời

15 egreg Sep 10 2020 at 23:23

Bạn có thể làm điều đó, nhưng bạn không nên lấy Python làm hình mẫu để lập trình trong LaTeX.

Trong LaTeX, các đối số được đặt trong dấu ngoặc nhọn và không thể thay thế bằng danh sách dấu phẩy.

Dù sao, đây là một triển khai với bất kỳ số lượng đối số nào trong mẫu (nhưng bạn tất nhiên phải chỉ định số lượng bạn muốn).

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\symb}{mmmm}
 {% #1=name of command to define, #2=number of args, #3=template, #4=default

  % define the internal version
  \cs_new_protected:cn { kelvin_symb_#1:\prg_replicate:nn{#2}{n} } { #3 }

  % define the external version
  \exp_args:Nc \NewDocumentCommand { #1 } { O{#4} }
   {
    \__kelvin_symb_do:nnx { #1 } { #2 } { \clist_map_function:nN { ##1 } \__kelvin_symb_brace:n }
   }
 }

\cs_new:Nn \__kelvin_symb_do:nnn
 {
  \use:c { kelvin_symb_#1:\prg_replicate:nn{#2}{n} } #3
 }
\cs_generate_variant:Nn \__kelvin_symb_do:nnn { nnx }

\cs_new:Nn \__kelvin_symb_brace:n { {#1} }

\ExplSyntaxOff

\symb{flow}{2}{f_{#1}^{#2}}{a,b}
\symb{foo}{4}{\int_{#1}^{#2}#3\,d#4}{0,1,x,x}

\begin{document}

$\flow$

$\flow[x,y]$

$\foo$

$\foo[a,b,f(x),x]$

\end{document}

Một số từ khác trên mã. Đầu tiên những điều dễ dàng:

\exp_args:Nc \NewDocumentCommand { #1 } { O{#4} }

là expl3phiên bản của

\expandafter\NewDocumentCommand\csname#1\endcsname { O{#4 } }

và nên được ưu tiên.

Tiếp theo là mô tả về cách \symbhoạt động. Đầu tiên, nó định nghĩa một hàm bên trong với bao nhiêu đối số được khai báo; văn bản thay thế của nó được cung cấp bởi mẫu đã cho. Do đó \symb{flow}{2}{f_{#1}^{#2}}{a,b}xác định

\kelvin_symb_flow:nn { f_{#1}^{#2} }

Ghi chú. Không có vấn đề gì với thực tế _là một bức thư trong phạm vi \ExplSyntaxOn, bởi vì khai báo \symb{flow}{2}{...}{...}được đưa ra bên ngoài phạm vi đó.

Sau đó, chúng tôi xác định lệnh cấp người dùng \flowvới một đối số tùy chọn, có đối số mặc định là đối số thứ tư \symb. Lệnh này gián tiếp gọi hàm đã định nghĩa trước đó bằng \__kelvin_symb_do:nnx.

Đối số đầu tiên, trong trường hợp này flow, đối số thứ hai là số đối số; mục đích của chúng là có thể gọi hàm bên trong. Đối số cuối cùng là danh sách dấu phẩy (mặc định hoặc đối số được cung cấp làm đối số tùy chọn \flow) nhưng được xử lý trước để tạo ra danh sách các mục có dấu ngoặc nhọn.

Phiên bản bình thường \__kelvin_symb_do:nnnchỉ tạo hàm bên trong \kelvin_symb_flow:nnvà bỏ ký hiệu đối số thứ ba. Nhưng chúng tôi đang sử dụng một biến thể của nó; khi nào

\clist_map_function:nN { #1 } \__kelvin_symb_brace:n

được mở rộng hoàn toàn (vì xbiến thể), nếu áp dụng cho a,bnó sẽ mang lại {a}{b}. Vì vậy, chúng tôi kết thúc với

\kelvin_symb_flow:nn { a } { b }

và LaTeX rất vui được mở rộng như bình thường f_{a}^{b}.

8 wipet Sep 10 2020 at 22:52

Ví dụ, bạn có thể sử dụng cái này:

\def\sdef#1{\expandafter\def\csname#1\endcsname}

\def\symb#1#2[#3,#4]{%
  \sdef{#1}{\expandafter\futurelet\expandafter\next\csname#1:a\endcsname}%
  \sdef{#1:a}{\ifx\next[\csname#1:b\expandafter\endcsname 
              \else     \csname#1:b\endcsname[#3,#4]\fi}%
  \sdef{#1:b}[##1,##2]{#2}%
}  

\symb{flow}{f_#1^#2}[a,b]

$\flow$ and $\flow[x,y]$.

Phiên bản thứ hai của macro này thực hiện yêu cầu của bạn từ các nhận xét:

\def\sdef#1{\expandafter\def\csname#1\endcsname}
\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}

\def\symb#1#2[#3]{%
  \sdef{#1}{\expandafter\futurelet\expandafter\next\csname#1:a\endcsname}%
  \sdef{#1:a}{\ifx\next[\csname#1:b\expandafter\endcsname 
              \else     \csname#1:c\endcsname #3,,,,,,,,\end \fi}%
  \sdef{#1:b}[##1]{\def\paramslistA{#3,}\def\paramslistB{}\setparams ##1,\end,
     \csname#1:c\expandafter\endcsname \paramslistB,,,,,,,,\end}%
  \sdef{#1:c}##1,##2,##3,##4,##5,##6,##7,##8,##9\end{#2}%
}
\def\setparams #1,{\ifx\end#1%
      \expandafter\addto\expandafter\paramslistB\expandafter{\paramslistA}%
   \else \expandafter \setparamslist \paramslistA \end #1,%
   \expandafter\setparams\fi}
\def\setparamslist#1,#2\end#3,{\def\paramslistA{#2}\addto\paramslistB{#3,}}

\symb{flow}{f_#1^#2}[a,b]

\symb{test}{test: 1=#1, 2=#2, 3=#3, 4=#4}[a,b,c,d]

$\flow$ and $\flow[x,y]$.

\test

\test[mmm]

\test[x,y,z]

Giải pháp này khác với giải pháp của egreg: bạn không cần phải giải thích 3 macro được tạo phức tạp, chỉ sử dụng các nguyên thủy TeX.

5 DavidCarlisle Sep 10 2020 at 22:58

Điều này tương tự như wipet nhưng làm cho đối số để symb bắt buộc như đã thảo luận trong phần bình luận

\documentclass{article}

\def\symb#1#2#3{%
\expandafter\def\csname x#1\endcsname##1##2{#2}%
\expandafter\newcommand\csname #1\endcsname[1][#3]{%
  \expandafter\splitcomma\csname x#1\endcsname ##1\relax}}

\def\splitcomma#1#2,#3\relax{#1{#2}{#3}}

\begin{document}

\symb{flow}{f_#1^#2}{a,b}


$\flow$

$\flow[x,y]$

\end{document}

Và một phiên bản cho phép nhiều (tối đa 8) mục nhập trong danh sách đối số.

\documentclass{article}

\def\symb#1#2#3{%
\expandafter\def\csname x#1\endcsname##1##2##3##4##5##6##7##8##9{#2}%
\expandafter\newcommand\csname #1\endcsname[1][#3]{%
  \expandafter\splitcomma\csname x#1\endcsname ##1,,,,,,,,,,\relax}}


\def\splitcomma#1#2,#3,#4,#5,#6,#7,#8,#9\relax{#1{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}\relax}

\begin{document}

\symb{flow}{f_#1^#2}{a,b}

\symb{flowb}{f_#1^#2g_#3^#4}{a,b,c,d}


$\flow$

$\flow[x,y]$

$\flowb$

$\flowb[x,y,w,z]$

\end{document}