0

I was trying to come up with a Lua-based solution to another question here and failed miserably. There something I certainly don't understand about LuaLaTeX. Here is a toy code that shows the problem.

The Lua function receives an integer that must be 1 or 2 and, depending on this value, will return (through tex.print) a different subdivision of interval [0,3]. The returned string is received by a foreach loop, hopefully to use it as a list of abscissas for a graph.

\documentclass{article}
\usepackage{luacode}
\usepackage{tikz}

\LuaCodeDebugOn
\begin{luacode}
function listx(n)
    local values = ""
    if n == 1 then
        for i=1, 3 do
            values = values .. i .. ","
        end
    elseif n == 2 then
        for i=1, 6 do
            values = values .. i/2 .. ","
        end
    end
    values = values:sub(1, -2)
    tex.print(values)
end
\end{luacode}

\begin{document}
\directlua{listx(2)}  % displays 0.5,1.0,1.5,2.0,2.5,3.0

\begin{tikzpicture}
\foreach \x in {\directlua{listx(2)}} { 
    \fill (\x,0.5) circle (0.02);
};
\end{tikzpicture}
\end{document}

The first line after \begin{document} shows that the function prints the expected list of values, just as a test.

But the TikZ picture shows a single dot at coordinates (0,0), instead of 3 or 6 dots evenly spaced. My guess is that, for some reason, tex.print doesn't send the data in the proper format for foreach, or that some LaTeX processing happens too soon, or too late...

I'm surprised because I routinely use Lua code to compute numerical values that I pass to siunitx for good rendering. I assume the problem comes from the fact that I'm trying to pass a list of values from Lua to LaTeX, something that I had never tried to do before.

In any case, I'm a bit out of my league, and I don't seem to be able to make \LuaCodeDebugOn show any code to help me understand what's happening.

How must I call the Lua function, how must it send its result back, so that foreach understands it as a list of numbers to loop over?

(please don't pay too much attention to the Lua code itself, it's just a stupid toy program I wrote for this post)

4

3 Answers 3

2

More recent versions of tikz support the expand list option for \foreach:

\foreach[expand list] \x in {\directlua{listx(2)}} {  % <--- changed
    \fill (\x,0.5) circle (0.02);
};

MWE:

\documentclass{article}
\usepackage{luacode}
\usepackage{tikz}

\LuaCodeDebugOn
\begin{luacode}
function listx(n)
    local values = ""
    if n == 1 then
        for i=1, 3 do
            values = values .. i .. ","
        end
    elseif n == 2 then
        for i=1, 6 do
            values = values .. i/2 .. ","
        end
    end
    values = values:sub(1, -2)
    tex.print(values)
end
\end{luacode}

\begin{document}
\directlua{listx(2)}  % displays 0.5,1.0,1.5,2.0,2.5,3.0

\begin{tikzpicture}
\foreach[expand list] \x in {\directlua{listx(2)}} {  % <--- changed
    \fill (\x,0.5) circle (0.02);
};
\end{tikzpicture}
\end{document}

enter image description here

1
  • 1
    Thank you, this is exactly what I was hoping to be able to use as a syntax! It works like a charm.
    – Miyase
    Commented Jun 24, 2022 at 13:15
1

I don't think it's particularly "bad" to use edef, but this is a "pure Lua" solution:

nevertheless not everything can be done in pure Lua, if you want some TeX programming you need to switch to TeX sometimes, see Concurrently interleaving execution of Lua and TeX in LuaTeX

%! TEX program = lualatex
\documentclass{article}
\usepackage{luacode}
\usepackage{tikz}

\LuaCodeDebugOn
\begin{luacode*}
function foreachlistx(n)
    local values = ""
    if n == 1 then
        for i=1, 3 do
            values = values .. i .. ","
        end
    elseif n == 2 then
        for i=1, 6 do
            values = values .. i/2 .. ","
        end
    end
    values = values:sub(1, -2)
    tex.print([[\foreach \x in {]] .. values .. "}")
end
\end{luacode*}

\begin{document}

\begin{tikzpicture}
\directlua{foreachlistx(2)} {
    \fill (\x,0.5) circle (0.02);
};
\end{tikzpicture}
\end{document}

Of course the solution could be improved a bit by e.g. splitting the value list computation to a separate Lua function that can be called independently, but as I mentioned previously \foreach does not expand its argument in braces there isn't really a cleaner way. What it does should be relatively easy to understand (\directlua{...} "expands" (*) to \foreach \x in {...}, which is followed by the code to be executed, which as you can see does what you want)

(*): from LuaTeX manual

Note that the expansion of \directlua is a sequence of characters, not of tokens, contrary to all TEX commands. So formally speaking its expansion is null, but it places material on a pseudo-file to be immediately read by TEX, as 𝜀-TEX’s \scantokens.

1
  • Thanks again, for the code and also for the links. I'm currently trying to see how far I can push Lua in LaTeX, and at this point I need a better understanding of their interaction. This will help a lot.
    – Miyase
    Commented Jun 18, 2022 at 7:16
0

you can speak:

\edef\mylist{\directlua{listx(2)}}

\begin{tikzpicture}
\foreach \x  in \mylist { 
    \fill (\x,0.5) circle (0.1);
}
\end{tikzpicture}

or

\ExplSyntaxOn
\begin{tikzpicture}
\exp_args:Nx\clist_map_inline:nn{\directlua{listx(2)}}
{
  \fill (#1,0.5) circle(0.1);
}
\end{tikzpicture}
\ExplSyntaxOn
5
  • Thank you for this solution. It does work, however I lose the ability to pass 1 or 2 as an argument. I read a bit about \edef and it doesn't seem to be possible to make it accept arguments without quite a bit of tweaking.
    – Miyase
    Commented Jun 18, 2022 at 0:49
  • @Miyase What do you mean "pass argument"? You're passing 2 there
    – user202729
    Commented Jun 18, 2022 at 0:58
  • But that 2 is hard-coded in the \mylist macro, so I can't change it when I call the macro in the loop.
    – Miyase
    Commented Jun 18, 2022 at 7:12
  • @Miyase Yes you can, just put the whole edef inside the loop.
    – user202729
    Commented Jun 18, 2022 at 8:18
  • OK, I'll try that. Thanks again.
    – Miyase
    Commented Jun 18, 2022 at 8:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .