我真的很想创建一个默认长度,即使\bar
长度发生变化,它也与原长度相同,但我还希望用户能够将其设置为他们想要的任何长度。如果我使用宏而不是长度,我可以接近这个效果,但用户需要小心使用。最好的方法是什么?\foo
\foo
\bar
\bar
\baz
\documentclass{article}
\let\bar\relax
\newlength{\foo}
\newlength{\bar}\setlength{\bar}{\foo}
\newlength{\qux}
\def\baz{\foo}
\begin{document}
\setlength{\foo}{100pt}\bigskip
foo: \the\foo\par bar: \the\bar\par baz: \the\baz\par
\setlength{\foo}{2\baz}\bigskip
foo: \the\foo\par bar: \the\bar\par baz: \the\baz\par
\def\baz{400pt}
\setlength{\qux}{2\baz}\bigskip
% Note that \baz no long works with \the
foo: \the\foo\par bar: \the\bar\par baz: \baz\par
% Note that \qux is "wrong"
qux: \the\qux\par
\end{document}
答案1
这是我在计数器包中使用的类似方法xassoccnt
:将长度与“主”长度关联,比如“\foo”。
如果\foo
为 分配了一个新值\setlength
,则与其关联的所有长度都将获得相同的值。
我定义了\addtolength
等,以及删除和同步。星号版本的\setlength
和\addtolength
将仅操纵主长度!
但是,直接操作对等\foo
不起作用。\baz
\documentclass{article}
\usepackage{xparse}
\makeatletter
\let\latex@setlength\setlength
\let\latex@addtolength\addtolength
\newcommand{\stripslash}[1]{%
\expandafter\@gobble\string#1
}
\ExplSyntaxOn
\cs_new:Nn \strongbad_associate_lengths:nn {%
\seq_set_from_clist:cn {g_strongbad_#1_lengths_seq} {#2} % Populate unexpanded
\seq_remove_duplicates:c {g_strongbad_#1_lengths_seq}
\seq_remove_all:cn {g_strongbad_#1_lengths_seq} {#1}% Prevent self - association!
\seq_map_inline:cn {g_strongbad_#1_lengths_seq} {
\dim_if_exist:NF { ##1 } {% Preventing complaining about already existing length variables
\newlength{##1}
}
}
}
\NewDocumentCommand{\RemoveAssociatedLengths}{mm}{%
\seq_if_exist:cT { g_strongbad_ \stripslash{#1} _lengths_seq } {
\seq_set_from_clist:Nn \l_tmpa_seq {#2}
\seq_map_inline:Nn \l_tmpa_seq {
\seq_remove_all:cn { g_strongbad_ \stripslash{#1} _lengths_seq } {##1}
}
}
}
\NewDocumentCommand{\DeclareAssociatedLength}{mm}{%
\strongbad_add_associated_length:nn {#1}{#2}
}
\cs_new:Nn \strongbad_add_associated_length:nn {%
\seq_if_exist:cF { g_strongbad_\stripslash{#1}_lengths_seq} {
\seq_new:c {g_strongbad_\stripslash{#1}_lengths_seq}
}
\strongbad_associate_lengths:nn{\stripslash{#1}}{#2}
}
\NewDocumentCommand{\AddAssociatedLengths}{mm}{%
\strongbad_associate_lengths:nn{\stripslash{#1}}{#2}
}
\RenewDocumentCommand{\addtolength}{smm}{%
\IfBooleanF{#1}{% No starred command
\seq_if_exist:cT { g_strongbad_ \stripslash{#2} _lengths_seq } {
\seq_map_inline:cn { g_strongbad_ \stripslash{#2} _lengths_seq } {
\latex@addtolength{##1}{#3}
}% End of \seq_map_inline
}% End of \seq_if_exist
}% End of \IfBooleanF
\latex@addtolength{#2}{#3}
}
\NewDocumentCommand{\synclengths}{m}{%
\seq_if_exist:cT { g_strongbad_ \stripslash{#1} _lengths_seq } {
\dim_set:Nn \l_tmpa_dim {\the#1}
\setlength{#1}{\l_tmpa_dim}
}
}
\NewDocumentCommand{\syncaddtolength}{mm}{%
\addtolength*{#1}{#2}%
\synclengths{#1}%
}
\RenewDocumentCommand{\setlength}{smm}{%
\IfBooleanF{#1}{% No starred command
\seq_if_exist:cT { g_strongbad_ \stripslash{#2} _lengths_seq } {
\seq_map_inline:cn { g_strongbad_ \stripslash{#2} _lengths_seq } {
\latex@setlength{##1}{#3}
}% End of \seq_map_inline
}% End of \seq_if_exist
}% End of \IfBooleanF
\latex@setlength{#2}{#3}
}
\makeatother
\ExplSyntaxOff
\begin{document}
\newlength{\foo}
\newlength{\boz}
\DeclareAssociatedLength{\foo}{\baz,\buz,\boz,\biz}
\setlength{\foo}{100pt}
foo: \the\foo
baz: \the\baz
buz: \the\buz
boz: \the\boz
biz: \the\biz
\setlength{\baz}{\dimexpr200pt+\foo}
Foo again: \the\foo
Baz now: \the\baz
buz: \the\buz
boz: \the\boz
biz: \the\biz
Now using the starred version:
\setlength*{\foo}{5000pt}
Foo after starred version: \the\foo
Baz after starred version: \the\baz
Adding some value:
\addtolength{\foo}{100pt}
Foo after adding some value: \the\foo
Baz after adding some value to foo: \the\baz
Adding some value with synchronization first:
\syncaddtolength{\foo}{100pt}
Foo after adding some value: \the\foo
Baz after adding some value to foo: \the\baz
Buz after adding some value to foo: \the\buz
Removing buz and biz:
\RemoveAssociatedLengths{\foo}{\buz,\biz}
\addtolength{\foo}{-1000pt}
New foo: \the\foo
New biz: \the\biz
New buz: \the\buz
\end{document}
答案2
\baz
扩展为\foo
,这是一个长度,因此2\baz
在第二个长度设置中有效。但是,让\def\baz{400pt}
不再产生\baz
长度。是的,从技术上讲,它会扩展为有效长度,但不能按原样用于长度乘积。您必须明确使用\dimexpr
,否则值将被连接而不是相乘:
\documentclass{article}
\let\bar\relax% Just for this example
\newlength{\foo}% \foo is a length
\newlength{\bar}\setlength{\bar}{\foo}% \bar is a length
\newlength{\qux}% \qux is a length
\def\baz{\foo}% \baz expands to \foo, which is a length
\begin{document}
\setlength{\foo}{100pt}\bigskip
foo: \the\foo\par bar: \the\bar\par baz: \the\baz\par
\setlength{\foo}{2\baz}\bigskip
foo: \the\foo\par bar: \the\bar\par baz: \the\baz\par
\def\baz{400pt}% \baz is no longer a length
\setlength{\qux}{2\dimexpr\baz}\bigskip
foo: \the\foo\par bar: \the\bar\par baz: \the\dimexpr\baz\par
qux: \the\qux\par
\end{document}
狡猾一点的一点是,你可以
\def\baz{\dimexpr400pt}
答案3
您可以创建链接长度,但需要不同的命令(或覆盖标准命令)。语法类似于\newcounter
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\NewLength}{mo}
{
\newlength{#1}
\seq_new:c { g_strongbad_length_ \cs_to_str:N #1 _seq }
\IfValueT{#2}
{
\seq_gput_right:cn { g_strongbad_length_ \cs_to_str:N #2 _seq } { #1 }
}
}
\NewDocumentCommand{\SetLength}{mm}
{
\setlength{#1}{#2}
\seq_map_inline:cn { g_strongbad_length_ \cs_to_str:N #1 _seq }
{
\setlength{##1}{#2}
}
}
\ExplSyntaxOff
\NewLength{\foo}
\NewLength{\baz}[\foo]
\NewLength{\qux}
\begin{document}
\SetLength{\foo}{100pt}\bigskip
foo: \the\foo\par baz: \the\baz\par
\SetLength{\foo}{2\baz}\bigskip
foo: \the\foo\par baz: \the\baz\par
\SetLength{\baz}{10pt}\bigskip
foo: \the\foo\par baz: \the\baz\par
\SetLength\baz{400pt}
\SetLength{\qux}{2\baz}\bigskip
qux: \the\qux\par
\end{document}
每个定义的长度都有一个关联的长度列表,一旦修改(使用\SetLength
),这些长度将设置为与主长度相同的值。我留给你作为练习来实现\AddToLength
。
答案4
假设我想要一个长度为 2 倍\foo
减 3 pt 的长度。使用长度来定义它(如 的情况\barr
),它在定义时是固定的,因此 的后续更改\foo
不会对 产生影响\barr
。
但是,使用\def
以 开头的\dimexpr
(例如 的情况\baz
)允许\foo
和\baz
永久链接到 x2 - 3pt 关系,即使\foo
发生变化。此外,\baz
充当长度,即使它不是,因此\the\dimexpr3\baz
产生预期的长度结果。
\documentclass{article}
\newlength\foo
\newlength\barr
\setlength\foo{100pt}
\setlength\barr{\dimexpr2\foo-3pt}
\def\baz{\dimexpr2\foo-3pt\relax}
\begin{document}
\the\foo, \the\barr, \the\baz
\setlength\foo{200pt}
\the\foo, \the\barr, \the\baz
\the\dimexpr3\baz
\end{document}
跟进
问题是这种\dimexpr
方法如何处理胶水。答案是可以的,如果\relax
用于终止\dimexpr
前加入胶水。因此,
\documentclass{article}
\newlength\foo
\setlength\foo{100pt}
\def\baz{\dimexpr2\foo-3pt\relax minus20pt}
\begin{document}
\foo=100pt\relax
x\hspace{\baz}x
x\hspace{197pt minus 20pt}x
x\hspace{197pt}x
\noindent\hrulefill
\foo=165pt\relax
x\hspace{\baz}x
x\hspace{327pt minus 20pt}x
x\hspace{327pt}x
\end{document}
可以看出,胶水在的扩展中起作用\baz
。