7
$\begingroup$

Suppose I want to define a function whose argument must be a List of only Strings and List must have at least one member. I know how to require that the Head must be a List but how does one qualify List members within an argument? I really don't want to resort pattern matching inside the body of my definitions or trailing a conditional pattern outside my definitions.

This is what I have so far along with a few test cases...

fn[arg_List && AllTrue[arg, StringQ] && Length@arg > 0] := 
  Block[{}, arg];
fn[{"a", "b", "c"}] (* should return {"a","b","c"} *)
fn[{1, "b", "c"}]
fn[{}]

Unfortunately it produces no errors which in this case would have been very helpful.

$\endgroup$
4
  • 1
    $\begingroup$ does it work if you replace it with fn[arg_List /; (AllTrue[arg, StringQ] && Length@arg > 0)] := Module[{}, arg]; ? btw, why Block there? Why not use normal Module? $\endgroup$
    – Nasser
    Oct 28 at 8:11
  • $\begingroup$ @Nasser thank you that worked!!! i try not to use modules unless absolutely necessary because they require more memory and are slower than blocks. $\endgroup$ Oct 28 at 8:18
  • 1
    $\begingroup$ because they require more memory and are slower than blocks I did not know this. I use Modules all the time, since Block scoping is a little confusing to me. But if they work for you, that should work then. $\endgroup$
    – Nasser
    Oct 28 at 8:20
  • 2
    $\begingroup$ You could also do fn[arg_ /; VectorQ[arg, StringQ] && arg != {}] := ... $\endgroup$
    – Roman
    Oct 28 at 8:46
11
$\begingroup$

When imposing restrictions on patterns, there are typically two options:

  • Use Condition (/;) to specify an arbitrary expression as test. (This is what was shown in the comments)

    fn[arg_List /; (AllTrue[arg, StringQ] && Length@arg > 0)] := Module[{}, arg];
    fn[arg_ /; VectorQ[arg, StringQ] && arg != {}] := Module[{}, arg];
    

    This is most flexible, but can be significantly slower, since the pattern matcher has to ask the kernel to evaluate the condition

  • Use structural patterns: The pattern arg_List is mostly equivalent to arg:{___}, where Pattern (:) gives the name, and the {___} is "anything that looks like a list with zero or more arguments (see BlankNullSequence, ___). To require at least one element, use BlankSequence (__). You can also specify a head for these elements, e.g. __String. So the pattern here would be:

    fn[arg:{__String}] := Module[{}, arg];
    

    If each element should follow a more complex pattern, you can use Repeated (..) and RepeatedNull (...). So to require e.g. a list of lists of strings, use

    fn[arg:{{__String}...}] := Module[{}, arg];
    

    If the condition on the single elements is more complex, you can use PatternTest (?):

    fn[arg:{__String?(StringStartsQ["a"])}] := Module[{}, arg];
    

    This example requires a list of strings starting with a.

$\endgroup$
3
  • $\begingroup$ you really kicked ass on this answer. you dispel many mysteries behind conditional patterns for arguments that should have been better explained in the official documentation. $\endgroup$ Oct 28 at 11:24
  • $\begingroup$ if i wanted this fn[arg:{__String}] but wanted to require all strings have StringLength > 0 not sure how to add that. $\endgroup$ Nov 11 at 16:12
  • $\begingroup$ @JulesManson I would do it similar to the last example, so e.g. (StringLength[#]>0&) instead of (StringContainsQ["a"]) $\endgroup$
    – Lukas Lang
    Nov 11 at 23:11

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.