2

\const does not work for the current definition. It is defined to easily declared constants in both TeX and PostScript scopes. For example, if I want to define a const Speed of a value 300000000, I can declare it with a single call \const{Speed}{300000000} which is known in both PS and TeX scopes. Another example, if I want to create a const NumberOfElements in PS and TeX scopes, I can call \const{NumberOfElements}{\length{\list}} where \length (defined as shown in the MWE) and \def\list{a,b,c}.

The problem is I have no knowledge to make \const{\length{\list}} work while keeping \const works as the original intent. Here is the MWE.

\documentclass[pstricks,border=12pt]{standalone}

\usepackage{pstricks-add,fp}

% user defined data
\def\listX{1,2,3,4,5,6}
\def\listY{1,2,3}

% to determine the number of elements in a list
\newcounter{x}
\def\length#1{\setcounter{x}{0}\psforeach{\i}{#1}{\stepcounter{x}}\thex}


% to create constants in both TeX and PS scopes
\def\const#1#2{%
    \expandafter\FPeval\csname#1\endcsname{#2}%
    \pstVerb{/#1 \csname#1\endcsname\space def}%
}



\const{Nx}{\length{\listX}}
\const{Ny}{\length{\listY}}
\const{CellW}{1}
\const{CellH}{2}


\const{CanvasWidth}{Nx*CellW}
\const{CanvasHeight}{Ny*CellH}


\begin{document}

\begin{pspicture}[showgrid=bottom](\CanvasWidth,\CanvasHeight)
    % use other PS constants here
\end{pspicture}

\end{document}

Bonus question: Is there a smarter way to know the number of elements in a list?

3
  • 4
    Could you try to come up with a more descriptive title to your question, one that is more specific? The current title could hopefully apply to any question we have here -- I don't think people ask questions that they can easily answer themselves.
    – doncherry
    Jul 23, 2012 at 14:07
  • This is a better title, but I don't think the works is grammatically correct. Howsabout omitting the word?
    – user10274
    Dec 30, 2012 at 0:58
  • 1
    Great title....
    – user10274
    Dec 30, 2012 at 7:21

2 Answers 2

8
+100

Let's see the matter in general, though a deep discussion would require many pages of a book.

You are concerned with definitions of commands, not only with nesting macro calls. The "working" definition

\def\const#1#2{\expandafter\def\csname#1\endcsname{#2}}

with the call

\const{Count}{\length{\list}}

is equivalent to

\def\Count{\length{\list}}

If you have also \def\list{1,2,3}, then

\const{Count}{\length{\list}}\Count

would print 3. However, if you say

\def\list{1,2,3}
\const{Count}{\length{\list}}
\Count

\def\list{a,b,c,d}
\Count

you'd get

3
4

Why? Because the macro \Count is defined in terms of \list, which will then use the current meaning of \list, not the one at definition time: \list is just a pointer to a token list (the current replacement text).

Your problem is to assign as the meaning \Count the current length of \list, independently from possible subsequent redefinitions. And you want it as a plain number, because you must pass it to \pstVerb as the replacement text of a PostScript token.

The way to cope with this is to perform the computations, store the numeric result in a temporary control sequence and then using \edef.

Here's a way:

\makeatletter
\newcount\const@count
\def\length#1{%
  \const@count=\z@
  \@for\next:=#1\do{\advance\const@count\@ne}%
  \edef\@tempa{\number\const@count}%
}
\def\const#1#2{%
  #2%
  \expandafter\let\csname #1\endcsname\@tempa
  \begingroup\edef\x{\endgroup
    \noexpand\pstVerb{/#1 \@tempa\space def}}\x
}
\makeatother

Call this as

\const{Count}{\length{\list}}

The important thing is to perform the computation before defining \Count and doing \pstVerb.

This is analogous to what you're doing elsewhere with \FPeval.

First of all \length{\list} is computed and the result is stored in \@tempa with \edef, so that the actual number is the replacement text. Then \let will make \Count have the current meaning of \@tempa and then the \pstVerb is emitted, in an indirect way, to avoid any reference to \@tempa in the replacement text, thanks to \edef again.


Let' try to generalize this; I suggest a three argument macro:

\makeatletter
\def\const#1#2#3{% #1=name, #2=method, #3=data
   #2{#1}{#3}%
   \begingroup\edef\x{\endgroup
     \noexpand\pstVerb{/#1 \csname#1\endcsname\space def}}\x
}
\newcount\const@count
\def\length#1#2{%
  \const@count=\z@
  \@for\next:=#2\do{\advance\const@count\@ne}%
  \expandafter\edef\csname#1\endcsname{\number\const@count}%
}
\makeatother

Now \const{Count}{\length}{\list} or \const{ABC}{\FPeval}{2*3} will work.

If \FPeval is the default method, you can simplify the syntax:

\documentclass{article}
\usepackage{pstricks-add,fp}

% user defined data
\def\list{1,2,3,4,5,6}

\makeatletter
\newcommand\const[3][\FPeval]{% #1=method, #2=name, #3=data
   \expandafter#1\csname#2\endcsname{#3}%
   \begingroup\edef\x{\endgroup
     \noexpand\pstVerb{/#2 \csname#2\endcsname\space def}}\x
}
\newcount\const@count
\def\length#1#2{%
  \const@count=\z@
  \@for\next:=#2\do{\advance\const@count\@ne}%
  \edef#1{\number\const@count}%
}
\makeatother

\begin{document}

\const[\length]{Count}{\list}
\Count

\const{ABC}{2*2}
\ABC
\end{document}
0
4
+50

It is simplest if you work purely by expansion, then the behaviour of the TeX and PS constructs are more similar. I changed the syntax slightly using \ in the product expression to save parsing the tokens.

\documentclass[pstricks,border=12pt]{standalone}

\usepackage{pstricks-add,fp}

% user defined data
\def\listX{1,2,3,4,5,6}
\def\listY{1,2,3}

% to determine the number of elements in a list
\makeatletter

\long\def\length#1{%
\expandafter\xlength\expandafter0#1,\relax,}

\long\def\xlength#1#2,{%
\ifx\relax#2%
\the\numexpr#1\relax
\expandafter\@gobblefour
\fi
\expandafter\xlength\expandafter{\the\numexpr#1+1\relax}}
\makeatother

% to create constants in both TeX and PS scopes
\def\const#1#2{%
    \expandafter\edef\csname#1\endcsname{\the\numexpr#2\relax}%
    \pstVerb{/#1 \csname#1\endcsname\space def}%
}



\const{Nx}{\length{\listX}}
\const{Ny}{\length{\listY}}
\const{CellW}{1}
\const{CellH}{2}


\const{CanvasWidth}{\Nx*\CellW}
\const{CanvasHeight}{\Ny*\CellH}


\begin{document}

\begin{pspicture}[showgrid=bottom](\CanvasWidth,\CanvasHeight)
    % use other PS constants here
\end{pspicture}

\end{document}

You must log in to answer this question.

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