Determine whether some expression contains a given symbol

Try FreeQ

FreeQ[x^2, t]
(*True*)
FreeQ[x^2, x]
(*False*)

ruebenko provides the built-in function, but supposing FreeQ was not provided we can find another way. You are on the right track to use ReplaceAll, but it would be much better to give a result as soon as the search pattern is found.

freeQ[expr_, pat_] :=
 Catch[expr /. pat :> RuleCondition@Throw[False, "freeQ"]; True, "freeQ"]

RuleCondition (1)(2)(3) is needed in the case that expr is Hold[ ] or the Throw will never evaluate.

Creating a massive expression (~1GB) and doing Timings (each in a separate session to circumvent caching) shows that this is reasonably fast.

big = Expand[(1 + x + y)^200 (2 - q)^150];

! FreeQ[big, q] // Timing

{1.03, True}

! freeQ[big, q] // Timing

{1.311, True}

Block[{q, s}, (big /. q -> s) =!= big] // Timing

{6.739, True}


Another approach that is cleaner, but perhaps less didactic, that will also return as soon as the pattern is found uses Cases:

freeQ2[expr_, pat_] :=
   {} === Cases[expr, pat, {0, -1}, 1, Heads -> True]

! freeQ2[big, q] // Timing

{0.983, True}

An important difference is that ReplaceAll (short form /.) will scan from the outside in while Cases, like most Mathematica functions, will scan inside out. See:

  • How to perform a depth-first preorder traversal of an expression?

And more examples:

  • ReplaceRepeated seemingly omits some rules
  • How to remove redundant {} from a nested list of lists?
  • Using ReplaceAll to replace a head
  • why Level does not sort output according to their levels
  • How to ReplaceAll independent of depth?
  • Is Replace doing double work?