以类似数组的方式存储多个值,然后访问它们

以类似数组的方式存储多个值,然后访问它们

我正在构建一种由两部分组成的词典:第一部分是单词,第二部分是词根。我想自动跟踪哪些单词与哪些词根相关。我已经掌握了这样做的信息:

% \entryword[ROOT]{LANGUAGE}{WORD}{DESCRIPTION}
% \entryword[serve]{english}{reserve}{This is a wonderful word.}
% \entryword{english}{thing}{There's no root for this one.}
\newcommand{\entryword}[4][]{
    \ifthenelse{\isempty{#1}}
        {}
        {
            % The optional argument, if not empty, represents the root.
            % Now, I would like to annotate this word in the list of
            % relatives of the specified root. It should be done here, I think.
        }
    \entryprompt{\foreignlanguage{#2}{#3}}
    #4

    \ifthenelse{\isempty{#1}}
        {}
        {
            Root: \textit{-#1-}.
        }
}

通过以类似数组的方式方便地存储每个根的亲属,我可以自动打印它们:

% \entryroot{serve}{This root comes from latin.}
\newcommand{\entryroot}[2]{
    \entryprompt{\textit{-#1-}}
    #2

    % Here, how could I automate it?
    Relatives: .
}

一位 MWE 表示:

\documentclass[b5paper,11pt]{report}

\usepackage[b5paper,margin=1.5cm]{geometry}
\usepackage{multicol}
\usepackage[main=english]{babel}
\usepackage[utf8]{inputenc}
\usepackage{xifthen}


\newcommand{\entryprompt}[1]{
    {
        \par\noindent
        \textbf{\textsf{#1}}
    }
}

\newcommand{\entryword}[4][]{
    \ifthenelse{\isempty{#1}}
        {}
        {
            % The optional argument, if not empty, represents the root.
            % Now, I would like to annotate this word in the list of
            % relatives of the specified root.
        }
    \entryprompt{\foreignlanguage{#2}{#3}}
    #4

    \ifthenelse{\isempty{#1}}
        {}
        {
            Root: \textit{-#1-}.
        }
}

\newcommand{\entryroot}[2]{
    \entryprompt{\textit{-#1-}}
    #2

    % Here, how could I automate it?
    Relatives: .
}


\begin{document}
\thispagestyle{empty}
\begin{center}
    \Large
    \textbf{Words}
\end{center}

\newpage

\begin{multicols}{2}
    \entryword[serve]{english}{reserve}{This is a wonderful word.}
    \entryword{english}{thing}{There's no root for this word.}
\end{multicols}

\newpage

\thispagestyle{empty}
\begin{center}
    \Large
    \textbf{Roots}
\end{center}

\newpage

\begin{multicols}{2}
    \entryroot{load}{I don't know where this one comes from.}
    \entryroot{serve}{This root comes from latin.}
\end{multicols}
\end{document}

理想情况下,亲属关系应存储在一种可以遍历的表中,以便以多种方式打印。我已经查看了该arrayjobx包,但它似乎不能满足我的要求。特别是:

  • 字符串不能作为索引。
  • 它似乎没有提供一种在数组末尾添加元素的简单方法。

正如 egreg 所建议的,我添加了所需输出的示例:

- 字 -

保留 - 这是一个很棒的词。词根:服务。

事物——这个词没有词根。

-- 根 --

加载-我不知道这个是从哪里来的。

服务 - 这个词根来自拉丁语。相关词:保留。

答案1

您可以定义一个数组,在处理时加载与根相关的内容\entryword。使用时\entryroot,会打印该数组。

\documentclass{article}
\usepackage[english]{babel}
\usepackage{xparse}

\NewDocumentCommand{\entryprompt}{mm}{%
  \par\noindent\textbf{\textsf{#1}} #2%
}

\ExplSyntaxOn
\NewDocumentCommand{\entryword}{ommm}
 {
  \entryprompt{\foreignlanguage{#2}{#3}}{#4}
  \IfValueT{#1}
   {% if a root is present, print it
    \ Root:\nobreakspace#1
    % then add the word as a relative to the root
    \kalrish_addtoroot:nn { #3 } { #1 }
   }
 }
\NewDocumentCommand{\entryroot}{mm}
 {
  \par\noindent
  #1\nobreakspace--\nobreakspace#2
  % if the root has relatives, the corresponding sequence exists
  \seq_if_exist:cT { g_kalrish_root_ \tl_to_str:n { #1 } _seq }
   {
    % start printing the relatives
    \c_space_tl Relatives:~
    % separated by a comma
    \seq_use:cn { g_kalrish_root_ \tl_to_str:n { #1 } _seq } { ,~ }
   }
 }

\cs_new_protected:Npn \kalrish_addtoroot:nn #1 #2
 {
  % if a root appears for the first time, allocate a sequence
  \seq_if_exist:cF { g_kalrish_root_ \tl_to_str:n { #2 } _seq }
   {
    \seq_new:c { g_kalrish_root_ \tl_to_str:n { #2 } _seq }
   }
  % add the current word as a relative
  \seq_gput_right:cn { g_kalrish_root_ \tl_to_str:n { #2 } _seq } { #1 }
 }

\ExplSyntaxOff

\begin{document}

\section{Words}

\entryword[serve]{english}{reserve}{This is a wonderful word.}

\entryword{english}{thing}{There's no root for this word.}

\entryword[serve]{english}{service}{This is also a word of tennis}

\section{Roots}

\entryroot{load}{I don't know where this one comes from.}

\entryroot{serve}{This root comes from latin.}

\end{document}

在此处输入图片描述

答案2

另一种不需要任何包的解决方案(因为只使用了 TeX 原语和基本宏):

\def\sxdef#1{\expandafter\xdef\csname#1\endcsname}
\def\entryword{\def\opt{}\futurelet\next\entrywordA}
\def\entrywordA{\ifx\next[\expandafter\entrywordB \else \expandafter\entrywordC \fi}
\def\entrywordB[#1]{\def\opt{#1}\entrywordC}
\def\entrywordC#1#2#3{{\bf#2} #3%
   \ifx\opt\empty \else \space Root: \opt
      \expandafter\ifx \csname r:\opt\endcsname \relax \sxdef{r:\opt}{#2}%
      \else \sxdef{r:\opt}{\csname r:\opt\endcsname, #2}\fi
   \fi}
\def\entryroot#1#2{#1 -- #2%
   \expandafter \ifx \csname r:#1\endcsname \relax \else
      \space Relatives: \csname r:#1\endcsname \fi
}

\entryword[serve]{english}{reserve}{This is a wonderful word.}

\entryword{english}{thing}{There's no root for this word.}

\entryword[serve]{english}{service}{This is also a word of tennis}

\entryroot{load}{I don't know where this one comes from.}

\entryroot{serve}{This root comes from latin.}

\end

答案3

这是一个适合进一步编辑的演示,骨架是用 LuaLaTeX 编写的。

我们将所有单词和词根存储在两个外部文件中,并在下次运行 TeX 时加载它们。关键行是\newcommand\entryword[4][]{\directlua{storeword([[#1]],[[#2]],[[#3]],[[#4]])}}将所有 TeX 参数传递给 Lua,storeword在本例中传入函数。我们甚至可以用 Lua 处理 TeX 文件(纯文本),而无需 TeXing。

还有很多可能的改进,我们可以补充:

  • 页码:定义<->列表,双向(\label,,)。\ref\pageref
  • 交叉引用:两个方向的单词<->词根(有或没有测试条目的存在)。
  • 对条目进行排序(例如在 Lua、MakeIndex、Xindy、Biber 等中)。现在,条目已散列,因此每次运行 TeX 后,我们可能会得到不同的条目顺序。自己尝试一下。:-)

我们运行lualatex,我附上mal-root.tex文件和结果预览。

% lualatex mal-root.tex
\documentclass[a4paper]{article}
\pagestyle{empty}
\usepackage{luacode}
\parindent=0pt

\begin{document}
\begin{luacode*}
malwords={}
malroots={}

-- Store a single word to a data table...
function storeword(root,lang,word,text)
-- print(root,lang,word,text)
-- split root(s) to a table
  roots={}
  for singleroot in string.gmatch(root, "([^,]+)") do
    table.insert(roots, singleroot)
  end
  malwords[word]={ roots, lang, text }
end -- function storeme

-- Store a single root to a data table...
function storeroot(root,text)
-- print(root,text)
  malroots[root]={ text }
end -- function storeroot

-- Print both lists (words, roots)...
function printall(orderfile)
-- load stored files if they exist
  for _,malf in pairs({"dumpwords.lua","dumproots.lua"}) do
    f=io.open(malf,"r")
    if f then f:close(); dofile(malf); end -- print(malf); 
  end -- for, files

-- Typeset the first list...
  if orderfile==1 and malwordsfull then  -- words
    tex.print("\\section{List of Words}")
    for theword,term in pairs(malwordsfull) do 
      mals="\\textbf{"..theword.."}  -- "..term[3]
      if #term[1]>0 then 
        mals=mals.." Root"
        if #term[1]>1 then mals=mals.."s" end
        mals=mals..": "
        for elemi,elem in pairs(term[1]) do
          mals=mals..elem
          if elemi<#term[1] then mals=mals..", " end
        end -- for
      end -- if #>0
      mals=mals.."\\par"
      tex.print(mals)
    end
  end -- words

-- Typeset the other list (to add cross references?)

  if  orderfile==2 and malrootsfull then -- roots
    tex.print("\\section{List of Roots}")

-- Check all roots...
    for theword,term in pairs(malrootsfull) do 
      mals="\\textbf{"..theword.."} -- "..term[1]
      toadd=""; counter=0
-- Check all words...
      for elem,oword in pairs(malwordsfull) do
-- Check all roots in words...
        for oroota,oroot in pairs(oword[1]) do
          if oroot==theword then counter=counter+1; toadd=toadd..elem..", " end
        end -- for rootsfull
      end -- if orderfile
      if counter>0 then
        mals=mals.." Relative"
        if counter>1 then mals=mals.."s" end
        mals=mals..": \\textit{"..string.sub(toadd,1,-3).."}"
      end -- if
      mals=mals.."\\par"
      tex.print(mals)
    end -- for

  end -- if
end -- function printwords()

-- http://stackoverflow.com/questions/9168058/lua-beginner-table-dump-to-console
function dumper(o)
  if type(o) == 'table' then
    local s = '\n{'
    for k,v in pairs(o) do
      if type(k) ~= 'number' then k = '"'..k..'"' end
      s = s .. '['..k..'] = ' .. dumper(v) .. ', '
    end
    return s..'}\n'
  else
    return '"'..tostring(o)..'"'
  end
end

-- Save data to a single file...
function savedata(file, data, name)
  saveme=io.open(file, "w")
  mojkovo=name.." = "..dumper(data, name)
  saveme:write(mojkovo)
  saveme:close()
end

-- Save both data tables...
function saveall()
  savedata("dumpwords.lua", malwords, "malwordsfull")
  savedata("dumproots.lua", malroots, "malrootsfull")
end
\end{luacode*}


% Let's call Lua...
\newcommand\entryword[4][]{\directlua{storeword([[#1]],[[#2]],[[#3]],[[#4]])}}
\newcommand\entryroot[2]{\directlua{storeroot([[#1]],[[#2]])}}
\newcommand\printwords{\directlua{printall(1)}}
\newcommand\printroots{\directlua{printall(2)}}
\newcommand\savedata{\directlua{saveall()}}

% Call Lua to work (data from the previous TeX run)...
\printwords
\printroots

% Let's test if it works (some words) and ...
\entryword[sleep1,sleep2]{english}{sleeping}{A word with more roots.}
\entryword[serve]{english}{reserve}{This is a wonderful word.}
\entryword{english}{thing}{There's no root for this word.}
\entryword[serve]{english}{service}{This is also a word of tennis.}

% ... and (some roots)
\entryroot{load}{I don't know where this one comes from.}
\entryroot{serve}{This root comes from latin.}

% Save all data to two external files for next TeX run...
\savedata
\end{document}

示例:生成两个列表

相关内容