CTeX-org / forum

A temporarily alternate forum of `bbs.ctex.org`
https://t.me/chinesetex
Apache License 2.0
210 stars 16 forks source link

应用需求:识别传递的参数中 `{}` 的数量 #309

Closed xkwxdyy closed 3 months ago

xkwxdyy commented 3 months ago

检查清单

操作系统

mac os 14

TeX 发行版

TeXLive 2024

描述问题

需求描述

\cs_new_protected:Npn \__whu_define_constant:nn #1#2
  { \tl_const:cn { c__whu_constant_ #1 _tl } {#2} }
\cs_new_protected:Npn \__whu_define_constant:nnn #1#2#3
  {
    \tl_const:cn { c__whu_constant_ #1    _tl } {#2}
    \tl_const:cn { c__whu_constant_ #1 _en_tl } {#3}
  }

\NewDocumentCommand { \whudefineconstant } { s m }
  {
    \bool_if:nTF { #1 }
      {
        % \whudefineconstant { {<name>} {<content>} {<content_en>} list }
        \clist_map_inline:nn
          {
            #2
          }
          { \__whu_define_constant:nnn ##1 }
      }
      {
        % \whudefineconstant { {<name>} {<content>} list }
        \clist_map_inline:nn
          {
            #2
          }
          { \__whu_define_constant:nn ##1 }
      }
  }

上面定义了一个函数 \whudefineconstant,作用是批量定义一些常值的 tl,其中 \whudefineconstant* 还定义了英文的版本。

使用场景为论文模板开发中,定义一些固定的值比如“摘要”,“ABSTRACT”,“致谢”等。

但是有一个需求是需要额外定义这个常值的 toc 版本:具体来说,就是在设置一些章节标题的时候,比如“致谢”,可能样式是致 \quad 谢,但是在目录中仍然是 致谢。思路是定义两个 tl\c__whu_constant_acknowledgement_tl\c__whu_constant_acknowledgement_toc_tl 分别储存 致 \quad 谢致谢,然后通过\chapter[\c__whu_constant_acknowledgement_toc_tl]{\c__whu_constant_acknowledgement_tl} 的方式来实现效果。

所以就想能把 \whudefineconstant 继续优化,甚至不需要带*版本。

功能设想

\whudefineconstant 依旧支持批量生成,即必选参数是一个 clist,其中每一个 item 是 2-5 个 {} 的组合,比如

\whudefineconstant{
  {acknowledgement}{致\quad谢}{ACKNOWLEDGEMENT}{致谢}{Acknowledgement},
  {abstract}{摘\qquad要}{Abstract}{摘要}
}

就定义了

总结来说就是

所以识别每个 item 有多少个 {} 就是关键。

还考虑到如果某一个 {} 里仍然有 {},比如 {{\kaishu 摘}要} (虽然可能不太常见),也需要能正确识别数量,可能需要 peek 配对的括号。

上述需求超出了我的能力范围,所以请求帮助

最小工作示例(MWE)

\documentclass{ctexart}
\ExplSyntaxOn
\cs_new_protected:Npn \__whu_define_constant:nn #1#2
  { \tl_const:cn { c__whu_constant_ #1 _tl } {#2} }
\cs_new_protected:Npn \__whu_define_constant:nnn #1#2#3
  {
    \tl_const:cn { c__whu_constant_ #1    _tl } {#2}
    \tl_const:cn { c__whu_constant_ #1 _en_tl } {#3}
  }

\NewDocumentCommand { \whudefineconstant } { s m }
  {
    \bool_if:nTF { #1 }
      {
        % \whudefineconstant { {<name>} {<content>} {<content_en>} list }
        \clist_map_inline:nn
          {
            #2
          }
          { \__whu_define_constant:nnn ##1 }
      }
      {
        % \whudefineconstant { {<name>} {<content>} list }
        \clist_map_inline:nn
          {
            #2
          }
          { \__whu_define_constant:nn ##1 }
      }
  }
\ExplSyntaxOff

\begin{document}

\end{document}

链接

No response

其他信息

我只了解到 latex3 中 peek 模块好像是扫描啥的,但是不太会。

附件

No response

muzimuzhi commented 3 months ago

谨防过度工程化。直接手写 \tl_const:Nn 也不怎么麻烦。

所以识别每个 item 有多少个 {} 就是关键。

\tl_count:n

看你想如何定义输入里不使用括号时,代码的行为。\whudefineconstant 要提供给用户使用吗?(样式/要求的多样性这么丰富吗?)

% !TeX program = xelatex
\documentclass{ctexart}
\ExplSyntaxOn
\NewDocumentCommand { \whudefineconstant } { m }
  {
    \clist_map_inline:nn { #1 }
      {
        \int_case:nnF { \tl_count:n {##1} }
          {
            { 2 } { \__whu_define_constant_aux:nn ##1 }
            { 3 } { \__whu_define_constant_aux:nnn ##1 }
            % more cases
          }
          % raise error
          { }
      }
  }

\cs_new_protected:Nn \__whu_define_constant_aux:nn
  {
    \tl_const:cn { c__whu_constant_ #1 _tl } {#2}
  }

\cs_new_protected:Npn \__whu_define_constant_aux:nnn #1#2#3
  {
    \tl_const:cn { c__whu_constant_ #1    _tl } {#2}
    \tl_const:cn { c__whu_constant_ #1 _en_tl } {#3}
  }
\ExplSyntaxOff

\begin{document}
\whudefineconstant{
  {acknowledgement}{致\quad 谢}{ACKNOWLEDGEMENT},
  {abstract}{摘\qquad 要}
}

\newcommand{\WhuPrintConstant}[1]{%
  \par\noindent
  Constant ``\textbf{#1}'':
  \ifcsname c__whu_constant_#1_tl\endcsname
    \\\null\qquad (cn) \UseName{c__whu_constant_#1_tl}
    \ifcsname c__whu_constant_#1_en_tl\endcsname
      \\\null\qquad (en) \UseName{c__whu_constant_#1_en_tl}
    \fi
  \else
    \\\null\qquad ! Undefined
  \fi
  \par
}

\WhuPrintConstant{random}
\WhuPrintConstant{acknowledgement}
\WhuPrintConstant{abstract}
\end{document}

image

参数一多,记忆参数的位置/顺序会带来负担,也许 key-value 形式更好。

一个 key-value 的尝试

```tex % !TeX program = xelatex \documentclass{ctexart} \ExplSyntaxOn \NewDocumentCommand { \whudefineconstant } { m } { \keys_set:nn { whu/constant } {#1} } \cs_new_protected:Nn \__whu_define_constant_family:n { \keys_define:nn { whu/constant } { #1/中文正文 .code:n = { \__whu_set_constant:nnn {#1} { \l_keys_key_str } {##1} } , #1/中文目录 .code:n = { \__whu_set_constant:nnn {#1} { \l_keys_key_str } {##1} } , #1/英文正文 .code:n = { \__whu_set_constant:nnn {#1} { \l_keys_key_str } {##1} } , #1/英文目录 .code:n = { \__whu_set_constant:nnn {#1} { \l_keys_key_str } {##1} } , #1 .code:n = { \keys_set:nn { whu/constant/#1 } { ##1 } \bool_lazy_and:nnT { \__whu_constant_if_exist_p:nn {#1} {中文正文} } { ! \__whu_constant_if_exist_p:nn {#1} {中文目录} } { \__whu_set_constant:nnn {#1} {中文目录} { \__whu_use_constant:nn {#1} {中文正文} } } \bool_lazy_and:nnT { \__whu_constant_if_exist_p:nn {#1} {英文正文} } { ! \__whu_constant_if_exist_p:nn {#1} {英文目录} } { \__whu_set_constant:nnn {#1} {英文目录} { \__whu_use_constant:nn {#1} {英文正文} } } } } } \__whu_define_constant_family:n {致谢} \__whu_define_constant_family:n {摘要} \__whu_define_constant_family:n {啥啥} \cs_new_protected:Nn \__whu_set_constant:nnn { \tl_const:cn { c__whu_constant_ #1/#2 _tl } {#3} } \cs_new_protected:Nn \__whu_use_constant:nn { \tl_use:c { c__whu_constant_ #1/#2 _tl } } \cs_new_protected:Nn \__whu_get_constant_csname:nn { c__whu_constant_ #1/#2 _tl } \prg_new_conditional:Nnn \__whu_constant_if_exist:nn { p , T , F , TF } { \tl_if_exist:cTF { \__whu_get_constant_csname:nn {#1} {#2} } \prg_return_true: \prg_return_false: } \ExplSyntaxOff \begin{document} \whudefineconstant{ 致谢={ 英文正文=ACKNOWLEDGEMENT, 英文目录=Acknowledgement, 中文正文=致\quad 谢, 中文目录=致谢, }, 摘要={ 中文正文=摘\qquad 要, 英文正文=Abstract, }, 啥啥={ 中文正文=啥啥, }, } \ExplSyntaxOn \cs_new_protected:Npn \WhuPrintConstant #1 { \par\noindent \textbf{#1} \__whu_print_constant_aux:nn {#1} {中文正文} \__whu_print_constant_aux:nn {#1} {中文目录} \__whu_print_constant_aux:nn {#1} {英文正文} \__whu_print_constant_aux:nn {#1} {英文目录} \par } \cs_new_protected:Nn \__whu_print_constant_aux:nn { \\\null\qquad (#2):~ \cs_if_exist_use:cF { c__whu_constant_ #1/#2 _tl } { \fbox{未定义} } } \ExplSyntaxOff \WhuPrintConstant{致谢} \WhuPrintConstant{摘要} \WhuPrintConstant{啥啥} \end{document} ```

输入

\whudefineconstant{
  致谢={
    英文正文=ACKNOWLEDGEMENT,
    英文目录=Acknowledgement,
    中文正文=致\quad 谢,
    中文目录=致谢,
  },
  摘要={
    中文正文=摘\qquad 要,
    英文正文=Abstract,
  },
  啥啥={
    中文正文=啥啥,
  },
}

存储的信息 image

我还是觉得,这有过度工程化的嫌疑。把

\addtocontentsline...
\chapter*[...]{...}

暴露给用户不是很坏的方案。

xkwxdyy commented 3 months ago

谨防过度工程化。直接手写 \tl_const:Nn 也不怎么麻烦。

我的问题,我没有想过度工程化,只是我不知道(忘记了)还可以用 \tl_count: 来计算 {} 啥的。

\whudefineconstant 要提供给用户使用吗?(样式/要求的多样性这么丰富吗?)

这个命令其实更多情况下都是开发层的使用,但也计划要提供给用户,让用户可以根据一些特殊情形微调,不过可能用的不多。

参数一多,记忆参数的位置/顺序会带来负担,也许 key-value 形式更好。

感谢慕子!这个键值的处理挺不错的,我学习一下。

话说工程化有啥问题吗?不太懂,我写的时候也没想太多。我写模板可能更多只是为了提升自己的水平 hhhh,在写的过程中学一些新东西。

muzimuzhi commented 3 months ago

\whudefineconstant 要提供给用户使用吗?(样式/要求的多样性这么丰富吗?)

这个命令其实更多情况下都是开发层的使用,但也计划要提供给用户,让用户可以根据一些特殊情形微调,不过可能用的不多。

开发者设置一次、用户在特殊情况下再设置一次,就会修改(已初始化的)常量的值,于是很可能应该按局部或全局变量来命名(\l__whu_...\g__whu_...)。与 https://github.com/nju-lug/NJUThesis/issues/240 类似。

我后来想到,除了把每一条设置都储存在单独的 tl 里,还可以用 prop/seq 储存(\g__whu_abstract_titles_prop),虽然它们都是 O(n) 的,但这里 n <= 4。长度确定的情况下,存成 \tl_gset:Nn \g__whu_abstract_titles_tl {{<cn>}{<cn-toc>}{<en>}{<en-toc>}} 的 tuple 形式也可以,然后用 \use_(i|ii|iii|iv):nnnn 取用。

xkwxdyy commented 3 months ago

开发者设置一次、用户在特殊情况下再设置一次,就会修改(已初始化的)常量的值,于是很可能应该按局部或全局变量来命名(\l__whu_...\g__whu_...)。与 nju-lug/NJUThesis#240 类似。

如果在模板里的话,lg 有什么不同的使用场景吗

muzimuzhi commented 3 months ago

设计上,声明总是全局的,然后总是局部赋值 \l_xxx_<type> ,如 setput_rightclear;总是全局赋值\g_xxx_<type>,如 gsetgput_rightgclear。这么做能避免影响 save stack(可以在 texdoc texbytopic 里直接搜 "save stack")。

实现上,以上只是软性约束,也就是 \tl_gset:Nn \l_tmpa_tl {...} 不会报错。开启 \debug_on:n { check-declarations } 后,对 c/l/g 不匹配的使用会报错(目前覆盖不全,会逐渐补全)。

功能上,如果需要某些设置局部生效,如 {\mysetup{<new settings>} new settings applied} old settings restored 就适合用 l。反之如果需要在当前分组结束后仍然生效,就适合用 g。其他时候可以任选。我的印象是,传统的设置项,内部大多用的局部赋值。

xkwxdyy commented 3 months ago

好的,感谢。我感觉这个常量还是适合用 g 一些。