Skip to content

Costruire e utilizzare un elenco

Questa è la soluzione più valida che troverai da offrire, guardala però attentamente e analizza se si adatta al tuo lavoro.

Soluzione:

Questo è essenzialmente in expl3 fuori dalla scatola. L'idea è di mappare l'elenco originale; se un elemento non è presente nell'elenco di rimozione, lo si aggiunge a un elenco temporaneo e, alla fine, si reimposta l'elenco originale a quello temporaneo.

Ho aggiunto un po' di zucchero sintattico agli elenchi di nomi, che permette di averne molti senza definire nuove macro.

documentclass{article}
usepackage{xparse}

ExplSyntaxOn
NewDocumentCommand{definelist}{mm}
 {% #1 = list name, #2 = items
  % allocate a new list or clear an existing one
  clist_clear_new:c { l_kessels_#1_clist }
  % set the list
  clist_set:cn { l_kessels_#1_clist } { #2 }
 }

NewDocumentCommand{subtractlist}{mm}
 {% #1 = original, #2 = items to subtract
  kessels_list_subtract:nn { #1 } { #2 }
 }

NewDocumentCommand{uselist}{mm}
 {% #1 = list name, #2 = output separator
  clist_use:cn { l_kessels_#1_clist } { #2 }
 }

clist_new:N l__kessels_list_temp_clist

cs_new_protected:Nn kessels_list_subtract:nn
 {
  % clear the temporary list
  clist_clear:N l__kessels_list_temp_clist
  % map the original list
  clist_map_inline:cn { l_kessels_#1_clist }
   {
    % if the item doesn't appear in the remove list, add it to the temp one
    clist_if_in:cnF { l_kessels_#2_clist } { ##1 }
     {
      clist_put_right:Nn l__kessels_list_temp_clist { ##1 }
     }
   }
  % reconstitute the original list
  clist_set_eq:cN { l_kessels_#1_clist } l__kessels_list_temp_clist
 }

ExplSyntaxOff

begin{document}

definelist{original}{Lima,Alpha,Delta,Oscar,Tango,Whisky,Echo,Romeo,Xray}
definelist{remove}{Whisky,Oscar,Romeo,Delta}

uselist{original}{, }

subtractlist{original}{remove}

uselist{original}{, }

end{document}

enter image description here

Si potrebbe pensare di aggiungere copylist per salvare una copia di un elenco e maplist per definire azioni sugli elementi.

Un'implementazione più completa. Il secondo argomento di maplist è un modello che prende ogni elemento come #1.

documentclass{article}
usepackage{xparse}

ExplSyntaxOn
NewDocumentCommand{definelist}{mm}
 {% #1 = list name, #2 = items
  % allocate a new list or clear an existing one
  clist_clear_new:c { l_kessels_#1_clist }
  % set the list
  clist_set:cn { l_kessels_#1_clist } { #2 }
 }
NewDocumentCommand{prependtolist}{mm}
 {
  clist_put_left:cn { l_kessels_#1_clist } { #2 }
 }
NewDocumentCommand{appendtolist}{mm}
 {
  clist_put_right:cn { l_kessels_#1_clist } { #2 }
 }

NewDocumentCommand{subtractlist}{mm}
 {% #1 = original, #2 = items to subtract
  kessels_list_subtract:nn { #1 } { #2 }
 }

NewDocumentCommand{uselist}{mm}
 {% #1 = list name, #2 = output separator
  clist_use:cn { l_kessels_#1_clist } { #2 }
 }

NewDocumentCommand{copylist}{mm}
 {
  clist_clear_new:c { l_kessels_#2_clist }
  clist_set_eq:cc { l_kessels_#2_clist } { l_kessels_#1_clist }
 }

NewDocumentCommand{maplist}{mm}
 {
  cs_set:Nn __kessels_list_map:n { #2 }
  clist_map_function:cN { l_kessels_#1_clist } __kessels_list_map:n
 }

clist_new:N l__kessels_list_temp_clist

cs_new_protected:Nn kessels_list_subtract:nn
 {
  % clear the temporary list
  clist_clear:N l__kessels_list_temp_clist
  % map the original list
  clist_map_inline:cn { l_kessels_#1_clist }
   {
    % if the item doesn't appear in the remove list, add it to the temp one
    clist_if_in:cnF { l_kessels_#2_clist } { ##1 }
     {
      clist_put_right:Nn l__kessels_list_temp_clist { ##1 }
     }
   }
  % reconstitute the original list
  clist_set_eq:cN { l_kessels_#1_clist } l__kessels_list_temp_clist
 }

ExplSyntaxOff

begin{document}

definelist{original}{Lima,Alpha,Delta,Oscar,Tango,Whisky,Echo,Romeo,Xray}
definelist{remove}{Whisky,Oscar,Romeo,Delta}

uselist{original}{, }

copylist{original}{changed}

subtractlist{changed}{remove}

uselist{changed}{, }

maplist{changed}{fbox{#1vphantom{Ay}} }

maplist{original}{fbox{#1vphantom{Ay}} }

end{document}

enter image description here

Come ha detto Andrew, expl3 ha ottimi strumenti di alto livello per molte cose, compresa l'elaborazione delle liste. Ma expl3 o meno, la cosa più importante è capire cosa si sta facendo e inviare un file minimo funzionante quando siete bloccati (il vostro codice non ha end{document}).

Quindi, mi concentrerò sul tuo "secondo approccio". Si inizializza il risultato con un primo elemento che è @gobblee vi aspettate che il problema scompaia magicamente? Perché dovrebbe? Non è così, ed è per questo che meaning vi ha mostrato. L'idea era probabilmente quella di finalizzare il risultato con qualcosa di simile a xdefprocessedlist{processedlist} in modo che l'elemento @gobble si mangi la virgola che la segue, ma questo è molto brutto, dato che xdef (come edef) potrebbe causare problemi quando si espandono i token dagli elementi reali (cose come textbf, ref, ecc.). [email protected] sarebbe meno problematico, ma comunque espanderebbe le cose in tutti gli elementi senza una buona ragione.

In questo caso, poiché gli oggetti sono costituiti solo da token di caratteri non attivi, questo approccio funzionerebbe, ma si romperebbe con input più delicati e avrebbe effetti collaterali indesiderati, quindi è una cattiva pratica IMO. La mia raccomandazione è di non aggiungere l'elemento @gobble e controllare semplicemente se processedlist è vuoto, per decidere se anteporre una virgola all'elemento appena aggiunto.

Detto questo, fornirò un modo per fare con l'elemento @gobble alla fine senza effetti collaterali indesiderati. È un po' più lungo del codice seguente, ma dovrebbe essere leggermente più veloce, poiché il codice per aggiungere elementi a processedlist ha un controllo in meno da fare.

Indipendentemente dalla tecnica utilizzata per eliminare la prima virgola, l'algoritmo è relativamente inefficiente, perché anche quando il ciclo interno ha trovato l'elemento corrente di #1 in #2continuerà a controllare tutti gli elementi rimanenti di #2. expl3 ha prg_break_point: e prg_break: per evitare questo problema e, soprattutto, le funzioni di mappatura che supportano questo tipo di interruzione del ciclo (ad es, clist_map_inline:nn e clist_map_break:).

documentclass{article}
usepackage[T1]{fontenc}        % for the '>' character in horizontal mode

makeatletter
newififisinlist

defsubtractlist#1#2{% #1:original list, #2:remove list
  defprocessedlist{}%
  @fortempa:=#1do{%
    isinlistfalse
    @fortempb:=#2do{ifxtempatempbisinlisttruefi}%
    ifisinlist
    else
      ifxprocessedlistempty
        expandaftergdefexpandafterprocessedlistexpandafter{tempa}%
      else
         expandafter[email protected]@macro
         expandafterprocessedlist
         expandafter{expandafter,tempa}%
      fi
    fi
  }%
}
makeatother

setlength{parindent}{0pt}
setlength{parskip}{1.5ex plus 0.5ex minus 0.2ex}

begin{document}

section*{Second macro for subtracting two lists}

deforiginallist{Lima,Alpha,Delta,Oscar,Tango,Whisky,Echo,Romeo,Xray}
defremovelist{Whisky,Oscar,Romeo,Delta}

verb|originallist|: originallistpar
verb|removelist|: removelistpar
verb|subtractlist{originallist}{removelist}|par
subtractlist{originallist}{removelist}
verb|processedlist|: processedlistpar

meaningprocessedlist

end{document}

enter image description here

Con la funzione @gobble

Come promesso, ecco un modo per fare le cose correttamente con l'iniziale @gobble:

documentclass{article}
usepackage[T1]{fontenc}        % for the '>' character in horizontal mode

makeatletter
newififisinlist

defstartingPoint{@gobble}

defsubtractlist#1#2{% #1:original list, #2:remove list
  globalletprocessedliststartingPoint
  @fortempa:=#1do{%
    isinlistfalse
    @fortempb:=#2do{ifxtempatempbisinlisttruefi}%
    ifisinlist
    else
      expandafter[email protected]@macro
      expandafterprocessedlist
      expandafter{expandafter,tempa}%
    fi
  }%
  ifxprocessedliststartingPoint
    gdefprocessedlist{}%
  else
    % Expand twice so that the @gobble eats the comma that follows it.
    xdefprocessedlist{%
      unexpandedexpandafterexpandafterexpandafter{processedlist}}%
  fi
}
makeatother

setlength{parindent}{0pt}
setlength{parskip}{1.5ex plus 0.5ex minus 0.2ex}

begin{document}

section*{Second macro for subtracting two lists}

deforiginallist{Lima,Alpha,Delta,Oscar,Tango,Whisky,Echo,Romeo,Xray}
defremovelist{Whisky,Oscar,Romeo,Delta}

verb|originallist|: originallistpar
verb|removelist|: removelistpar
verb|subtractlist{originallist}{removelist}|par
subtractlist{originallist}{removelist}
verb|processedlist|: processedlistpar

meaningprocessedlist

end{document}

Stesso risultato di cui sopra.

Nota: se non si capisce il seguente pezzo di codice:

xdefprocessedlist{%
  unexpandedexpandafterexpandafterexpandafter{processedlist}}%

fa la stessa cosa di:

expandafterexpandafter
expandaftergdef
expandafterexpandafter
expandafterprocessedlist
expandafterexpandafter
expandafter{processedlist}%

P.S.: Trovo semplice expl3 più leggibile di mucchi di codice expandafterusati per fare cose molto semplici.

Un approccio diverso con listofitems. In sostanza, uso i remove-list come separatori di liste. In questo modo, non appaiono in un rigurgito di elenco digerito, perché sono stati rimossi come separatori di elenco (insieme alle virgole in eccesso).

documentclass{article}
usepackage{listofitems}
newcommandsubtractlists[2]{%
  expandaftersetsepcharexpandafter{expandafter,expandafter/#2}%
  readlist*mylist{#1}%
  foreachitemzinmylist[]{%
    ifnumzcnt>1relaxifnumlistlenmylist[zcnt]=1relax,fifi
    foreachitemzzinmylist[zcnt]{%
      ifnumzzcnt>0relaxzzfi}}%
}
begin{document}
deforiginallist{Lima,Alpha,Delta,Oscar,Tango,Whisky,Echo,Romeo,Xray}
List: originallist

defremovelist{Whisky||Oscar||Romeo||Delta}
Remove: removelist

Result:
subtractlists{originallist}{removelist}
end{document}

enter image description here

Se si insiste sul fatto che l'elenco di rimozione debba essere separato da virgole (invece che da ||), è necessario un piccolo extra:

documentclass{article}
usepackage{listofitems}
makeatletter
newcommandsubtractlists[2]{%
  setsepchar{,}%
  readlistremlist{#2}%
  deftmp{,/}%
  foreachitemzinremlist[]{ifnumzcnt=1else[email protected]@macrotmp{||}fi
    expandafter[email protected]@macroexpandaftertmpexpandafter{z}}%
  expandaftersetsepcharexpandafter{tmp}%
  readlist*mylist{#1}%
  foreachitemzinmylist[]{%
    ifnumzcnt>1relaxifnumlistlenmylist[zcnt]=1relax,fifi
    foreachitemzzinmylist[zcnt]{%
      ifnumzzcnt>0relaxzzfi}}%
}
makeatother
begin{document}
deforiginallist{Lima,Alpha,Delta,Oscar,Tango,Whisky,Echo,Romeo,Xray}
List: originallist

defremovelist{Whisky,Oscar,Romeo,Delta}
Remove: removelist

Result:
subtractlists{originallist}{removelist}
end{document}

enter image description here

valutazioni e commenti

Alla fine di questo articolo puoi trovare le spiegazioni di altri utenti, puoi anche mostrare la tua se ti piace.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.