Checking through an expression

I am writing a code that manipulates expressions that contains functions with an arbitrary number of arguments. An example expression is

expr = x f[a, b, c, d] - 4 f[b, a, d] + z f[] + f[a, b, d] - f[a]f[b,d]


What I would like to do is to make my code to be a little user-friendly by making it throw a message if the expression is invalid. Basically, the only symbols allowed to appear in f are allowedList={a,b,d}, and so I probably need to somehow use Scan to check through expr. (The above example should yield an 'error' since the first term has a c in it.)

To make answering easier, please assume that the input expr will be such that all arguments will have Head = Symbol. But please allow for the possibility that multiple fs could be multiplied together.

-

This function yields True if the expression expr is valid, otherwise it yields False:

validExpressionQ[ expr_, allowedList_List] :=
Union @ Cases[ Variables[expr], f_[x___] :> x, Infinity] == Union @ allowedList


now we have, e.g.

validExpressionQ[ x f[a, b, d] - 4 f[b, a, d] + z f[] + f[a, b, d] - f[a] f[b, d],
{a, b, d}]

True

validExpressionQ[ x f[a, b, c, d] - 4 f[b, a, d] + z f[] + f[a, b, d] - f[a] f[b, d],
{a, b, d}]

False

-
I guess it should work even if f has DownValues –  belisarius Jan 29 at 13:19
not sure, BTW :) –  belisarius Jan 29 at 13:20
Very nice. In the right hand side, would it still work if I replaced Union @ allowedList with just allowedList? –  QuantumDot Jan 29 at 14:35
@QuantumDot No, Union sorts the list, it might be replaced with Sort but then it doesn't delete duplicates. –  Artes Jan 29 at 20:38

A possibility is to define a support function:

EDIT: Thanks to Simon Woods' comment: .. instead of __

check[(a | b | d) ..] := 1;
check[___] = Indeterminate;


The substitution

expr /. f-> check


contains Indeterminate if f has arguments not allowed.

Concluding:

If[FreeQ[expr /. f -> check, Indeterminate], "ok", "error"]


returns "error" for your example and "ok" if the arguments are acceptable.

-
check[a, b] returns Indeterminate. Perhaps you intended to use RepeatedNull rather than BlankSequence ? –  Simon Woods Jan 29 at 21:30
I just posted something very similar before reading this answer. +1! Nevertheless I think my code has advantages. –  Mr.Wizard Jan 30 at 15:52

Since it seems that evaluation of the expression is specifically permitted let's leverage that:

check[expr_, f_, pat_] := FreeQ[expr /. f[pat ...] -> 1, _f]

check[expr, f, a | b | d]
check[expr, f, a | b | c | d]

False

True


I used Alternatives instead of List to make this cleaner, but you can always Apply Alternatives if needed.

If for some reason you don't like the method above, here is my take on Artes's method:

check2[expr_, f_, args_] :=
{} === Complement[Cases[expr, f[x___] :> x, -2], args]

check2[expr, f, {a, b, d}]
check2[expr, f, {a, b, c, d}]

False

True

-