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

关于LaTeX2e中文档类使用l3keys键对值的问题 #285

Closed RadioNoiseE closed 1 year ago

RadioNoiseE commented 1 year ago

关于LaTeX2e中文档类使用l3keys键对值的问题

开头

由于在使用expl3写LaTeX2e文档类,于是就想用l3keys的键对值列表来替代原本的\DeclareOption。但在这么做的时候我就有了下面两个问题。

假设如此定义了一个键对值列表:

    \keys_define:nn { testcls } { <blabla> }

同时定义了一个宏供用户设置:

    \NewDocumentCommand \testclsset { } { \keys_set:nn { testcls } }

问题一

LaTeX2e应是在读入完文档类的.sty文件后才会遇见导言区的\testclsset宏的。那么如果我使用<key>.initial在文档类中为键设置初始值,然后用户再在导言区使用\testclsset设置其他值,这个值是否会被处理两遍?

这样的话在下面的情况

又或是读入文档类时仅定义宏,而在\testclsset时才会对其进行处理。此时<key>.initial就是对\testclsset中未设置的键进行缺省设置的补全吗?

问题二

如何使用l3keys来完成类似\testclsset的功能呢?

我看了jlreq.cls的实现,他似乎是用了l3keys2e的\ProcessKeyOptions以及自己的\jlreqset。前一种是通过

    \documentclass[
        <key> «= <val>»
    ]{jlreq}

来设置,后一种似乎就会有上述重复设置两次的问题。(那么大一个文档类,不用文学编程,plain-TeX和LaTeX2e、LaTeX3全混在一起,看的时候真的dt)

我也看了ctex宏集的实现。似乎是也这样的:

\NewDocumentCommand \ctexset { } { \ctex_set:n }
\ProcessKeyOptions { ctex / option }

是不是将最好只设置一次的抛给\ProcessKeyOptions,其余的可以处理两次且一般用户不会重新设置的就用\key_set:nn呢?

结尾

似乎是个很蠢的问题?麻烦各位老师了。

祝万事顺意!:wq

zepinglee commented 1 year ago

问题一

LaTeX2e应是在读入完文档类的.sty文件后才会遇见导言区的\testclsset宏的。那么如果我使用<key>.initial在文档类中为键设置初始值,然后用户再在导言区使用\testclsset设置其他值,这个值是否会被处理两遍?

不是两遍,.initial:n 仅在定义时执行。

比如定义了 cjk-char-scale .initial:n = { 2 },相当于在 \keys_define:nn 处执行了 \keys_set:nn { testcls } { cjk-char-scale = 2 }。后续的 \keys_set:nn(包括 \ProcessKeyOptions 处和用户使用的 \testclsset)都不会再执行 .initial:n= { 2 }

问题二

如何使用l3keys来完成类似\testclsset的功能呢?

是不是将最好只设置一次的抛给\ProcessKeyOptions,其余的可以处理两次且一般用户不会重新设置的就用\key_set:nn呢?

我没太看懂这个问题的意思。

muzimuzhi commented 1 year ago
RadioNoiseE commented 1 year ago

啊,好像是我没太说清楚。

en,我指的执行两次是指,比如定义

\keys_define:nn { testcls }
  {
    font.code:n = \DeclareFontShape{x}{x}{x}{#1}{}
    font.initial:n = { sourcehanserif }
  }

后,在文档类载入的时候被执行了一次,载入了思源宋字体。接着用户又在导言区使用了

\testclsset{font={hiraginopron}}

设置其为冬青明朝体。那么是否就会载入两个字体,且第一个字体为废的。

第二个问题问的就是呃,如何在不重复将负责某个键的代码(如上例中的\DeclareFontShape)执行两次的情况下还要设置一个缺省值(.initial)并提供给用户一个接口(\testclsset)。

下面那段的意思就是说,ctex文档类似乎是用l3keys2e宏集将传给文档类的option转换成对应键对值,从而仅设置一次比如zihao,linespread,fontset等键(这些键也是需要在载入文档类的时候在方括号内设置的)。

而后面关于章节标题样式的这些就会让他们执行两次,执行完缺省设置后如果用户用\testclsset设置了就再执行一次。如果没有指定就可以只执行一次。

我第二个问的是有没有可能在不需要在载入文档类时设置,且相应代码不会被执行第二次的情况下设置键对值的值。(比如不设置.initial?但这样用户不设置的话,也许可以用钩子在导言区最后设置?那是否会覆盖用户设置?)

好吧,似乎我的语文的确不太好。麻烦了

ps 我明天要上学,所以有可能我回的不会很及时(话说马上中考二模了n

RadioNoiseE commented 1 year ago
  • 请一次只问一个问题

  • 「会对性能有较大影响吧」未必有较大影响,你可以测试一下,使用 timer 相关 primitive 或封装后的 l3benchmark 包。

嗯 好的

主要是如果第一个问题是第二种情况的话那第二个问题就不存在了。

谢谢你(们)!

muzimuzhi commented 1 year ago

第二个问题问的就是呃,如何在不重复将负责某个键的代码(如上例中的\DeclareFontShape)执行两次的情况下还要设置一个缺省值(.initial)并提供给用户一个接口(\testclsset)。

前面只储存,在很后面的时候(hook begindocumentbefore/begindocument,后者里能载入宏包)再执行真正的代码(比如储存在 token list 里,那就判断 token list 是否为空,为空就使用缺省值)。缺省值也不是非要用 l3keys.initial 设置。

RadioNoiseE commented 1 year ago

第二个问题问的就是呃,如何在不重复将负责某个键的代码(如上例中的\DeclareFontShape)执行两次的情况下还要设置一个缺省值(.initial)并提供给用户一个接口(\testclsset)。

前面只储存,在很后面的时候(hook begindocumentbefore/begindocument,后者里能载入宏包)再执行真正的代码(比如储存在 token list 里,那就判断 token list 是否为空,为空就使用缺省值)。缺省值也不是非要用 l3keys.initial 设置。

  • 滞后执行也有坏处,比如在声明之后、执行之前,把内容存进盒子就用不上字体(因为这时候设置字体的代码还没执行)

我明白了

那似乎像CTeX一样\ProcessKeyOptions\key_set联用是非常好的实现了。

超感谢各位!

RadioNoiseE commented 1 year ago
  • 有非常大的可能性,你不需要这样的过早优化。

嗯,有道理(毕竟一个月前打算要用expl3来写文档类到现在我才刚把纸张选择和字体写完--研究xreal和nfss倒是花了一个星期。)

但是我碰不到电脑(steam的锅),是在iPad上用写的(a-shell,有一个大致健全的texlive2022)。iPad比较破,3gb运存,果子的策略导致内存最多1gb,载多字体,它,会闪退。

好多废话。麻烦各位了

muzimuzhi commented 1 year ago

但是我碰不到电脑(steam的锅),是在iPad上用写的(a-shell,有一个大致健全的texlive2022)。iPad比较破,3gb运存,果子的策略导致内存最多1gb,载多字体,它,会闪退。

@clerkma 移动端需求