1
$\begingroup$

I've been having an issue with functions which are supposed to receive as input a list of tuples. My troubles are two-fold:

  1. The function doesn't properly identify the pattern test I laid out in the definition of the inputs.
  2. The pattern matching itself is not ideal for a list of (same length) tuples and I couldn't find a more suitable pattern to match for.

My issue was originally in a very convoluted function, but I managed to replicate it in a simpler form.

g[x_, {a_, b_}] := a x^b (*generic function - actual definition of G is irrelevant other than the input structure*)
SetAttributes[myfun, HoldFirst]
myfun[f_[args__, pars:{__}], x_ : {{__} ..}] := f[Sequence @@ #, pars] & /@ x

First I check to see if my pattern matches what I want it to do (more or less as it still doesn't properly identify tuples of different lengths).

In[58]:= MatchQ[Tuples[Range@10,1],{{__}..}]
MatchQ[Range@10,{{__}..}]
MatchQ[x,{{__}..}]

Out[58]= True

Out[59]= False

Out[60]= False

Note that a simple Range doesn't trigger the pattern match and neither does a generic symbol x (The symbol returns a weird output which I just noticed after posting this question - the function is not applied and yet the symbol is returned). Despite this:

In[68]:= myfun[g[x, {a, b}], Range[2, 4]] (*Regular range*)
myfun[g[x, {a, b}],Tuples[ Range[2, 4],1]] (*Properly formatted tuples*)
myfun[g[x,{a,b}],x] (*Symbol (!) - still works*)

Out[68]= {2^b a,3^b a,4^b a}

Out[69]= {2^b a,3^b a,4^b a}

Out[70]= x

My only conclusion is that for some reason the function does not evaluate the condition I set. Were I a betting man, my money is likely on problematic syntax. Also, if there is a better way to match a list of tuples I would appreciate the insight.

Help is greatly appreciated!

$\endgroup$
4
  • 4
    $\begingroup$ If you want to match a list of lists that are all the same length, why not just use x_List?MatrixQ? $\endgroup$ Dec 29, 2020 at 12:49
  • $\begingroup$ Thank you for your reply, My understanding of pattern matching syntax in pure function input is a little lacking and not well formed. Is there a good reference for it? I haven't found much by way of the documentation. $\endgroup$
    – Ben
    Dec 29, 2020 at 14:48
  • 1
    $\begingroup$ I'd recommend starting here. The ? syntax (PatternTest) is explained a little further down. $\endgroup$ Dec 29, 2020 at 18:29
  • $\begingroup$ Your suggestion was very helpful! Thank you for the reference as well, I will make sure to read further into this subject. $\endgroup$
    – Ben
    Dec 30, 2020 at 0:26

2 Answers 2

1
$\begingroup$

I figured out one of my issues on my own and figured I would update this question in case others happen upon it.

I had a syntactic error in the definition of my function.

Instead of

myfun[f_[args__, pars:{__}], x_ : {{__} ..}]

I should have written

myfun[f_[args__, pars:{__}], x : {{__} ..}]

Note the underscore near the x input. I still don't have a better way to identify tuples.

$\endgroup$
1
  • 1
    $\begingroup$ So does x : {{_, _}..} do what you want better? $\endgroup$ May 23, 2022 at 16:09
1
$\begingroup$

This expression

x_ : {{__} ..}

is actually an Optional. It's a way to give a default value to an argument. We can demonstrate what it does with this:

myfun[g[x, {a, b}]]
(* {{a*__^b}} *)

So, it's equivalent to this:

myfun[g[x, {a, b}], {{__} ..}]

Now, since you always passed in a second argument, it's not the default value that is causing problems. It's just that you actually didn't provide a constraint on that second argument in terms of pattern matching. The only constraint that the argument is required to match is x_ (the bit before the colon). So anything is allowed to match. It's perhaps unfortunate that the colon is used for both Pattern and Optional, because it leads to these kinds of masking problems.

As you already discovered, the solution is to just remove the underscore after the x, which results in the colon being parsed as Pattern rather than Optional.

While it's not directly related to your question, and so maybe not applicable to your real computations (I know g was just a dummy example), I feel compelled to comment on your design. If we look at this:

myfun[f_[args__, pars : {__}], x : {{__} ..}] := f[Sequence @@ #, pars] & /@ x

we notice that args is never referenced on the right hand side. There are times when you want to do this, just match a pattern but not use the actual values, and in those situations you don't have to give the pattern a name:

    myfun[f_[__, pars : {__}], x : {{__} ..}] := f[Sequence @@ #, pars] & /@ x

However, in this case, that seems inadvisable. The reason why you needed to set the HoldFirst attribute was so that g[x, {a, b}] didn't get evaluated, which it otherwise would since it matches that pattern for g. In other words, you're manually constructing a g-headed expression that would normally evaluate directly as an argument to myfun. Consequently, you must not be retrieving any g-headed expressions elsewhere and passing them to myfun, because then they'd be evaluated before myfun and myfun wouldn't work properly. So, you're manually creating dummy values for g's first argument (that you will throw away) just to match a pattern. That's not a reason to use unnamed patterns.

A better strategy would look something like this:

myfunAlt[func_, pars : {__}, x : {{__} ..}] := func[Sequence @@ #, pars] & /@ x

Now, there are improvements we could make to that, but since we don't know your "real" g or anything about your context, I won't speculate on next steps.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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