1

I need to write a metapost vardef returning several numerical values. Metapost seems to allow returning only one value.

How to make a metapost vardef returning multiple numerical values?

  • Hello Peteris, and welcome to tex.SE! It might just be me, but I have no idea what you are asking! What do you mean by metapost and vardef? – Andreas Storvik Strauman Aug 31 '18 at 20:22
  • @AndreasStorvikStrauman Metapost is a TeX friend that produces Postscript graphics; vardef is a primitive of Metapost, for defining a special type of macro. – egreg Aug 31 '18 at 20:48
  • 1
    It would be better if you gave an example of what's your aim. – egreg Aug 31 '18 at 20:48
  • Could you return a pair or a color? Then you could extract the numbers with xpart, ypart, redpart, etc – Thruston Sep 1 '18 at 17:48
  • In metapost you can return pairs but it's just 2 numbers. I need more than 2. – Peteris Daugulis Sep 1 '18 at 21:07
2

In MetaPost a begingroup ...;... endgroup construct can only return a single value and a vardef automatically consists of one of these, but you can break out of it. If the values are independent and you always return the same amount, you could use

vardef returnMultipleValues(expr x) =
    x + 1
  endgroup, begingroup
    x + 2
  endgroup, begingroup
    x + 3
  endgroup, begingroup
    x + 4
enddef;

for i := returnMultipleValues(3):
    show(i);
endfor;

Another approach which allows you to generate the values in the same group is based on Sergey Slyusarev's answer. The advantage here is that you do not have to remember to use scantokens with the result because that is already handled in the vardef:

 vardef returnMultipleValues (expr x) =
    save y, s;
    string s;
    numeric y[];
    y1 := x + 1;
    y2 := x + 2;
    y3 := x + 3;
    y4 := x + 4;
    s := decimal(y1) & "," & decimal(y2) & "," & decimal(y3) & "," & decimal(y4);
    expandafter
  endgroup
  expandafter gobbled expandafter true scantokens(s) gobble begingroup
enddef;

for i := returnMultipleValues(3):
    show(i);
endfor;

EDIT: I found a way to automate the first version and solving the grouping problem: Just use

def multireturn(expr first)(text values) =
  first for v = values: endgroup, begingroup v endfor
enddef;

vardef returnMultipleValues (expr x) =
  save y, s;
  numeric y[];
  y1 := x + 1;
  y2 := x + 2;
  y3 := x + 3;
  y4 := x + 4;
  multireturn(y1,y2,y3,y4)
enddef;

for i := returnMultipleValues(3):
    show(i);
endfor;

This works because for expands the list at the beginning, afterwards v is a capsule so it no longer is affected by groups.

  • Cool! The only thing (not directly related to the original question though) is that (expr first) in multireturn seems to rule out its use for suffixes (which can be quite handy), but it's possible to make multireturn accept single text argument, like this: def multireturn(text values) = save tmp; tmp := 0; for v = values: if (tmp > 0): endgroup, begingroup fi save i; tmp := 1; v endfor enddef; – Sergey Slyusarev Sep 1 '18 at 22:47
  • @SergeySlyusarev I am not quite sure how you want to return a suffix in a verdef, but wouldn't that require replacing for with forsuffixes anyway? In any case, I think save i should be save tmp. – Marcel Krüger Sep 1 '18 at 23:15
  • You're right, that would require an extra argument: def multireturn(expr isSuffixes)(text values) = save isFirst; boolean isFirst; isFirst := true; if (isSuffixes): forsuffixes v = values: if (not isFirst): endgroup, begingroup fi save isFirst; boolean isFirst; isFirst := false; v endfor else: for v = values: if (not isFirst): endgroup, begingroup fi save isFirst; boolean isFirst; isFirst := false; v endfor fi enddef; and maybe isn't worth it. – Sergey Slyusarev Sep 2 '18 at 8:23
1

One way you could do this, is to convert numerical values to strings, concatenate them and return as a single string. Then you can get the values back using scantokens

vardef returnMultipleValues (expr x) =
    save y;
    numeric y[];
    y1 := x + 1;
    y2 := x + 2;
    y3 := x + 3;
    y4 := x + 4;
    decimal(y1) & "," & decimal(y2) & "," & decimal(y3) & "," & decimal(y4)
enddef;

string something;

something = returnMultipleValues(3);

for i := scantokens(something):
    show(i);
endfor;

Similarly you can use paths for the same thing:

vardef returnMultipleValues (expr x) =
    save y;
    numeric y[];
    y1 := x + 1;
    y2 := x + 2;
    y3 := x + 3;
    y4 := x + 4;
    (x, y1) -- (x, y2) -- (x, y3) -- (x, y4)
enddef;

path something;

something = returnMultipleValues(3);

for i := 0 step 1 until length(something):
    show(ypart(point i of something));
endfor;

Or, if the number of values you want to return is limited to 2 or 3, as @thruston suggested, you could use a pair or a color.

  • Thanks Sergey This may be a way to go. But now I am just trying to write a program using vardefs which return numericals or pairs. – Peteris Daugulis Sep 1 '18 at 21:11
  • Paths are quite natural way to store pairs. And you can incorporate pairs into strings as well, and even mix different types like this "(" & decimal(y1) & "," & decimal(y2) & ") , " & decimal(y3) & "," & decimal(y4). But i agree with @egreg more details on what exactly you want to achieve would help a lot. – Sergey Slyusarev Sep 1 '18 at 22:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.