Предположим, у меня есть список, определенный с помощью \def\zList{0,0,1,1,1,2,2,10}
.
Как получить список (разделенный запятыми) 0,1,2,10
, содержащий уникальные/различимые значения 0,0,1,1,1,2,2,10
?
Может быть, есть способ pgfmath
?(Мне также нужно все это в более сложных контекстах [pgfplots/-table], так что pgfmath
было бы не так уж и плохо...)
\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}
решение1
Вы можете удалить дубликаты, используяexpl3
\ExplSyntaxOn
\cs_new_eq:NN \removeclistdupes \clist_remove_duplicates:N
\ExplSyntaxOff
\Removeclistdupes\List
Полный пример
\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}
решение2
\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}
Если вам некомфортно работать со списками токенов, то вот версия, которая использует \def
s вместо
\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}
решение3
Редактировать: Кто-то просил обрезать "ненужные" нули. Это можно сделать \FPclip
из пакета fp.
Если вы хотите выполнить сортировку с помощью инструментов, предоставляемых tikz
, вы можете рассмотреть вложенную итерацию по уже сформированному списку:
\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}
Объяснение:
У тебя есть⟨список, переданный пользователем⟩( \zList
) и⟨сортированный список, созданный на данный момент⟩( \List
).
С каждым \n
элементом⟨список, переданный пользователем⟩сделайте следующее:
Установите флаг (
\ifalreadyinserted
// )\alreadyinsertedfalse
,\alreadyinsertedtrue
чтобы указать, что может возникнуть необходимость вставить этот элемент\n
в⟨сортированный список, созданный на данный момент⟩.«Посмотрите» на каждый
\o
элемент⟨сортированный список, созданный на данный момент⟩для выяснения, является ли элемент\n
элемент⟨список, переданный пользователем⟩необходимо вставить в⟨сортированный список, созданный на данный момент⟩:Пока значение
\o
элемента⟨сортированный список, созданный на данный момент⟩не больше значения\n
элемента⟨список, переданный пользователем⟩\n
, элемент⟨список, переданный пользователем⟩не нужно вставлять в⟨сортированный список, созданный на данный момент⟩.Если, пока флаг все еще указывает на то, что может возникнуть необходимость вставить элемент
\n
в⟨сортированный список, созданный на данный момент⟩, это происходит в первый разчто значение\o
элемента⟨сортированный список, созданный на данный момент⟩больше, чем значение\n
элемента⟨список, переданный пользователем⟩\n
, элемент⟨список, переданный пользователем⟩необходимо вставить в⟨сортированный список, созданный на данный момент⟩\o
перед элементом⟨сортированный список, созданный на данный момент⟩.
Если это происходит в первый раз... — флаг нужен для того, чтобы узнать, происходит ли это в первый раз.Если значение
\o
элемента⟨сортированный список, созданный на данный момент⟩больше или равно значению\n
элемента⟨список, переданный пользователем⟩, то необходимо установить флаг, указывающий на то, что нет необходимости вставлять\n
элемент⟨список, переданный пользователем⟩в⟨список, переданный пользователем⟩.Если после рассмотрения всех
\o
элементов⟨сортированный список, созданный на данный момент⟩флаг по-прежнему указывает на то, что может возникнуть необходимость вставить\n
элемент⟨список, переданный пользователем⟩в⟨сортированный список, созданный на данный момент⟩, то это указывает на то, что значение\n
элемента⟨список, переданный пользователем⟩больше, чем значения всех элементов\o
, которые уже находятся в⟨сортированный список, созданный на данный момент⟩\n
и что поэтому элемент⟨список, переданный пользователем⟩необходимо приложить к⟨сортированный список, созданный на данный момент⟩.
решение4
Для разнообразия приведу решение на основе LuaLaTeX.
Входная строка -- определяемая, скажем, \zList
-- может содержать числа, макросы (кроме \zList
себя), которые расширяются до чисел, и строки, содержащие список чисел, разделенных запятыми. Числа не обязательно должны быть отсортированы в порядке возрастания.
\unique
извлекает уникальные отсортированные числа, содержащиеся в \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}