Suponha que eu tenha uma lista definida via \def\zList{0,0,1,1,1,2,2,10}
.
Como posso obter uma lista (separada por vírgula) 0,1,2,10
que contém os valores exclusivos/distintos em 0,0,1,1,1,2,2,10
?
Existe talvez uma maneira pgfmath
?(Eu também preciso de tudo em contextos mais complexos [pgfplots/-table], então pgfmath
não seria tão ruim...)
\documentclass[a4paper]{article}
\usepackage{tikz}
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
\let\List=\empty% create List
\foreach \n in \zList {%
\pgfmathparse{\n}% <--- A clever method needed here
\ifx\empty\List{} \xdef\List{\pgfmathresult}%
\else \xdef\List{\List,\pgfmathresult}%
\fi}
Show Zero List: \zList
Show List-Actual: \List
Show List-Target: 0,1,2,10
\end{document}
Responder1
Você pode remover duplicatas usandoexpl3
\ExplSyntaxOn
\cs_new_eq:NN \removeclistdupes \clist_remove_duplicates:N
\ExplSyntaxOff
\Removeclistdupes\List
Um exemplo completo
\documentclass[a4paper]{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \removeclistdupes \clist_remove_duplicates:N
\ExplSyntaxOff
\usepackage{tikz}
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
\let\List=\empty% create List
zList
\foreach \n in \zList {%
\pgfmathparse{\n}% <--- A clever method needed here
\ifx\empty\List{} \xdef\List{\pgfmathresult}%
\else \xdef\List{\List,\pgfmathresult}%
\fi}
\removeclistdupes\List
Show Zero List: \zList
Show List-Actual: \List
Show List-Target: 0,1,2,10
\end{document}
Responder2
\documentclass{article}
\usepackage{listofitems,pgffor}
\newcounter{zcount}
\newtoks\mytoks
\mytoks{}
\expandafter\def\csname zmatch0\endcsname{-9999999}% NUMBER NOT IN LIST
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
The original list is \zList
\readlist\zdata{\zList}
\foreachitem\z\in\zdata[]{%
\gdef\ztest{F}%
\foreach\zcnt in {0,...,\thezcount}{%
\ifnum\z=\csname zmatch\zcnt\endcsname\relax\gdef\ztest{T}\fi%
}%
\if F\ztest
\stepcounter{zcount}%
\expandafter\gdef\csname zmatch\thezcount\expandafter\endcsname
\expandafter{\z}%
\expandafter\ifx\expandafter\relax\the\mytoks\relax
\else\mytoks\expandafter{\the\mytoks,}\fi
\mytoks\expandafter{\the\expandafter\mytoks\z}%
\fi
}
The new list is \the\mytoks
\end{document}
Se você não se sente confortável com listas de tokens, aqui está uma versão que usa \def
s, em vez disso
\documentclass{article}
\usepackage{listofitems,pgffor}
\newcounter{zcount}
\expandafter\def\csname zmatch0\endcsname{-9999999}% NUMBER NOT IN LIST
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
The original list is \zList
\readlist\zdata{\zList}
\foreachitem\z\in\zdata[]{%
\gdef\ztest{F}%
\foreach\zcnt in {0,...,\thezcount}{%
\ifnum\z=\csname zmatch\zcnt\endcsname\relax\gdef\ztest{T}\fi%
}%
\if F\ztest
\stepcounter{zcount}%
\expandafter\xdef\csname zmatch\thezcount\expandafter\endcsname
\expandafter{\z}%
\ifnum\thezcount=1\relax
\xdef\zNewList{\csname zmatch1\endcsname}%
\else
\xdef\zNewList{\zNewList,\csname zmatch\thezcount\endcsname}
\fi
\fi
}
The new list is \zNewList
\end{document}
Responder3
Editar: Alguém pediu para cortar zeros "desnecessários". Você pode fazer isso \FPclip
no pacote fp.
Se desejar fazer a classificação com as ferramentas fornecidas por tikz
, você pode considerar a iteração aninhada na lista já construída:
\documentclass[a4paper]{article}
\usepackage[nomessages]{fp}
\usepackage{tikz}
\usetikzlibrary{math}
% pgfmanual.pdf promises a ot of things to work which often don't due to bugs.
% E.g., with tikz/pgf 3.1.1 by default there is no \ifpgfmathfloatparseactive
% and evaluation of if-expressions via \pgfmathfloattofixed seems corrupted.
% \newif\ifpgfmathfloatparseactive
% \pgfmathfloatparseactivefalse
%
% Afaik current release (the date of writing this answer is
% August 28, 2020) is 3.1.5b.
% Seems things are fixed there.
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newif\ifalreadyinserted
\begin{document}
\newcommand\one{1}
\newcommand\two{2}
\newcommand\onecommaeight{1.8}
\newcommand*\zList{0,0,1,1,1.8,1.6754376,\one,\two,1,2,4+4,2,10,1.7,1.7,\onecommaeight,8,1.0}
\newcommand*\List{}% create List
\foreach \n in \zList {%
\pgfmathparse{\n}%
\let\n=\pgfmathresult
\FPclip{\n}{\n}%
\expandafter\PassFirstToSecond\expandafter{\List}{%
\def\List{}%
\global\alreadyinsertedfalse
\foreach \o in
}{%
\tikzmath{%
if (\o <= \n) then {{\xdef\List{\List\ifx\List\empty\else,\fi\o}};}%
else {{\xdef\List{\List\ifx\List\empty\else,\fi\ifalreadyinserted\else\n,\fi\o}};};%
if (\o >= \n) then {{\global\alreadyinsertedtrue};};%
}%
}%
\ifalreadyinserted\else
\xdef\List{\List\ifx\List\empty\else,\fi\n}%
\fi
}
Show Zero List: \texttt{\frenchspacing\string\zList: \meaning\zList}
Show List-Actual: \texttt{\frenchspacing\string\List: \meaning\List}
\end{document}
Explicação:
Você tem a⟨lista passada pelo usuário⟩( \zList
) e a⟨lista ordenada criada até agora⟩( \List
).
Com cada elemento \n
do⟨lista passada pelo usuário⟩faça o seguinte:
Defina um sinalizador (
\ifalreadyinserted
/\alreadyinsertedfalse
/\alreadyinsertedtrue
) para indicar que pode ser necessário inserir esse elemento\n
no⟨lista ordenada criada até agora⟩."Olhe" para cada elemento
\o
do⟨lista ordenada criada até agora⟩para descobrir se o elemento\n
do⟨lista passada pelo usuário⟩precisa ser inserido no⟨lista ordenada criada até agora⟩:Enquanto o valor do elemento
\o
do⟨lista ordenada criada até agora⟩não é maior que o valor do elemento\n
do⟨lista passada pelo usuário⟩, o elemento\n
do⟨lista passada pelo usuário⟩não precisa ser inserido no⟨lista ordenada criada até agora⟩.Se, embora o sinalizador ainda indique que pode haver necessidade de inserir o elemento
\n
no⟨lista ordenada criada até agora⟩, ocorre pela primeira vezque o valor do elemento\o
do⟨lista ordenada criada até agora⟩é maior que o valor do elemento\n
do⟨lista passada pelo usuário⟩, o elemento\n
do⟨lista passada pelo usuário⟩precisa ser inserido no⟨lista ordenada criada até agora⟩antes do\o
elemento⟨lista ordenada criada até agora⟩.
Se ocorrer pela primeira vez...—o sinalizador é necessário para descobrir se é a primeira vez.Se o valor do elemento
\o
do⟨lista ordenada criada até agora⟩é maior ou igual ao valor do elemento\n
do⟨lista passada pelo usuário⟩, então o sinalizador precisa ser definido para indicar que não há necessidade de inserir o elemento\n
do⟨lista passada pelo usuário⟩no⟨lista passada pelo usuário⟩.Se depois de examinar todos os elementos
\o
do⟨lista ordenada criada até agora⟩a flag ainda indica que pode haver necessidade de inserir o elemento\n
do⟨lista passada pelo usuário⟩no⟨lista ordenada criada até agora⟩, então isso indica que o valor do elemento\n
do⟨lista passada pelo usuário⟩é maior que os valores de todos os elementos\o
que já estão no⟨lista ordenada criada até agora⟩e que, portanto, o elemento\n
do⟨lista passada pelo usuário⟩precisa ser anexado ao⟨lista ordenada criada até agora⟩.
Responder4
Para variar, aqui está uma solução baseada em LuaLaTeX.
A string de entrada - definida por, digamos, \zList
- pode conter números, macros (exceto \zList
ela mesma) que se expandem para números e strings que contêm uma lista de números separados por vírgula. Os números não precisam ser classificados em ordem crescente.
\unique
extrai os números classificados exclusivos contidos em \zList
.
% !TeX program = lualatex
\documentclass{article}
%% Lua-side code
\usepackage{luacode} % for 'luacode' environment
\begin{luacode}
function string_to_table (str)
local fields = {} -- initialize the table
str:gsub( "([^,]*)" , function ( c )
-- strip off anyleading and trailing whitespace:
c = c:gsub ( "^%s*(.-)%s*$" , "%1" )
-- insert 'c' in 'fields'
table.insert ( fields , tonumber(c) )
end )
return fields
end
function remove_duplicate_entries ( t )
-- from https://stackoverflow.com/a/20067270/1014365
local hash = {}
local res = {}
for _,v in ipairs(t) do
if (not hash[v]) then
res[#res+1] = v
hash[v] = true
end
end
return res
end
function unique ( s )
local t
-- Convert string 's' to a Lua table:
t = string_to_table ( s )
-- Sort the table entries in ascending order:
table.sort ( t , function(a,b) return a<b end)
-- Retain the unique elements:
t = remove_duplicate_entries ( t )
-- Convert table back to string and print:
tex.sprint ( table.concat ( t, "," ) )
end
\end{luacode}
%% LaTeX-side code:
\newcommand\unique[1]{\directlua{unique(\luastring{#1})}}
\begin{document}
\def\mynum{10}
\newcommand\mystr{"\mynum,0"}
\def\zList{0,10,1,1,2,2,1,0,\mynum,\mystr}
\zList
\unique{\zList}
\end{document}