TransWikia.com

Variable number of repeating command in macros

TeX - LaTeX Asked by piggy on June 24, 2021

I am quite new to Latex and for the past few days I have been struggling to create a macro to insert schemes with labels. I use .eps files for my schemes which include a temporary marker that is then replaced with a number by the chemstyle package. The command is schemeref and includes the temporary marker and a label (named cmpd…).
My macro works if there is just one item to label:

documentclass{article}
usepackage{chemstyle} 

begin{document}

newcommandinsertscheme[5]{%
  begin{scheme}
    schemeref[#1]{cmpd:#2}
    includegraphics{#3}
    caption{#4}
    label{scheme:#5}
  end{scheme}}

insertscheme
{TMP1}{benzene}
{benzene.eps}{Structure of compound{cmpd:benzene}.}{benzene}

end{document}

enter image description here

However, I also have schemes which include more than one structure, usually between 2 and 7. Without the macro I can label all structures like the following example shows:

begin{scheme}
schemeref[TMP1]{cmpd:benzene}
schemeref[TMP2]{cmpd:Brbenzene}
includegraphics{benzene-Brbenzene.eps}
caption{Structure of compound{cmpd:benzene} and compound{cmpd:Brbenzene}.}
label{scheme:benzene-Brbenzene}
end{scheme}

My question now: How do I need to change the macro so that it can be used for schemes with a variable number structures inside?

I am using the LaTeX compiler on Overleaf.

4 Answers

If you want to take this route you can add protect to make it work. (I would not recommend using macros with such excessive numbers of arguments, it is almost impossible to recall their usage, especially if you have several of them.)

documentclass{article}
usepackage{chemstyle} 

begin{document}

newcommandinsertscheme[5]{%
  begin{scheme}
    schemeref[#1]{cmpd:#2}
    includegraphics{#3}
    caption{#4}
    label{scheme:#5}
  end{scheme}}

insertscheme
{TMP1}{benzene}
{benzene.eps}{Structure of protectcompound{cmpd:benzene}.}{benzene}

end{document}

Answered by user232027 on June 24, 2021

If all your calls to insertscheme are of pattern

insertscheme{⟨IDENTIFIER A⟩}%
             {⟨IDENTIFIER B⟩}%
             {⟨IDENTIFIER B⟩.eps}%
             {Structure of compound{cmpd:⟨IDENTIFIER B⟩}.}%
             {⟨IDENTIFIER B⟩}%

, then you can instead define

newcommandinsertscheme[2]{%
  begin{scheme}%
    schemeref[{#1}]{cmpd:#2}%
    includegraphics{#2.eps}%
    caption{Structure of compound{cmpd:#2}.}%
    label{scheme:#2}%
  end{scheme}%
}

and do insertscheme{⟨IDENTIFIER A⟩}{⟨IDENTIFIER B⟩}%.

Then you can define a macro insertschemes which processes a list of comma-separated two-argument-tuples which works as follows:

insertschemes{%
  {TMP1}{benzene},
  ...
  {TMP2}{Brbenzene},
}%

The macro insertschemes in the example below, if called as

insertschemes{%
  {TMP1}{benzene},
  {TMP2}{Brbenzene},
  {TMP3}{Fbenzene},
}%    

, creates the tokens

begin{scheme}%
schemeref[{TMP1}]{cmpd:benzene}%
schemeref[{TMP2}]{cmpd:Brbenzene}%
schemeref[{TMP3}]{cmpd:Fbenzene}%
includegraphics{benzene-Brbenzene-Fbenzene.eps}%
caption{%
  Structure of compound{cmpd:benzene} %
  and compound{cmpd:Brbenzene} %
  and compound{cmpd:Fbenzene}%
}%
label{scheme:benzene-Brbenzene-Fbenzene}%
end{scheme}%

for you.

documentclass{article}
usepackage{chemstyle} 

usepackage{xparse} 
ExplSyntaxOn
clist_new:N l__my_insertschemes_clist
tl_new:N l__my_insertschemes_schemerefs_tl
tl_new:N l__my_insertschemes_compounds_tl
tl_new:N l__my_insertschemes_names_tl
bool_new:N  __bool_my_insertschemes_names_first_item
cs_new_protected:Nn __my_insertschemes_set_tls:nn {
  bool_if:NTF __bool_my_insertschemes_names_first_item {
    bool_set_false:N __bool_my_insertschemes_names_first_item
  }{
    tl_put_right:Nn l__my_insertschemes_compounds_tl {~and~}
    tl_put_right:Nn l__my_insertschemes_names_tl {-}
  }
  tl_put_right:Nn l__my_insertschemes_compounds_tl {compound{cmpd:#2}}
  tl_put_right:Nn l__my_insertschemes_names_tl {#2}
  tl_put_right:Nn l__my_insertschemes_schemerefs_tl {
    schemeref[{#1}]{cmpd:#2}
  }
}
cs_new_protected:Nn __my_insertschemes_use_tls:nnnn{
  % If you want to see what this does, uncomment the following two comments:
  %deftempa{
    begin{#1}
    #2
    includegraphics{#3.eps}
    caption{Structure~of~#4}
    label{scheme:#3}
    end{#1}
  %}showtempa
}
cs_generate_variant:Nn __my_insertschemes_use_tls:nnnn {nVVV}

NewDocumentCommand insertschemes {m} {
  group_begin:
  clist_clear:N l__my_insertschemes_clist
  tl_clear:N l__my_insertschemes_schemerefs_tl
  tl_clear:N l__my_insertschemes_compounds_tl
  tl_clear:N l__my_insertschemes_names_tl
  clist_set:Nn l__my_insertschemes_clist {#1}
  bool_set_true:N __bool_my_insertschemes_names_first_item
  clist_map_inline:Nn l__my_insertschemes_clist {
    __my_insertschemes_set_tls:nn ##1
  }
  __my_insertschemes_use_tls:nVVV {scheme}
                                   {l__my_insertschemes_schemerefs_tl} 
                                   {l__my_insertschemes_names_tl} 
                                   {l__my_insertschemes_compounds_tl}
  group_end:
}
ExplSyntaxOff

begin{document}

insertschemes{%
  {TMP1}{benzene},
  {TMP2}{Brbenzene},
  {TMP3}{Fbenzene},
}%

end{document}

If you need to process more than two arguments per argument-tuple, then all you need to do is to redefine __my_insertschemes_set_tls:nn to process more than two arguments/identifiers.



Edit 1:

That was one of my first attempts to use expl3/LaTeX3.

To be honest, I like the approach of wipet much better:

His macros are short and are all expandable.
His approach is very resource-efficient, since no temporary assignments are made in any loop, such as defining temporary helper macros and the like.
And with his doscheme you don't need to type so many curly braces. ;-)


Edit 2:

Now that wipet has started the ball rolling, a variation of his code where in the very edge case of doscheme's 2nd argument being empty compounds will not create a compound-command in the 1st iteration:

documentclass{article}
usepackage{chemstyle} 

defdoscheme[#1]#2{%
   begin{scheme}%
   schemerefs #1--;#2--%
   includegraphics{#2.eps}%
   caption{Structure of compounds{}#2--.}%
   label{scheme:#2}%
   end{scheme}%
}
defschemerefs #1-#2;#3-{%
   ifx^#1^elseschemeref[{#1}]{cmpd:#3}afterfi{schemerefs#2;}fi
}
defcompounds #1#2-{%
  ifx^#2^else#1compound{cmpd:#2}afterfi{compounds{ and }}fi
}
defafterfi#1#2fi{fi#1}

begin{document}

Tests:
doscheme[TMP1]{benzene}
doscheme[TMP1-TMP2]{benzene-Brbenzene}
doscheme[TMP1-TMP2-TMP3]{benzene-Brbenzene-Rkbenzene}

end{document}


If you don't wish the name of the image-file, the text of the caption and the name of the cross-referencing-label to be created automatically but wish them to be passed as arguments, the following code defines a variant of insertschemes where

insertschemes{%
  {TMP1}{benzene},
  {TMP2}{Brbenzene},
  {TMP3}{Fbenzene},
}{NameOfImageFile}{text of caption}{CrossReferencingLabel}%

delivers the tokens

begin{scheme}%
schemeref[{TMP1}]{cmpd:benzene}%
schemeref[{TMP2}]{cmpd:Brbenzene}%
schemeref[{TMP3}]{cmpd:Fbenzene}%
includegraphics{NameOfImageFile.eps}%
caption{text of caption}%
label{scheme:CrossReferencingLabel}%
end{scheme}%

, i.e., only the sequence of schemeref-commands is created automatically:

documentclass{article}
usepackage{chemstyle} 

usepackage{xparse} 
ExplSyntaxOn
clist_new:N l__my_insertschemes_clist
tl_new:N l__my_insertschemes_schemerefs_tl
cs_new_protected:Nn __my_insertschemes_set_tls:nn {
  tl_put_right:Nn l__my_insertschemes_schemerefs_tl {
    schemeref[{#1}]{cmpd:#2}
  }
}
cs_new_protected:Nn __my_insertschemes_use_tls:nnnnn {
  % If you want to see what this does, uncomment the following two comments:
  %deftempa{
    begin{#1}
    #2
    includegraphics{#4.eps}
    caption{#3}
    label{scheme:#5}
    end{#1}
  %}showtempa
}
cs_generate_variant:Nn __my_insertschemes_use_tls:nnnnn {nVnnn}

NewDocumentCommand insertschemes {mmmm} {
  group_begin:
  clist_clear:N l__my_insertschemes_clist
  tl_clear:N l__my_insertschemes_schemerefs_tl
  clist_set:Nn l__my_insertschemes_clist {#1}
  clist_map_inline:Nn l__my_insertschemes_clist {
    __my_insertschemes_set_tls:nn ##1
  }
  __my_insertschemes_use_tls:nVnnn {scheme}
                                   {l__my_insertschemes_schemerefs_tl}
                                   {#3}
                                   {#2} 
                                   {#4}
  group_end:
}
ExplSyntaxOff

begin{document}

insertschemes{%
  {TMP1}{benzene},
  {TMP2}{Brbenzene},
  {TMP3}{Fbenzene},
}{NameOfImageFile}{text of caption}{CrossReferencingLabel}%

end{document}

Syntax where optional arguments of schemeref are optional within the argument-tuples also can be achieved without expl3 - here the loop is carried out inside the scheme-environment:

documentclass{article}
usepackage{chemstyle} 

makeatletter
begingroup
% Removespaces removes one leading and one trailing space from its argument if present.
% Be aware that this implementation also removes a leading implicit space token like
% @sptoken but does not remove a trailing implicit space token.
defRemovespaces#1{%
  endgroup
  newcommandRemovespaces[1]{%
    % Let's have romannmeral create a negative number from an alphabetic constant.
    % The negative number will not be printed by romannumeral but things get expanded
    % while scanning for the <optional space> that is probably trailing the alphabetic
    % constant and that is also removed if present. (It will be present if the argument
    % of Removespaces has a leading space.)
    romannumeral-`AEnsureForbiddenBeforeTrailSpacenoexpand##1UD@Forbidden#1UD@Forbidden
  }
}Removespaces{ }%
@ifdefinableEnsureForbiddenBeforeTrailSpace{%
  longdefEnsureForbiddenBeforeTrailSpace#1 UD@Forbidden{%
    RemoveTrailingForbiddens#1UD@Forbidden
  }%
}%
@ifdefinableRemoveTrailingForbiddens{%
  longdefRemoveTrailingForbiddens#1UD@Forbidden#2{#1}%
}%
@ifdefinablegobbledot{defgobbledot.{}}%
@ifdefinableschemerefloop{%
  longdefschemerefloop#1,{%
    if$detokenizeexpandafter{@secondoftwo#1{}}$%
    expandafter@firstoftwoelseexpandafter@secondoftwofi
    {schemerefloop.}{%
       expandafterexpandafter
       expandafterexpandafter
       expandafterexpandafter
       expandafterdoschemeref
       expandafterRemovespaces
       expandafter{gobbledot#1}%
    }%
  }%
}%
newcommanddoschemeref{%
  @ifnextchar[doschemeref@optdoschemeref@noopt
}%
newcommanddoschemeref@opt[2][]{%
  schemeref[{#1}]{cmpd:#2}schemerefloop.%
}%
newcommanddoschemeref@noopt[1]{%
  ifxschemerefloop#1expandafter@gobbleelseexpandafter@firstofonefi
  {schemeref{cmpd:#1}schemerefloop.}%
}%
@ifdefinableinsertschemes{%
  DeclareRobustCommandinsertschemes[4]{%
     begin{scheme}%
     schemerefloop.#1,{schemerefloop},%
     includegraphics{#2.eps}%
     caption{#3}%
     label{#4}%
     end{scheme}%
  }%
}%
makeatother

begin{document}

insertschemes{%
  [TMP1]{benzene},
  {Brbenzene},
  [TMP3]{Fbenzene},
}{NameOfImageFile}{text of caption}{CrossReferencingLabel}%

end{document}

Answered by Ulrich Diez on June 24, 2021

You can create a number of key-value options for insertscheme:

enter image description here

documentclass{article}

usepackage{chemstyle,graphicx,xkeyval}

makeatletter
define@cmdkey{fam}{scheme}[empty@key]{}
define@cmdkey{fam}{schemeopt}[]{}
define@cmdkey{fam}{compoundAref}[]{}
define@cmdkey{fam}{compoundAlabel}[empty@key]{}
define@cmdkey{fam}{compoundBref}[]{}
define@cmdkey{fam}{compoundBlabel}[empty@key]{}
% Add additional compound keys here...
define@cmdkey{fam}{caption}[empty@key]{}
define@cmdkey{fam}{label}[empty@key]{}

newcommand{insertscheme}[1]{{%
  setkeys{fam}{%
    schemeopt=,
    %compoundAref=,
    compoundAlabel=,
    compoundBref=,
    compoundBlabel=,
    % Add additional compound key defaults here...
    label=,
    #1
  }%
  begin{scheme}
    ifxcmdKV@fam@compoundAlabelempty@keyelse
      begingroupedefx{endgroupnoexpandschemeref[cmdKV@fam@compoundAref]{cmpd:cmdKV@fam@compoundAlabel}}x
    fi
    ifxcmdKV@fam@compoundBlabelempty@keyelse
      begingroupedefx{endgroupnoexpandschemeref[cmdKV@fam@compoundBref]{cmpd:cmdKV@fam@compoundBlabel}}x
    fi
    % Add additional compounds here...
    ifxcmdKV@fam@schemeempty@keyelse
      begingroupedefx{endgroupnoexpandincludegraphics[cmdKV@fam@schemeopt]{cmdKV@fam@scheme}}x
    fi
    ifxcmdKV@fam@captionempty@keyelse
      caption{cmdKV@fam@caption}%
    fi
    ifxcmdKV@fam@labelempty@keyelse
      expandafterlabelexpandafter{scheme:cmdKV@fam@label}%
    fi
  end{scheme}
}}
makeatother

begin{document}

insertscheme{%
  scheme=example-image,
  schemeopt={width=5em},
  compoundAref=TMP1,
  compoundAlabel=benzene,
  compoundBref=TMP2,
  compoundBlabel=Brbenzene,
  caption={Structure of compound{cmpd:benzene} and compound{cmpd:Brbenzene}.}
}

end{document}

You could add additional compound keys compoundCref, compoundClabel, ... with additional setting within insertscheme to suit your needs.

Answered by Werner on June 24, 2021

I suggest to use doscheme macro wit syntax

doscheme[TMPA-TMPB-etc.]{nameA-nameB-etc.}

which does:

  • opens environment scheme
  • does schemeref[TMPA]{cmpd:nameA}schemeref[TMPB]{cmpd:nameB} etc. in loop.
  • inserts nameA-nameB-etc.eps picture.
  • inserts capition Structure of compound{cmpd:nameA} and cmpound{cmpd:nameB} and ... (in loop)
  • inserts label scheme:nameA-nameB-etc.
  • closes environment scheme.

Example:

documentclass{article}
usepackage{chemstyle} 

defdoscheme[#1]#2{%
   begin{scheme}
     schemerefs #1--;#2--;%
     includegraphics{#2.eps}
     caption{Structure of compounds #2--.}
     label{scheme:#2}
   end{scheme}
}
defschemerefs #1-#2;#3-#4;{%
   ifx^#1^else schemeref[#1]{cmpd:#3}afterfi{schemerefs#2;#4;}fi
}
defcompounds #1-{compound{cmpd:#1}compoundsA}
defcompoundsA #1-{%
   ifx^#1^elsespace and compound{cmpd:#1}expandaftercompoundsAfi
}
defafterfi#1#2fi{fi#1}

begin{document}

Tests:
doscheme[TMP1]{benzene}
doscheme[TMP1-TMP2]{benzene-Brbenzene}
doscheme[TMP1-TMP2-TMP3]{benzene-Brbenzene-Rkbenzene}

end{document}

Answered by wipet on June 24, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP