雾月
雾月
这家伙很懒,什么也没写!

注册于 3年前

回答
184
文章
2
关注者
19

并不是所有 x 中的 # 都需要双写。直接用 primitive 实现的一般不需要双写。但是即使这样也有例外情况,这应该是 LaTeX3 的函数命名不一致导致的,由于 \cs_new:Npx 这些函数使用广泛,目前看来不太可能再修改它们的名称了。目前我记得的不需要双写的 #x 参数有 \cs_new...\cs_(g)set... 系列以及 \iow_shipout_x:Nn。所有的 \exp_.. 中的 x 都需要双写(包括 \cs_generate_variant:Nn\prg_generate_conditional_variant:Nnn 生成的变体)。
需要双写的原因是定义了一个临时的宏,见后文。

# 作为引用参数的标识时,它应该仅写一次(随嵌套次数而倍增),当 # 作为它自己时,应该双写。
需要双写的 # 可以使用 \exp_not:n 包裹起来,这样就不要双写。如可以 \use:x { \exp_not:n { \def\tmp#1{#1} } }

\cs_new:Npx 相当于 \long\edef。这里的 x 其实是 e。

定义函数时,\edef\test#1#2{#1,#2},括号里的 #1 #2 是引用第一、二个参数,应该把它们看成一个整体作为 1 个记号,## 也应当看成一个记号。\test 实际上是:两个参数的宏,它展开为 <参数1>,<参数2>
\edef\test#1{\def\noexpand\foo##1#1##2{##1 ##2 #1 \def\foo####1{####1##2}}}\test 是有一个参数的宏,它展开为 \def\foo#1<参数1>#2{#1 #2 <参数1> \def\foo##1{##1#2}}

\use:x 实际上是 \def\use:x#1{\edef\temp{#1}\temp},当写 \use:x{?#1?} 时,\use:x 展开为 \edef\temp{?#1?}\temp,此时在定义 \temp 时就会出错,因为它没有参数,但它的替换文本中却引用了参数,所以必须双写 \use:x{?##1?},此时 ## 表示 #,而不是引用参数的标识。

\use:e 则不是这样,它使用 \expanded primitive 实现,没有定义一个临时的宏,# 直接就是作为它自己。

还是那句话,能使用 e 就使用 e 而非 x

1.
你的理解是对的,定义函数时遵循这个规则可以在需要展开其参数时不至于有太大的开销。
如果需要展开 nofe 参数之后的参数,如 \foo:Nno\foo:Nne\foo:Nff\foo:NnV\foo:no,只有使用 \expanded primitive(\tex_expanded:D)才是最快的。而所谓的“optimized function”则总可以找到一个不用 \expanded 实现且比 \expanded 快的方法。(“最快”与“更快”都是从一般的情况来说的)

就是说,使用 \expanded 实现的函数都认为是需要更慢的处理。

在终端中执行 latexdef -b \ExplSyntaxOn -a \ExplSyntaxOff \exp_args:NVo 可以查看 \exp_args:NVo 的定义,(目前)它是用 \expanded 实现的,但可以找到一个不用 \expanded (且比它快)的实现:

\cs_gset:Npn \exp_args:NVo #1#2#3
  {
    \exp_after:wN #1
    \exp_after:wN { \exp:w \exp_after:wN
      \__exp_eval_register:N \exp_after:wN #2 \exp_after:wN }
    \exp_after:wN { #3 }
  }

\exp_args:NNVV\exp_args:NcVV 也是如此。但由于它们目前都是使用 \expanded 实现的,因此才把它们归类到需要更慢的处理的函数上。

2.
last_unbraced 就是最后一个参数展开的结果的最外层不自动添加 {}。比如

\tl_set:Nn \foo { ab }
\exp_args:No \faa { \foo }          % 结果是 \faa { ab }
\exp_last_unbraced:No \faa { \foo } % 结果是 \faa ab

如果展开的结果中有括号,也不会移除括号。另外,Nc 参数的结果总是不自动添加 {}

3.
这些是用来定义 \exp_args:...\exp_last_unbraced:... 的。已经明确表示应当仅用于 l3expan 这个模块中,当然想在其它地方用也不是不可以。
\::: 作为分割符,前面的是展开方式,后面的是参数。
比如 \::n \::f \::o \::e \::e_unbraced \::: 就是 \::: 后面的那一项移除可能的括号,接着对随后的那些项依次进行指定的展开。(\.._unbraced 只能是最后一个)
好处是不受 TeX 的函数最多 9 个参数的限制,坏处是甚至可能比 \expanded 更慢。

把图片名分别保存到 seq 里即可。

\seq_new:N \g__my_chapter_oimage_seq % odd 
\seq_new:N \g__my_chapter_eimage_seq % even 
\seq_new:N \g__my_part_oimage_seq % odd 
\seq_new:N \g__my_part_eimage_seq % even 

\keys_define:nn { my/titleimage }
  {
    chapteroddimage  .code:n = 
      \seq_gset_from_clist:Nn \g__my_chapter_oimage_seq {#1} ,
    chapterevenimage .code:n = 
      \seq_gset_from_clist:Nn \g__my_chapter_eimage_seq {#1} ,
    partoddimage     .code:n = 
      \seq_gset_from_clist:Nn \g__my_part_oimage_seq    {#1} ,
    partevenimage    .code:n = 
      \seq_gset_from_clist:Nn \g__my_part_eimage_seq    {#1} ,
  }
\cs_new:Npn \__my_title_image:Nn #1#2 % image seq, number
  { \seq_item:Nn #1 { \int_mod:nn {#2} { \seq_count:N #1 } + 1 } }
\cs_new:Npn \my@chapterimagename
  {
    \int_if_odd:nTF { \value{page} } 
      { \__my_title_image:Nn \g__my_chapter_oimage_seq { \value{chapter} } }
      { \__my_title_image:Nn \g__my_chapter_eimage_seq { \value{chapter} } }
  }
\cs_new:Npn \my@partimagename
  {
    \int_if_odd:nTF { \value{page} }
      { \__my_title_image:Nn \g__my_part_oimage_seq { \value{part} } }
      { \__my_title_image:Nn \g__my_part_eimage_seq { \value{part} } }
  }
\cs_new_protected:Npn \titleimage #1 
  { \keys_set:nn { my/titleimage } {#1} }

使用时

\titleimage{
  chapteroddimage={inner_pics/songodd, inner_pics/yinghuaodd},
  chapterevenimage={inner_pics/songeven, inner_pics/yinghuaeven},
  ...
}

\includegraphics{\my@chapterimagename} % 会自动改变
\includegraphics{\my@partimagename}    % 会自动改变

放到文档类中时,不要用 my 作为模块名,改成你自己的名字。

1.
忽略类别码(catcode)是指在比较两个 token list 时,先把它们 \detokenize,然后再按字典序比较,(也就是不比较它们的类别码,因为经过 \detokenize 后,同一字符的类别码都相同,控制序列也没有了),即使用 \str_if_eq:...,反之,则使用 \tl_if_eq:...

\str_set:Nn \a { ~ \\ $ }
\tl_set:Nn  \b { ~ \\ $ }
\tl_set:Nx  \c { \c_space_tl \c_backslash_str \c_backslash_str \c_dollar_str }

\a\c 的值完全相同:即,它们的长度相同且每一对字符的 (catcode,charcode) 完全相同,也就是 \tl_if_eq:NNTF \a \c 返回 true 分支。
\b\c 的长度不同,即 \tl_if_eq:NNTF \b \c 返回 false 分支。但对 \b 的值使用 \detokenize\tl_to_str:n)之后,就变成了 \c(的值),也就是 \str_if_eq:NNTF \b \c 返回 true 分支。

prop 中 key 的比较就是使用 \str_if_eq:...
“忽略类别码”只有在比较 token list 时才有意义。

(在 (u)pTeX 中应该是 kcatcode 而非 catcode。)

2.
\prop_set_from_keyval:Nn 是使用 \keyval_parse:nnn 实现的。
\tl_trim_spaces:n 移除首尾空格,但在此之后,如果结果是一个正确嵌套的组(如 {a } 是,{a}{b} 不是),并不会继续移除外层的 {}。而 \keyval_parse:nnn 在分别解析 key 和 value 时则会继续移除外层的 {}

{~a~}~ = ~bc~ ,  % key 是 " a ",value 是 "bc"
~a~ = {~bc~}~ ,  % key 是 "a",value 是 " bc "
{ a= } = {b=c} , % key 是 "a=",value 是 "b=c"
{ a, } = {b,} ,  % key 是 "a,",value 是 "b,"
a =,             % key 是 "a",value 是 ""
a,               % key 是 "a",没有 value
这样,key 和 value 中可以包含空格、逗号和等号

如上例。\keyval_parse:nnn 并不会修改 key 和 value 的类别码。

3.
某些情况下可能会改变 =, 的类别码为 13(active),这样解析键值时就会出错,比如 keyval 宏包。
\keyval_parse:nnn 专门处理了这种情况。方法是递归。
先以 active , 分割出 n 项,然后对这 n 项分别以 other , 分割,由此得到 key=val。再对 = 施以类似的操作,由此得到每个 key 与 对应的 value。

代码中的多出的长度正好是 \@pnumwidth。这是因为没有正确设置目录中章的页码。
改为

\titlecontents{chapter}[0mm]
  {\addvspace{0em}}
  {\contentsmargin{0pt}\boxedd}
  {\contentsmargin{0pt}\boxeddd}
  {}
  [\addvspace{0em}]

即可。

load:该键只能作为宏包选项(或文档类选项)被设置;
preamble:该键只能在导言区设置;
general:该键只要有定义就能设置。

使用了 load 的键,只在使用 \SetKeys 时才能正确处理。使用 l3keys2e 宏包的设置键的命令不会正确处理。

使用了 preamble 的键,可以在导言区以及 begindocument/before 钩子(以及 etoolbox\AtEndPreamble)中被设置,无法在其它位置设置。

general,清除 loadgeneral 的效果,这是默认的情形。

设置 .usage:n 时,只是把键保存到 \l_keys_usage_load_prop\l_keys_usage_preamble_prop 中,等到设置键时,再根据键是否在它们中而决定是否能被设置(实际上,在加载宏包、在导言区之后,会重新定义这两个 prop 里的键,使得它们无法再被设置)。

下例中,有两个位置出错,因为它们在不正确的位置设置了某些键。
\DeclareKeys\SetKeys 由 LaTeX2e 内核提供,模块名是可选参数。

\documentclass{article}
\begin{filecontents}[force]{mypkg.sty}
\DeclareKeys[mypkg]{
  faa.store=\mypkgfaa,
  faa.usage=load,
  fbb.store=\mypkgfbb,
  fbb.usage=preamble,
  fcc.store=\mypkgfcc,
}
\ProcessKeyOptions[mypkg]
\end{filecontents}

\usepackage[faa=1,fbb=1,fcc=1]{mypkg}

\SetKeys[mypkg]{faa=2,fbb=2,fcc=2} % 出错:faa=2

\begin{document}
\SetKeys[mypkg]{fbb=3,fcc=3} % 出错:fbb=3

hello.
aa: \mypkgfaa,
bb: \mypkgfbb,
cc: \mypkgfcc.

\ExplSyntaxOn
\prop_show:N \l_keys_usage_preamble_prop
\ExplSyntaxOff

\end{document}

TeX 在获取命令的参数时,参数的 catcode 已经固定了,多个空格合并为一个,行结束符(一般是换行符)转为空格。所以再使用修改 catcode 的命令(比如 \obeyspaces)已经无效了。

要么用环境,且不能用 b 参数,要么原样获取参数,然后重新扫描(使用 \scantokens\tl_rescan:nn 及类似命令)。

\documentclass{article}
\begin{document}

\ExplSyntaxOn
\NewDocumentCommand{\test}{ O{\linewidth} +v }
  {
    \coffin_clear:N \l_tmpa_coffin
    \vcoffin_set:Nnn \l_tmpa_coffin {#1} 
      { % 注意如果 \parindent>0pt 这里会有缩进
        \small \ttfamily
        \obeyspaces
        \obeylines 
        \scantokens{#2}
        \ignorespacesafterend
      }
    \coffin_typeset:Nnnnn \l_tmpa_coffin {H}{l}{0pt}{0pt}
  }
\ExplSyntaxOff

\noindent % 移除段首缩进
\test{ha   ha
hahahha
}

\end{document}

当然也还有其它办法,不过比这个略微复杂。

PS:使用 varwidth 还能有不一样的效果:

\documentclass{article}
\usepackage{varwidth}
\begin{document}

\ExplSyntaxOn
\NewDocumentCommand{\testa}{ O{\linewidth} +v }
  {
    \coffin_clear:N \l_tmpa_coffin
    \hcoffin_set:Nn \l_tmpa_coffin 
      { 
        \varwidth [b] { \dim_eval:n {#1} } 
        \dim_set:Nn \parindent { \c_zero_dim }
        \small \ttfamily
        \obeyspaces
        \obeylines 
        \scantokens{#2}
        \endvarwidth 
      }
%    \coffin_typeset:Nnnnn \l_tmpa_coffin {H}{l}{0pt}{0pt}
    \coffin_display_handles:Nn \l_tmpa_coffin { blue }
  }
\NewDocumentCommand{\testb}{ O{\linewidth} +v }
  {
    \coffin_clear:N \l_tmpa_coffin
    \vcoffin_set:Nnn \l_tmpa_coffin {#1} 
      {
        \dim_set:Nn \parindent { \c_zero_dim }
        \small \ttfamily
        \obeyspaces
        \obeylines 
        \scantokens{#2}
        \ignorespacesafterend
      }
%    \coffin_typeset:Nnnnn \l_tmpa_coffin {H}{l}{0pt}{0pt}
    \coffin_display_handles:Nn \l_tmpa_coffin { red }
  }
\ExplSyntaxOff

\noindent
\testa{ha   ha
hahahha
}

\vspace{1cm}

\noindent
\testb{ha   ha
hahahha
}

\end{document}

image.png

tabularray 不支持这么写。只能把结果保存到 tl 里,然后用 expand 键。

\documentclass{ctexart}
\usepackage{tabularray}
\SetTblrInner[tblr]{%
    cells={c},
    hlines,
    vlines,
}
\begin{document}
\ExplSyntaxOn

\tl_clear:N \tableval 
\seq_set_split:Nnn \l_my_a_seq { , }{ a,b,c,d,ef }
\int_step_inline:nnn { 1 } { \seq_count:N \l_my_a_seq }
  {
    \int_compare:nNnTF {#1} < { \seq_count:N \l_my_a_seq }
      {
        \tl_put_right:Nn \tableval { #1 & }
      }
      {
        \tl_put_right:Nn \tableval  { #1 \\ }
      }
  }
\int_step_inline:nnn { 1 } { \seq_count:N \l_my_a_seq } 
  {
    \int_compare:nNnTF {#1} < { \seq_count:N \l_my_a_seq }
      {
        \tl_put_right:Nx \tableval { \seq_item:Nn \l_my_a_seq {#1} & }
      }
      {
        \tl_put_right:Nx \tableval { \seq_item:Nn \l_my_a_seq {#1} }
      }
  }
\ExplSyntaxOff

\begin{tblr}[expand=\tableval]{}
\tableval
\end{tblr} 
\end{document}

  1. 使用临时变量时,特别是需要用到 \.._put_left:Nn\.._put_right:Nn 等命令时,要注意先清空变量;
  2. 使用 \dim_compare:nNn.. 时,第 1、3 个参数必须是具体的长度(或长度表达式);
如此得到的 \l_tmpb_clist 为:{11.5632pt,34.68959pt,23.12639pt}

本例中,在使用 \clist_sort:Nn 之前,你可以用 \clist_show:N \l_tmpb_clist 看看是不是你想的那个结果:

\clist_set:Nn \l_tmpa_clist { ha, hahaha, haha }

\clist_clear:N \l_tmpb_clist
\clist_map_inline:Nn \l_tmpa_clist
  {
    \clist_put_right:Nn \l_tmpb_clist
      {
        \hcoffin_set:Nn \l_tmpa_coffin {#1}
        \dim_use:N \coffin_wd:N \l_tmpa_coffin
      }
  }
\clist_show:N \l_tmpb_clist

为什么结果不是 {11.5632pt,34.68959pt,23.12639pt} 而是

The comma list \l_tmpb_clist contains the items (without outer braces):
>  {\hcoffin_set:Nn \l_tmpa_coffin {ha}\dim_use:N \coffin_wd:N \l_tmpa_coffin}
>  {\hcoffin_set:Nn \l_tmpa_coffin {hahaha}\dim_use:N \coffin_wd:N \l_tmpa_coffin }
>  {\hcoffin_set:Nn \l_tmpa_coffin {haha}\dim_use:N \coffin_wd:N \l_tmpa_coffin }

因为你用的是 \clist_put_right:Nn,而
image.png
要把长度保存到 \l_tmpb_clist 里,而不是 \hcoffin_se...,可以这样:

\clist_set:Nn \l_tmpa_clist { ha, hahaha, haha }

\clist_clear:N \l_tmpb_clist
\clist_map_inline:Nn \l_tmpa_clist
  {
    \hcoffin_set:Nn \l_tmpa_coffin {#1}
    \clist_put_right:Nx \l_tmpb_clist
      { \dim_use:N \coffin_wd:N \l_tmpa_coffin }
  }

\clist_sort:Nn \l_tmpb_clist
  {
    \dim_compare:nNnTF {#1} > {#2}
      { \sort_return_same:    }
      { \sort_return_swapped: }
  }

\documentclass{article}
% \usepackage{amsmath}
% \usepackage{unicode-math}
\begin{document}

\def\cdotss{\cleaders\hbox to 0.5em{\hfil$\cdotp$\hfil}\hskip 3.1em\relax}

\begin{equation}
    9 \div 2 = 4 \cdotss 1
\end{equation}

\end{document}

image.png
间距自己改改就行了,0.5em 是点之间的间距,省略号的总长为 3.1em,3.1÷0.5=6···0.1,所以总共6个点,多余的 0.1em 作为空白,对半放在省略号的两边。

用一个计数器,每执行一次 mark,就增加 1,坐标点随之改变。

\documentclass[tikz]{standalone}
\usetikzlibrary{decorations.markings}
\usetikzlibrary{intersections,through,calc}
\newcounter{intercnt}
\begin{document}
\begin{tikzpicture}
\draw[->] (0,0) -- (8,0)node[right]{$X$};
\draw[->] (0,0) -- (0,3) node[right]{$Y$};        
\draw [line width=1pt,name path=l]    plot [domain=-0.16:3.6, samples=20] ({2*\x-0.2*(sin(atan(cos(\x r))))},{sin(\x r)+0.2*(cos(atan(cos(\x r))))});
\setcounter{intercnt}{0} % 初始化
\path [decorate, decoration={markings,
mark=between positions 0 and 1 step 1/5 with{
\stepcounter{intercnt} % 加 1
\edef\pointA{A-\arabic{intercnt}}
\edef\pointB{B-\arabic{intercnt}}
\draw[name path=v] (0,-5pt)--(0,26pt);
\draw [name intersections={of=v and l, by={\pointA}}][very thick,blue,fill=black]  (\pointA) circle(2pt);
\draw [shift={(0,0)},teal!30,line width=0.5pt,name path=c](0,0)circle(0.6);
\draw [name intersections={of=v and c, by={\pointB}}][very thick,green,fill=black]  (\pointB) circle(2pt);
}
}] {plot [domain=0:3.14, samples=10] ({2*\x-0.2*(sin(atan(cos(\x r))))},{sin(\x r)+0.2*(cos(atan(cos(\x r))))})};  

\foreach \i in {1,...,\arabic{intercnt}}
  {\node at (A-\i) {$A_\i$}; \node at (B-\i) {$B_\i$};};
\end{tikzpicture}    
\end{document} 

image.png

tabularray 做不到。LaTeX 格式下,现在所有在 CTAN 上发布的表格宏包都做不到自动在单元格中间断开。

ConTeXt 格式有几个表格可以做到,比如 https://wiki.contextgarden.net/TABLE

如果一定要在 LaTeX 中实现,可以参考
https://www.zhihu.com/question/592211454/answer/2957730520

image.png

code0 那个地方不是花括号,是方括号。

代码不要贴图片,不能复制。

箭头和圆一起画。

\documentclass[tikz,border=2pt]{standalone}
\usetikzlibrary {arrows.meta}
\usetikzlibrary{decorations.markings}
\usepackage{xfp}
\tikzset{paint/.style={ draw=#1!50!black, fill=#1!80 },
  gray arrow/.style={>={Stealth[gray,inset=0pt, length=4pt, angle'=20]}},
}

\begin{document}
\begin{tikzpicture}
\draw [blue,line width=1pt]
  plot [domain=-0.5:4.5, samples=60] (\x,\fpeval{1/(1+0.0075*10^{\x})});
\path [decorate, decoration={markings,
  mark=between positions 0 and 1 step 1/30 with{
    \draw[->, gray arrow] (0,0)--(0,10pt);
    \draw[paint=yellow, line width=.5pt](0,0) circle (.5mm);
  }
}] {plot [domain=0:4.25, samples=60] (\x,\fpeval{1/(1+0.0075*10^{\x})})};  
\end{tikzpicture}

\end{document} 

image.png

% \usepackage{hyperref}

\label{lb:a}

\hyperref[lb:a]{text}

即可显示 text,并且有超链接。text 改成你想要的文字。

发布
问题