Latex3: Declaração de variáveis ​​locais

Criado em 18 out. 2017  ·  49Comentários  ·  Fonte: latex3/latex3

O documento O pacote expl3 e a programação LaTeX3 (v. 2017/09/18) afirma na p. 7:

A convenção de codificação LaTeX3 é que todas as variáveis ​​devem ser declaradas antes do uso.

Da mesma forma, o documento The LaTeX3 Interfaces (v. 2017/09/18) afirma na p. 10:

... em contraste com as variáveis, que sempre devem ser declaradas

expl3 não oferece recursos para declarar uma variável localmente; apenas globalmente. No entanto, ainda é possível criar uma variável local implicitamente usando as várias funções \..._set:N... .

É um princípio da programação estruturada que deve ser evitado o despejo desnecessário de variáveis ​​no escopo global. A linguagem TeX, como todas as linguagens de programação de alto nível que conheço (incluindo C, C #, Fortran, Java, Lisp, Pascal e Python), fornece uma construção de escopo para a criação de variáveis ​​locais, ou seja, grupos. Além disso, a própria linguagem de programação LaTeX3, felizmente, suporta a criação de variáveis ​​locais, apesar do que os manuais sugerem.

Em minha opinião, as advertências citadas acima devem ser excluídas dos manuais, e deve ser explicado que as variáveis ​​podem, e devem, ser criadas localmente sempre que possível.

Além disso, se for realmente desejado encorajar um estilo de programação no qual todas as variáveis ​​são declaradas antes de serem usadas (eu pessoalmente acho que é uma questão de estilo de programação que deve ser deixada ao gosto individual do programador), as funções precisam ser fornecido para declarar variáveis ​​locais, assim como existem funções para declarar variáveis ​​globais.

expl3 feature-request

Todos 49 comentários

A posição atual segue uma série de experimentos da equipe para estabelecer um padrão que funcione com os fundamentos do TeX, dando suporte a expl3 . Em particular, é importante notar que as variáveis ​​podem ser implementadas usando macros ou usando registradores, e ter em mente como funciona o agrupamento TeX.

Ao usar registradores (por exemplo, para o tipo int ), precisamos de um alocador para vincular o número de registro e o cs que estamos usando para acesso. Em contraste, para armazenamento baseado em macro isso não é necessário. Assim, embora \cs_set_eq:NN possa ser usado para gerar novos tl nomes (por exemplo), ele irá falhar com qualquer coisa que use registradores:

\int_set_eq:NN \l_undeclared_int \l_tmpa_in

irá gerar um erro.

É possível criar um alocador de registro local, e já tentamos isso no passado para _por exemplo, \int_local_new:N . (Veja o pacote etex para uma dessas implementações, ou olhe para trás no histórico expl3 .) A equipe eventualmente decidiu contra isso, pois não parecia se 'encaixar' bem com o escopo do TeX. A chave aqui é que o agrupamento de TeX não é baseado em declarações, mas sim em grupos explícitos, e que uma variável local existe dentro de qualquer grupo aninhado:

\begingroup
  \def\foo{}
  \begingroup
  \show\foo

Quando tentamos a _criação_ local de variáveis, parecia que havia o perigo de obscurecer como o agrupamento do TeX funcionava e que os programadores poderiam ser enganados.

Portanto, decidimos ir com a _declaração_ de 'todas as variáveis ​​globais', mas com a _configuração_ local e global de tais variáveis ​​(a convenção \l_... _versus \g_... ). Isso localiza o importante: o valor. Portanto, recomendamos que todas as declarações estejam no nível superior

\tl_new:N \l_my_tl
\int_new:N \l_my_int

...
\cs_new_protected:Npn \my_func:nn #1#2
  {
    \group_begin:
      \tl_set:Nx \l_my_tl { \tl_lower_case:n {#1} }

Até o momento, esse padrão parece funcionar bem para as tarefas para as quais expl3 foi usado.

No tex, mesmo as variáveis ​​definidas localmente não são completamente destruídas após o fim do grupo. Eles ainda usam algum espaço de string: https://tex.stackexchange.com/questions/316999/release-space-in-the-string-pool. Então imho é melhor ver as variáveis ​​como objetos globais.

@ u-fischer Bom argumento: não foi realmente considerado na época, mas vale a pena ter em mente.

Em minha opinião, essa abordagem está errada. Vai contra os princípios da programação estruturada e vai contra o TeX, que permite a criação de variáveis ​​locais. Também torna o LaTeX3 inconsistente, uma vez que alguns tipos de dados acomodam variáveis ​​locais e outros não.

Gostaria de solicitar que você reinstitua a criação local de variáveis ​​de todos os tipos. Isso dá aos programadores uma escolha: aqueles que preferem definir todas as variáveis ​​globalmente podem fazê-lo, e aqueles que desejam definir algumas globalmente e outras localmente também podem fazê-lo.

Se houver ressalvas, no que diz respeito à memória ou de outra forma, elas podem ser mencionadas na documentação.

Em 18 de outubro de 2017 às 21:17, EvanAad [email protected] escreveu:

Na minha opinião, esta abordagem é errada, e eu gostaria de solicitar que
você reinstitui a criação local de variáveis. Isso dá aos programadores uma
escolha: aqueles que preferem definir todas as variáveis ​​globalmente podem fazê-lo, e
aqueles que desejam definir alguns globalmente e alguns localmente também podem fazê-lo.

para variáveis ​​baseadas em um tipo de registro, você está alocando recursos de um
pool global fixo, então a alocação global do nome é, de fato, muito mais
natural. Dado que assumimos etex, existem mais de 256 registros de
cada tipo, o comportamento de alocação pode talvez ser mais oculto do que
poderia com o tex clássico, mas ainda é uma característica fundamental do
sistema TeX subjacente. expl3 nunca pode ser uma programação completamente geral
idioma: ele deve funcionar com o sistema TeX subjacente. Você não deveria ser
comparando com C #, mas com outras linguagens baseadas em TeX e em particular com
\ newcount e amigos.

Também não é verdade dizer que todas as outras linguagens permitem que variáveis ​​sejam
declarado em escopos locais, o fortran, por exemplo, só permite que as variáveis ​​sejam
declarado no início de uma função / sub-rotina.

Nenhuma das opções acima significa que definitivamente não adicionaríamos uma declaração local
sistema, mas apelar para linguagens de propósito geral não é um bom caso de uso.
Seria necessário haver casos de uso razoáveis ​​dentro da composição textual onde
usar um sistema de declaração global era um problema.

O fato de o Fortran exigir que as variáveis ​​sejam declaradas no início de uma função / sub-rotina não significa que essas variáveis ​​sejam globais. Se você insiste em encorajar um estilo de programação onde todas as variáveis ​​devem ser declaradas, que seja, mas forneça funções para declarar variáveis ​​locais correspondentes àquelas para declarar variáveis ​​globais.

A justificativa para o escopo e as variáveis ​​locais é antes de tudo conceitual. O fato de que o mecanismo TeX subjacente não libera alguns dos recursos associados às variáveis ​​locais não deve ser descartado, mas, em minha opinião, esse não pode ser o motivo para obliterar a noção de escopo e variáveis ​​locais. Exceto para programadores avançados, a implementação subjacente não é motivo de preocupação; e para programadores avançados, dicas e advertências podem ser oferecidas na documentação.

apelar para linguagens de uso geral não é um bom caso de uso.

Eu diria que divergir dos princípios de programação estruturada que foram implementados em praticamente todas as linguagens de programação dos anos 1960 até hoje, incluindo TeX, deve ser algo que a equipe do LaTeX3 deve ter razões muito convincentes para fazer. O ônus da prova deve recair sobre aqueles que desejam abolir o que é onipresente considerado uma boa prática de programação e o que já existe no TeX; não sobre aqueles que desejam preservá-lo.

Lista de tokens globalmente para que a alocação não toque em um registro já atribuído.

Digamos que sua variável inteira local \x tenha o registro 100 atribuído, que já é tomado pela variável \y em um nível superior; usar \y no mesmo nível onde \x é definido localmente seria simplesmente desastroso. Assim, a contabilidade global de registros atribuídos é necessária, tornando muito difícil liberar registros atribuídos localmente. Reservar um bloco de registradores para atribuições locais não é uma solução.

Não estou dizendo que não pode ser feito, mas não acho que valha a pena.

você está perdendo o fato de que \ newcount não é simplesmente local (ou global)
declarar um nome, é associar um nome a um fixo definido externamente
recurso. há apenas um registro de contagem 42 e todos os nomes que
são declarados como significando a contagem 42 estão se referindo ao mesmo registro, se
a alocação do nome é local ou global. Como mencionei as comparações com outros
idiomas não são tão úteis, mas se você quiser uma comparação, você deve
compare a alocação de fluxos de arquivos ou algo assim: nem sempre você pode isolar
declarações locais quando eles estão fazendo interface com um definido externamente
recurso.

Em 18 de outubro de 2017 às 21:43, EvanAad [email protected] escreveu:

O fato de que o Fortran requer que as variáveis ​​sejam declaradas no início de um
função / sub-rotina não significa que essas variáveis ​​sejam globais. Como eu
escreveu, se você insiste em encorajar um estilo de programação onde todas as variáveis
deve ser declarado, que assim seja, mas forneça funções para declarar local
variáveis ​​correspondentes àquelas para declarar as globais.

A justificativa para o escopo e as variáveis ​​locais é o primeiro e mais importante
conceptual. O fato de que o motor TeX subjacente não libera alguns
dos recursos associados às variáveis ​​locais não deve ser descartado,
mas, em minha opinião, esse não pode ser o motivo para obliterar a noção
de escopo e variáveis ​​locais. Exceto para programadores avançados, o
a implementação subjacente não é preocupante; e para programadores avançados, dicas
e advertências podem ser oferecidas na documentação.

-
Você está recebendo isto porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/latex3/latex3/issues/410#issuecomment-337721923 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/ABNcAimMfBDqA-e96Q7tkS-ERr5fv_2Mks5stmLqgaJpZM4P-Mpq
.

O ponto crucial é que, como Joseph e David mencionaram, permitir que int s seja definido localmente pode ser (e tem sido ) feito. Eu acho que vale a pena a dor, a fim de criar uma linguagem que apresenta uma camada de abstração consistente que está de acordo com os princípios da programação estruturada.

Porém, dizer que vale a pena é fácil para mim dizer, porque não sou eu que estou sujeito à dor de implementar a linguagem LaTeX3. Portanto, vamos adotar uma abordagem misericordiosa e dizer que, de fato, não vale a pena a dor. Multar. Neste caso, divida os tipos de dados em duas categorias: aqueles que permitem a criação de variáveis ​​locais e aqueles que não permitem. E descreva essas categorias nos manuais. Não diga: todas as variáveis ​​devem ser declaradas antes do uso. Diga: "Existem duas categorias de tipos de dados. A primeira, consistindo em cs , tl , clist , ... acomoda variáveis ​​locais e a segunda, consistindo em int , ... não. " E explique por que existe a segunda categoria. Desta forma, a documentação é fiel aos fatos e os programadores estão bem informados e têm uma escolha. Alguns programas não precisam usar tipos de dados da segunda categoria e, nesses casos, por que o programador deveria declarar variáveis ​​globais?

@EvanAad Suspeito que isso seja porque estou acostumado com a programação TeX (incluindo as restrições 'tradicionais' do TeX90), mas não tenho certeza de qual é o problema com a abordagem atual. Apoiamos e encorajamos variáveis ​​atribuídas localmente: elas são muito comuns e, de fato, fazem parte da sintaxe que temos para nomeação de variáveis ​​( \l_... / \g_... ). O fato de serem alocados / 'reservados' globalmente não interfere nisso.

BTW, não queremos amarrar interfaces à implementação: por exemplo, o tipo de dados prop teve pelo menos algumas implementações diferentes que eu conheço, uma das quais usou registradores e a atual usando macros.

Para adicionar mais uma nota a isso, a gota d'água que quebrou as costas do camelo (na minha memória) que o sistema de registro local / alocação de variável caiu foi por causa da inconsistência . Como macros e registros se comportavam de maneira diferente, você acabou tendo um comportamento diferente ao escrever

\group_begin:
  \int_new_local:N \l_tmpa_int
  \int_gset:Nn \l_tmpa_int {7}
\group_end:
% `\l_tmpa_int` undefined

vs

\group_begin:
  \tl_new_local:N \l_tmpa_tl
  \tl_gset:Nn \l_tmpa_tl {7}
\group_end:
% `\l_tmpa_tl` defined as `7`

E a única maneira de resolver esse problema seria escrever um sistema de alocação de macros, que nunca foi considerado seriamente devido à sobrecarga. (TeX fica mais lento quanto mais variáveis ​​são definidas e, portanto, dobrar o número de listas de tokens pode ter afetado visivelmente o desempenho.)

Eu ainda acho que expl3 fez a coisa certa ao abstrair macros e registradores em funções que se parecem e se sentem iguais - e se a desvantagem é que a sintaxe não suporta naturalmente variáveis ​​e registradores verdadeiramente locais, que viram uso extremamente limitado em TeX devido aos motivos descritos acima, então, da minha perspectiva, isso é uma compensação aceitável.

@wspr Mas os exemplos que você citou como aqueles que quebraram as costas do camelo são exemplos em que o programador abusou da linguagem ao atribuir globalmente a uma variável local. Isso está no programador. O sistema atual não resolve o problema do abuso de linguagem, pois um programador ainda pode usar uma variável \l_... como se fosse global.

... que está de acordo com os princípios da programação estruturada.

é uma declaração bastante ousada, visto que há mais de um conjunto de
princípios e dado que tenta unir princípios diferentes em
os superconjuntos geralmente criaram linguagens grandes mas inúteis no passado.

O ponto crucial não é necessariamente que algo possa ser feito (como
dentro das bases completas de Turing, todas as coisas são iguais nesse nível), mas
se algo pode ou não ser feito de forma eficiente e consistente, etc.

@wspr : Por que você fechou este problema como um valentão? Está tudo menos resolvido.

@EvanAad - apenas tentando manter as coisas organizadas. A discussão pode certamente continuar e, se necessário, a reabriremos.

Estou bloqueando esses problemas temporariamente devido a alguns comentários acalorados.

Eu acho que essa é uma questão relevante. Um resultado dessa discussão deve ser pelo menos que descrevemos com mais cuidado no documento por que escolhemos apenas as declarações globais. @EvanAad Não acho que você aprecie as limitações do sistema TeX, mas vamos voltar atrás e ignorar esse problema por um momento. Você poderia dar um exemplo (de 10-20 linhas, digamos) usando um hipotético \int_local_new:N ou um sistema diferente de sua escolha com a semântica de sua escolha? Isso ajudaria a enquadrar a discussão em um ambiente mais concreto, e podemos apontar vantagens / desvantagens.

Presumivelmente, temos que esperar a fechadura abrir, não sei como fazer isso. Em qualquer caso, a maioria das pessoas nesta conversa vai dormir por algumas horas. (A propósito, não acho que copiar o # 410 para o # 411 como uma discussão confusa foi muito razoável.)

Olhando para o fato de que esta é uma discussão bastante detalhada, acho que vale a pena resumir a história técnica e social que nos levou até onde estamos.

Em um nível técnico, o TeX fornece registros e macros para armazenamento. As macros podem apenas ser 'criadas' usando \def , mas para usar registradores por nome, precisamos de algum alocador para vincular o número de registro global, _e.g._ \count40 , a algum nome, por exemplo, \mycount . Isso foi fornecido desde o 'primeiro dia', com o TeX simples \newcount , _etc._ fornecendo o modelo. Simplesmente, \newcount aloca globalmente e, apesar do nome, não faz nenhuma verificação. Outros formatos, mais notavelmente LaTeX2e e ConTeXt, adotaram essa abordagem em termos gerais. Em geral, eles também adotaram a ideia de que os registros individuais são então reduzidos localmente ou globalmente, pois isso evita o acúmulo de pilha de salvamento.

O TeX90 forneceu apenas 256 registros dos tipos comuns, portanto, a reutilização de registros em contextos locais foi vital. Pode-se ver que em _por exemplo, graphics onde para manter o código 'são', um grande número de registros recebem novos nomes dentro de um grupo e são usados ​​puramente localmente. Com o e-TeX temos _muito_ mais registros, então esta abordagem é menos necessária: podemos alocar livremente mais registros (e macros) para propósitos dedicados. Em particular, significa que não somos tão pressionados a 'reciclar' registros sob vários nomes.

O código base de expl3 está em desenvolvimento há _muito_ tempo e originalmente não exigia o e-TeX. O desenvolvimento de expl3 também é feito principalmente 'sobre' o LaTeX2e, com um princípio geral de que expl3 não polui o espaço de nomes do documento nem altera os comportamentos principais do LaTeX2e.

Em particular, temos que ter em mente que o \newcount LaTeX2e é semelhante ao do plain: ele aloca globalmente e não verifica. Assim, em particular

\def\foo{%
  \begingroup
    \newcount\localfoocnt

é de 'estilo ruim' como se \foo fosse chamado várias vezes, então usaremos registros que nunca são liberados.

Para expl3 , a equipe tentou evitar vincular o comportamento documentado de variáveis ​​à implementação de macros ou registros. (Eu observo que permitimos o uso de tl sem um acessador, o que, em última análise, depende de eles serem macros.) Também usamos registradores para inteiros, dimens, _etc._ conforme estão disponíveis e oferecem melhor desempenho e 'terminação automática' do que fazer tudo em macros. (Isso seria possível usando e-TeX e as várias primitivas \<thing>expr .) Como tal, precisamos ter um sistema de alocador e, para sermos consistentes, alocamos todos os tipos de variáveis, não apenas aqueles que são baseados em registradores . Como @EvanAad observou, quando a verificação não está ativa, pode-se fazer _por exemplo, \tl_set_eq:NN \l_new_tl \l_existing_tl sem erros, mas este não é o comportamento suportado (a verificação irá sinalizá-lo, por exemplo).

Como parte do alocador expl3 , foi tomada a decisão de que new _deveria_ verificar a existência, em contraste com \newcount , então

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new:N \l_my_tl

irá gerar um _error_ se \my_foo: for usado repetidamente. Isso empurra a pessoa na direção que tem sido a prática padrão do TeX desde os primeiros dias do plain: as alocações vão fora da macro. (Observe que, simplesmente, \newcount é \outer portanto, é 'proibido' dentro de macros.)

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

O pacote etex introduziu há muitos anos um alocador de registro que pode fazer alocação local e global de registro, \loccount _versus_ \globcount , _etc. Por algum tempo, expl3 carregado etex e foi usado para fornecer funcionalidade para \int_local_new:N e similares. Dado o fato de que a maioria dos programadores de TeX está acostumada a fazer alocação global, talvez não seja surpreendente que isso não tenha sido levado tão amplamente. No entanto, a equipe também estava preocupada com possíveis mal-entendidos. Com algo como

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_local_new:N \l_my_tl

temos a questão do que acontece em chamadas aninhadas para \my_foo: , ou mais provavelmente lugares onde algum nome 'simples' como \l_my_tmp_tl deve ser usado. Com base na ideia geral de que queremos que new faça verificações, isso deve ser um erro. Programadores vindos de várias outras linguagens podem achar isso um tanto estranho. Claro, ele se encaixa nas regras de escopo do TeX.

Notavelmente, as mudanças no kernel LaTe2e nos últimos anos significam que não queremos mais carregar etex (de fato, trabalhamos duro para fazer expl3 'independente'). Portanto, qualquer novo alocador local teria que ser escrito para funcionar com LaTeX2e sem 'perturbar' comportamentos para aqueles que não usam expl3 : factível, mas não totalmente trivial.

Para algo como \l_my_tmp_tl pode-se, é claro, ainda fazer a alocação global, mas então é preciso saber quais \l_... variáveis ​​estão alocadas localmente no grupo atual e quais são alocadas globalmente, mas atribuídas localmente.

Não tenho dados de teste disponíveis, mas provavelmente vale a pena notar que alocar uma variável é mais trabalhoso do que simplesmente defini-la (certamente quando a verificação não está ativa), então usar um alocador local seria um pouco mais lento do que um alocador global mais atribuições locais.

Portanto, por uma mistura de razões técnicas e 'encaixe com a história', decidimos nos ater a _alocação_ estritamente global. Isso não impede de forma alguma a redução local das variáveis, que são encorajadas e amplamente utilizadas.

Isso nos leva ao elemento 'social'. A absorção de expl3 nos últimos anos tem sido fortemente auxiliada por tornar as peças 'amplamente' estáveis. Em algum estágio, a equipe tem que tomar uma decisão sobre as coisas, em parte para que as pessoas possam usá-las e em parte para que possamos passar para outras tarefas. Isso não nos impede de revisitar as coisas, mas convenções mais estabelecidas precisam de um bom motivo para serem alteradas.

Aqui, no momento, acho que não estou vendo o que há de errado com a abordagem "usual" de

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

pelo menos até o ponto em que é necessário voltar e alterar a configuração atual.

@blefloch Sim, concordo que minha reação ao encerramento do tópico de @wspr não foi razoável e peço desculpas a ele e a todos vocês. Senti que estava sendo dispensado e silenciado e reagi com um acesso de raiva. Isso é inaceitável. Desculpe por isso.

@josephwright, obrigado pela resposta detalhada. Depois de pensar um pouco no assunto, percebi que se eu usar apenas \<module>_clear_new:N , então, na verdade, todas as minhas variáveis ​​se comportarão como se fossem locais para o grupo ao redor, contanto que eu atribua usando \<module>_set... vez de \<module>_gset... . Além disso, acho que essa prática está de acordo com a convenção LaTeX3 de declarar uma variável antes do uso, então não serei sinalizado se as verificações estiverem ativadas.

Seguindo essa prática, eu reescreveria o último exemplo de @josephwright assim:

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl

O único tipo de dados que não se ajusta a esse esquema é cs , mas, pelo que eu sei, é legal criar variáveis ​​desse tipo com \cs_set:Npn et al. sem primeiro declará-los, porque LaTeX3 realmente não trata cs como um tipo de dados normal em paridade com os outros (que é um assunto separado que eu gostaria de discutir com vocês, mas vou deixar para outro tópico). Usando esta abordagem, cs variáveis ​​também podem ser criadas localmente. Se eu quiser manter a sintaxe consistente, posso escrever meu próprio invólucro \cs_clear_new:N .

Portanto, no que me diz respeito, agora você pode encerrar este problema se quiser, @wspr .

@EvanAad Em \<thing>_clear_new:N , observe que a declaração _é_ global onde a variável ainda não existe. Então

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl
    \group_end:
}
\my_foo:
\tl_show:N \l_my_tl

irá mostrar que \l_my_tl está definido e vazio, supondo que você não o tenha definido anteriormente com outra coisa.

Eu gostaria de me juntar a @blefloch na esperança de que esta discussão leve a uma documentação melhor. Em particular, acho que quando você escreve que o programador "deve" seguir um certo padrão de codificação, como nas frases que citei em minha postagem original, a documentação deve elucidar o que esse "deve" significar. O que acontecerá se o padrão não for seguido?

  1. O comportamento da linguagem será indefinido?
  2. A não adesão é atualmente suportada, mas pode não ser em versões futuras?
  3. O mecanismo relatará um erro?
  4. O mecanismo relatará um erro, mas apenas quando as verificações estiverem ativadas?
  5. Isso resultará em pequenos inconvenientes?
  6. A equipe do LaTeX3 reclamará do descontentamento, mas nenhum erro ou perda de funcionalidade resultará?

Como exemplo para 5, use a convenção de anexar especificações de argumento ao final do nome de uma função. Pelo que eu posso dizer, a única função que não funcionará corretamente se esta convenção não for cumprida é \cs_new:Nn , mas o resto, incluindo `cs_ new: Npn 'funcionará perfeitamente bem.

Como exemplo para 6, tome a convenção de marcar variáveis ​​locais e globais com \g_... e \l_... . Pelo que eu sei, não haverá absolutamente nenhuma repercussão em não seguir esta convenção. Tudo funcionará corretamente.

@EvanAad - desculpas aceitas e sinto muito por encerrar o problema antes que a discussão fosse concluída.

@josephwright - como uma pequena vantagem de benefícios para alocação local, se eu escrever

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      ...

então eu sei que \l_my_tl não está apenas livre de interferência externa, mas ao contrário de usar uma função _clear , eu sei que todos os vestígios da variável sumiram fora do uso da função. Em outras palavras, apenas olhando para o código, eu sei que ele não pode ser usado como uma variável semi-global no final da linha. (E posso verificar se nada de estranho está acontecendo usando \tl_if_exist_p:N .)

(Deve ser executado, mas pode continuar mais tarde se houver algum ponto de discussão mais aprofundada.)

@wspr Sim, mas isso volta com o fato de que é baseado no escopo do grupo TeX. Portanto, ele pode ser usado 'semi-globalmente' em uma construção do formulário

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      \tl_set:Nn \l_my_tl { foo }
      \__my_foo:
     ..
  }
\cs_new_protected:Npn \__my_foo:
  {
    \group_begin:
        \tl_use:N \l_my_foo % Definition from \my_foo: => "foo"
...
  }

o que era pelo menos parte do nosso pensamento.

Antes de encerrarmos, gostaria de enfatizar que, em nível técnico, há várias maneiras de configurar um alocador de registro local.

@josephwright - Eu nunca vi isso como um exemplo confuso; no Matlab, por exemplo, eles distinguem entre subfunções que não compartilham o escopo e subfunções aninhadas que o fazem. Então, a meu ver, sempre foi, bem, por que \__my_foo: herdaria o escopo da função “externa”? É perfeitamente consistente com o comportamento de agrupamento do TeX.

(Desculpe por continuar a discussão. Foi um longo dia. Alguém está interessado em continuar?)

Am 19.10.2017 um 09:23 schrieb Joseph Wright:

Acho que antes de encerrarmos, gostaria de enfatizar que em um
nível técnico, existem várias maneiras de configurar um registro local
alocador.

sim, mas bastante próximo do argumento de Turing, ou seja, qualquer implementação desse tipo
vai ser muito ineficiente em tempo de execução porque o mecanismo subjacente
está gerenciando o armazenamento de registros globalmente. E expl3 (nesse nível)
deve permanecer "razoavelmente eficiente.

isto é, digamos, variáveis ​​globais e locais e seu mutador. Em vez
do que testar em cada função se estiver fazendo uma operação global em um
variável local, o conceito está, por padrão, presente apenas nos nomes,
por exemplo, \ l _... deve ser uma variável local e não deve ser usada
com uma função global como ..._ gset: Nn, mas não estamos testando isso em
tempo de execução

No entanto, oferecemos um módulo de verificação (que é executado quantas vezes mais lento)
isso garante que todas essas convenções sejam realmente obedecidas.

Então, voltando às variáveis ​​globais / locais

o conceito padrão de expl3 é

  • declara o nome de uma variável uma vez (globalmente) - muitas porque em
    pelo menos alguns dos tipos têm apenas caixas de armazenamento globais

  • use a convenção de nome \ l_ \ g_ para denotar variáveis ​​locais e globais

que fornece tanto globais quanto locais, mas tem as restrições de

  • você não declara uma variável local no início de seu escopo,
    em vez disso, você usa _set ou _clear_new nesse ponto e o último
    pode significar que o nome da variável pode vir naquele ponto globalmente
    na existência

  • fora do escopo, a variável ainda existe com o padrão
    valor do tipo (por exemplo, _int sendo 0 etc), então você não obtém um compilador
    erro se você se referir a tal variável "por engano" fora de seu objetivo
    alcance

Então, basicamente, a única coisa que você não consegue é ser capaz de declarar um
variável local para que seu nome desapareça fora do escopo (e produz
um erro indefinido de algum tipo em tempo de execução se referido fora do
âmbito declarado).

Para fazer isso (o que sim é possível), expl3 precisaria manter o seu próprio
pool de recursos muito além de uma simples associação entre um nome e um global
piscina oferecida pelo motor e isso significaria que todo o acesso seria
muito notavelmente lento feito - o que vai contra os critérios de design de expl3.

@FrankMittelbach - agora você me deixou curioso; é a abordagem etex.sty para registradores realmente ineficiente? Ou talvez você se refira a variáveis ​​tl, caso em que eu concordo!

@FrankMittelbach Na minha opinião é importante distinguir, e deixar claro na documentação, o que é uma boa prática de acordo com a equipe LaTeX3 vs. o que é um requisito formal, gramatical ou semântico, das regras da linguagem LaTeX3.

As duas regras que você citou, a saber:

  • declare o nome de uma variável uma vez (globalmente)
  • use a convenção de nome \l_ \g_ para denotar variáveis ​​locais e globais

caem nesta rubrica de convenções de codificação que a equipe do LaTeX3 considera aconselháveis, mas nenhum deles é comandado pelas regras gramaticais do idioma, e o não cumprimento dessas convenções não causa nenhum erro ou perda de funcionalidade.

Em outras palavras, seguir essas regras é uma questão de gosto pessoal e estilo de codificação, e isso deve ficar claro na documentação, na minha opinião.

isso é um pouco como dizer que o sinal 50mh não é uma regra, mas uma direção
convenção e é uma questão de gosto se um driver aderir a ela (apenas
porque não é verificado imediatamente na maioria das vezes)

se você usar uma variável \ l_ com _gset, então você ainda programa em TeX, mas
você parou de obedecer às regras gramaticais do expl3
língua. Isso quebra imediatamente o seu código? provavelmente não, mas você
gerar acumulação de savestack (consulte o índice TeXbook)

você está dizendo que é apenas uma regra gramatical se estivermos verificando em
tempo de execução, digamos, toda terça-feira?

@FrankMittelbach Claro, se você definir isso como parte das regras da linguagem, então é por definição, mas meu ponto é que isso não deve ser definido como parte das regras da linguagem, porque nunca é verificado, e porque o não cumprimento desta convenção não causa, por si só, um erro ou perda de funcionalidade.

É a diferença entre dizer "Quando você escreve em inglês é melhor segurar a caneta com a mão direita porque isso evita manchar a tinta." e aprovar uma lei que diz que o inglês deve ser escrito com a caneta na mão direita. Claro, você pode aprovar essa lei, e há uma justificativa sólida para justificá-la, mas, em última análise, a escolha em que mão segurar a caneta deve ser deixada para o escritor individual. E haverá pessoas que escolherão não aderir a esta regra, e ainda assim acabarão escrevendo tão bem quanto aqueles que aderem a ela.

Acho que @FrankMittelbach exagera a sobrecarga das declarações locais ( \loccount gerencia as contagens localmente, nada que ele faz é global, de modo que mantém a sobrecarga bem).

Não vejo nenhum problema importante em fornecer \int_local:N e \tl_local:N (= \tl_set_eq:NN #1 \c_empty_tl ) etc. que seria muito análogo a \int_zero_new:N e \tl_clear_new:N mas só faria "novo" localmente. Isso requer um pouco de trabalho para os registradores, mas não excessivamente.

@EvanAad você deve saber que não declarar uma variável pode

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\tl_put_left:cn { l_my_tl } { foobar \par }
\l_my_tl
\end{document}

@blefloch O que estou dizendo é que é perfeitamente \<module>_clear_new ), e você não precisa nomeá-la \l_amount_paid_int , você pode simplesmente chamar it \amount_paid ou \amountPaid , como você faria em qualquer outra linguagem de programação. O \l_..._int é um bom mnemônico para lembrá-lo de que é um número inteiro e que deve ser usado localmente, mas você não deve ser forçado a usar este ou qualquer outro mnemônico.

porque nunca é verificado, e porque não aderindo a esta convenção
não causa por si só um erro ou perda de funcionalidade.

mas esse é o ponto

a) causa danos dependendo das circunstâncias - ou seja, quando você mistura
atribuições globais e locais para a mesma variável

b) verificamos mediante solicitação (e neste momento esse código pode não ser
funcional, mas era e provavelmente será novamente eventualmente por causa de a))

ps sim, entendi o que você quis dizer sobre segurar a caneta (ser canhoto) e sim, eu
estiveram acima do limite de velocidade (na minha bicicleta), mas ainda considero isso um
regra de tráfego não é uma convenção de tráfego e sim, pode-se desobedecer isso
sem dano, mas também se pode morrer com isso ou pelo menos acabar com uma multa

(O que meu exemplo de código anterior estava apontando é que mesmo para tl é essencial declará-los antes do uso. Há uma opção de verificação que testa as declarações, mas em uso normal não queremos tais overheads.)

Eu concordo com @EvanAad que os nomes são apenas uma convenção. Podemos verificar se as atribuições locais e globais não estão misturadas, mesmo sem a convenção de nome l_ / g_: uma vez que o código de verificação pode ser um pouco lento, é bastante razoável armazenar a informação sobre se uma determinada variável foi usada em um local / global atribuição. Para verificar os tipos, a situação é semelhante, exceto para dois pares de tipos que não podem ser distinguidos (não vou dizer qual deles para evitar atrapalhar a conversa).

Em 'convenções', \amountPaid é um comando de documento enquanto \l_amount_paid_int não é, e o último é parte do namespace amount (por convenção, mas muito importante no TeX ) Isso não se aplica a \l_ / \g_ , embora eu ache que muitas línguas têm uma forte 'orientação' na nomenclatura sem que isso seja aplicado em um nível técnico.

Em 19 de outubro de 2017, às 16:58, Joseph Wright [email protected] escreveu:

Em 'convenções', amountPaid é um comando de documento, enquanto \ l_amount_paid_int não é, e o último é parte do namespace amount (por convenção, mas muito importante no TeX). Isso não se aplica a \ l _ / \ g_, embora eu ache que muitos idiomas têm um forte 'direcionamento' na nomenclatura sem que seja aplicado em um nível técnico.

O pior pesadelo na programação LaTeX2e é definir um comando
no "espaço de nível de usuário", digamos \ foo, para descobrir que ele colide
com um comando outro pacote definido para uso _internal_ (então
não documentado no manual).

Isso realmente aconteceu? Sim, e não apenas uma vez. Aderindo ao
\convenção @commandname ajudou muito a evitar
tais problemas.

É claro que conflitos no "espaço no nível do usuário" também podem acontecer,
mas são muito mais fáceis de detectar e resolver. Quando se trata de
internos, muitas vezes é necessário perseguir expansões em um
nível profundo.

Diferentemente dos legisladores, não podemos impor multas ou prisão
quem não segue as leis de programação LaTeX3. Mas onde
uma comunidade e todos deveriam.

Nossas diretrizes sobre convenções de nomenclatura devem ajudar a nunca encontrar
comandos internos de um pacote entrem em conflito com os de outros.

Para o código pessoal, a pessoa tem o direito de fazer o que quiser: há
nenhuma lei proibindo alguém de acelerar em sua propriedade privada, mas
há um sobre fazer isso em uma via pública.

Se você quiser definir localmente a variável \ f de qualquer tipo,
você deve se preocupar com o comando sendo definido por alguns
pacote e, pela lei de Murphy, terminando exatamente no argumento
para uma função usando a variável \ f. Você pode imaginar algo pior
cenário?

tchau
Enrico

e você não precisa nomeá-lo \ l_amount_paid_int, você pode simplesmente chamá-lo de amount_paid como faria em qualquer outra linguagem de programação. O \ l _... é um bom mnemônico para lembrá-lo de que deve ser usado localmente, mas não precisa usar este ou qualquer outro mnemônico.

Certo. Você também pode usar md5-fc693aa157832059d7daeeb61c55 cddb : paid ou amount & paid (isso não é uma piada, conheço um pacote que usa &) ou o que você preferir. Mas mesmo que os nomes sejam apenas uma convenção: a comunicação será mais fácil se as pessoas seguirem tais convenções. Você tem feito muitas perguntas no tex.sx. O que você fará se tiver um problema com seu código escrito em "estilo pessoal"? Traduzir para o estilo padrão, fazer uma pergunta e traduzir de volta? Ou espera que todos sejam capazes de lidar com seu estilo pessoal?

@ eg9

Para o código pessoal, a pessoa tem o direito de fazer o que quiser

Você não saberia disso lendo a documentação, é tudo o que estou dizendo.

Se você deseja definir localmente a variável \f de qualquer tipo, você deve se preocupar com o comando sendo definido por algum pacote

Discordo. Se o seu código estiver dentro de \group_begin: ... \group_end: , e se você definir todas as variáveis ​​locais com \<module>_clear_new:N , e se você apenas atribuir variáveis ​​locais com \<module>_set:N... , não precisa se preocupar com o nome de suas variáveis ​​locais entrando em conflito com outros pacotes, a menos que seu código use outro pacote.

@ u-fischer

Mas mesmo que os nomes sejam apenas uma convenção: a comunicação será mais fácil se as pessoas seguirem tais convenções.

Não estou dizendo que não há boas razões para seguir as convenções. Tudo o que estou dizendo é que essas convenções não devem ser regras de linguagem, e a documentação deve diferenciar claramente entre convenções, para as quais a justificativa deve ser declarada, e regras. E a escolha de seguir as convenções deve ser, em última análise, do programador.

@EvanAad Você precisa se preocupar com conflitos de nomes, mesmo no caso que você descreve. Diga que você escreve

\cs_new_protected:Npn \evanaad_halve:n #1
  {
    \group_begin:
      \int_zero_new:N \f
      \int_set:Nn \f { (#1) / 2 }
      \iow_term:x { \int_use:N \f }
    \group_end:
  }

então um usuário do seu pacote faz

 \int_const:Nn \f {123}
 \evenaad_halve:n { \f }

Eles ficarão surpresos ao ver 0 e não 62.

Por outro lado, se você ficar com nomes como \evanaad_f etc (ou \@@_f etc mais curto usando l3docstrip magic), você deve estar seguro.

Você não saberia disso lendo a documentação, é tudo o que estou dizendo.

Desculpe, mas expl3.pdf usa a palavra "convenção" cerca de 20 vezes. No caso do comando público versus privado, há até a frase "Não há (quase) nenhuma maneira de aplicar isso sem uma sobrecarga de computação severa, portanto, implementamos isso apenas por meio de uma convenção de nomenclatura".

@blefloch Bom argumento.

@ u-fischer OK, é justo. E quanto à convenção de que o nome de uma função deve terminar com um especificador de argumento? Isso não é inteiramente uma convenção, porque as funções da seção 3.3 inspecionam o especificador de argumento, mas essas funções são meramente "açúcar sintático" e, se você não usá-las, não há impedimento em usar nomes de função como \mymodule_myfunc , mas você não saberia pelo manual.

Em 19 de outubro de 2017, às 17:52, EvanAad [email protected] escreveu:

@ u-fischer OK, é justo. E quanto à convenção de que os nomes das funções devem terminar com um especificador de argumento? Isso não é inteiramente uma convenção, porque as funções da seção 3.3 inspecionam o especificador de argumento, mas essas funções são meramente "açúcar sintático" e, se você não usá-las, não há impedimento em usar nomes de função como \ mymodule_myfunc.

Isso é necessário para cs_generate_ variant: Nn , é claro.

tchau
Enrico

Com \mymodule_myfunc você não seria capaz de usar nenhuma função de
l3expan . Essas funções de expansão e a noção de variantes são um
parte central do expl3.

Embora eu concorde que os nomes das variáveis ​​podem ser mais curtos (removendo
"l _" / "g_" e "_int" / ...), a assinatura da função realmente não é "apenas um
convenção".

@blefloch vejo seu ponto, e é bom, mas ainda assim, na minha opinião, se tudo o que você quer é escrever um comando de documento, você deve saber que pode fazê-lo definindo uma função como

\ExplSyntaxOn
\cs_new:Npn \MyDocumentCommand {Hello,~world!}
\ExplSyntaxOff

e você não precisa primeiro definir uma função "sombra" \mymodule_my_document_command: e, em seguida, copiá-la com

\cs_new_eq:NN \MyDocumentCommand \mymodule_my_document_command:

@blefloch E, a propósito, além de \cs_generate_variant:Nn , que @ eg9 mencionou, alguma outra função do módulo l3expan faz uso da parte especificador de argumento do nome da função?

agora parece-me que você está discutindo principalmente por discutir

sim você pode fazer tudo isso e no final do dia a única linguagem difícil
regras são o que os fios do motor TeX em seus primitivos. E dado
que TeX é uma linguagem que se auto-modifica, você pode ir basicamente de qualquer lugar
lá por exemplo

\ endlinechar-1 \ def ~ # 1 {\ catcode` # 113} ~ Q ~ S ~ U ~ _ ~ V ~ W ~ J ~ K ~ L ~ M ~ N ~ O ~ @ ~ X ~ Y ~ [~] ~ (
~ | ~ & ~ Z ~ '~ "~ ~ h ~ z ~: ~ q ~ j ~ k ~; ~ / ~) ~! ~, ~ $ ~ + \ Let_ \ let_ ~ newcount ~ $$ - 1 ~ Q ~ J ~ V ~ S~ K ~ W ~ U ~ L ~, ~ '' 1 ~ "" 2 ~ * 1 _ & \ count & 144 '& 155' & 145 "& 154" _ [\ ifnum _ (\ ifcase_O \ ou
_ | \ else _] \ fi_N \ number _ @ \ advance_X \ expandafter_Z \ global_Y \ typeout_ ~ newif
~ \ ifG ~ \ if_ ~ \ def ~ j {[0 Q [0Jk | $] | $] | $] | $]} ~ k {& 1NQNJ} ~ \ 2 # 1 # 2 {} ~: # 1 {

11 # 12 # 13 # 14 # 15 # 16 # 17 # 18} ~ h # 1 # 2 {# 2: {~ \ q # 1} ~ # 2 ^^ J} ~ \ q # 1 # 2 {(& 1 # 1 # 2 ~~ OO $]}

~ / {S {Linha e coluna? por exemplo, E6} \ read $ toM \ ifcat ~ X \ 2M ~ $$ X \ jM |! input!]} ~! # 1! {
Y {Inválido # 1.} /} ~ \ J # 1 # 2 {Q #1@Q- @J #2@J- 0; (V! Mover!]} ~; {V0 (jS1z1z0z {$} S
0z1z {$} S $ z1z0z {$}]} ~ _ {@, \ ifodd '-]} ~ z # 1 {{\ trueK # 1 {\ falseq}}} ~ q {@ QS @ JK [j = "
\ ifZk'Z_2] @ V1q | [j = 'ZVV \ ifG \ if | \ aftergroupq]]]]} ~ \, # 1 {Q # 1:.} ~. # 1 {J # 1; [0
WWVUQLJ]]} ~ + # 1 {(# 1O2O-2O0O0O0O0O-2O2]} ~) {'X "X" N'Y {^^ J:
~ ^^ Jh1Ah2Bh3Ch4Dh5Eh6Fh7Gh8H: ~ ^^ J} \ GfalseW (W $ | 0] ~: \, \ Gtrue [0 /]; k'_1] [$ = WY {(, Empate | Jogador [0> ,. | $] ~ ganha por N [0>, -],].} X \ dump])} ~~ {})

que é um belo documento TeX (na verdade, um belo documento LaTeX)
mas, no que diz respeito ao seu código, não é nada útil. E
o fato de Bruno ser capaz de escrever tal documento não significa que
o manual expl3 (ou neste caso um manual LaTeX) deve descrever qualquer um deles.

O código LaTeX (2.09 e também 2e) tinha o grande problema de que, nos primeiros dias
muitas pessoas programando que ele entendia sobre atalhos de baixo nível
em TeX e usá-los e abusá-los porque eles pensaram que não é
prejudicial. Como resultado, uma grande quantidade de pacotes 2e existentes são
incompatíveis entre si ou têm problemas sutis em alguns lugares quando usados
juntos etc ou quebram de vez em quando porque contornaram um ou o
interface do usuário (porque parecia funcionar sem ele).

Basicamente, você está nos pedindo repetidamente para documentar apenas isso,
ou seja, possíveis atalhos que violam os princípios de design apenas
porque às vezes funcionam (ou mesmo no momento sempre). Mas expl3
e suas convenções / regras são amplamente derivadas da experiência de que
codificadores desobedeceram a tais regras no passado e a bagunça que é o resultado
a partir disso. Portanto, não, as regras são deliberadas e não apenas meros caprichos (a maioria
do tempo) e mesmo que "se você sabe o que está fazendo, então você
pode contornar a maioria deles em uma situação específica ", o que não significa que
se você mover seu código de um lugar para o outro, ainda será
o caso ou precisa ser o caso ao longo do tempo.

Como foi dito por outros, há uma grande diferença entre o código que você escreve
o fly para você mesmo e o código que você escreve como "oficialmente" distribuído
pacote. Pelo menos para este último, pedimos a aceitação das regras e
considerá-los parte da língua. Para você, você pode querer
aprender a codificar um documento como o anterior, mas o conhecimento de como fazer
que não sairá do manual expl3


tendo dito isso, não quero desencorajá-los a conceitos desafiadores,
comandos, interfaces, o que você tem. Muitos dos pontos que você levantou sobre
outras ocasiões foram bem aproveitadas (ou pelo menos nos fizeram repensar uma
ou o outro ponto).

Mas, no que diz respeito ao manual expl3, acho que você ouviu de
várias pessoas é que não há interesse em documentar o
"não-convenções" e "não-regras". Além disso, se o código desviar muito
do que chamamos de regras / convenções, então ainda será o código TeX, mas
não é mais código expl3.

Acho que já conversamos sobre isso: vou fechar, mas é claro que reabrirei se solicitado.

Esta página foi útil?
0 / 5 - 0 avaliações