Methods revisited
ybeltukov posted a cleaner version of t7
that made me feel rather silly. (Thanks ybeltukov; it will teach me to be more careful about clearing definitions while experimenting!) I can't beat it, so instead I'll try to refine it. First, several of my functions and his b1
do not return False
on a failure to match, so this should be corrected. Second, one should incorporate extension of $IterationLimit
into the function. It would then look something like this:
t8[linked_, x_] :=
Module[{f},
Block[{$IterationLimit = ∞},
f[{x, R_}] = True;
f[{}] = False;
f[{L_, R_}] := f[R];
f @ linked
]]
t8[ll, {86, 99}]
True
A crash with most methods
I discovered that on longer linked lists all the methods suggested so far cause a kernel crash. ybeltukov confirmed that this problem also affects version 9.0.1.
Examples:
SeedRandom[1]
RandomInteger[99, {500000, 2}];
ll = Fold[{#2, #} &, {}, %];
MemberQ[ll, {86, 99}] (* kernel crash *)
Cases[ll, {86, 99}, -1, 1] (* kernel crash *)
ll /. {86, 99} :> Return[True] (* kernel crash *)
One way around this problem is to manage a stack manually as Daniel did here.
t9[linked_, pat_] :=
Module[{R = linked, L},
While[R =!= {},
{L, R} = R;
If[MatchQ[L, pat], Return @ True];
];
False
]
Now:
SeedRandom[1]
RandomInteger[99, {500000, 2}];
ll = Fold[{#2, #} &, {}, %];
t9[ll, {86, 99}]
True
This is not as fast as b1
/t8
however.
Original answer
If you did not have MemberQ
you could still walk the tree recursively. Here are several ways to do that:
t1[x_] := MatchQ[#, {x, _} | {_, _?#0}] &
t2[x_][{L_, R_}] := MatchQ[L, x] || t2[x][R]
t3[x_] := Module[{f}, f[{L_, R_}] := MatchQ[L, x] || f[R]; f]
t4[x_] := MatchQ[#[[1]], x] || #0 @ #[[2]] &
All functions have the syntax: tfunc[pattern][linkedlist]
.
Sometimes these are even faster than MemberQ
:
SetAttributes[timeAvg, HoldFirst]
timeAvg[func_] := Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}]
SeedRandom[1]
RandomInteger[99, {50000, 2}];
ll = Fold[{#2, #} &, {}, %];
MemberQ[ll, {86, 99}, Infinity] // timeAvg
0.01148
Block[{$RecursionLimit = 1*^6},
timeAvg @ #[{86, 99}][ll] & /@ {t1, t2, t3, t4}
]
{0.01812, 0.007112, 0.00612, 0.008736}
More experiments
Using the syntax tfunc[pat, list]
is somewhat faster, i.e. this is faster than t2
:
t5[x_, {L_, R_}] := MatchQ[L, x] || t5[x, R]
A bit faster still is shifting this to a form that is iterative:
t6[x_, {L_, R_}] /; MatchQ[L, x] = True;
t6[x_, {L_, R_}] := t6[x, R]
The fastest I found so far is combining this iterative form with the dedicated function a la t3
:
t7[x_] :=
Module[{f},
f[{L_, R_}] /; MatchQ[L, x] = True;
f[{L_, R_}] := f[R];
f
]
Timings for these three variations:
Block[{$RecursionLimit = 1*^6, $IterationLimit = 1*^6},
{
t5[{86, 99}, ll] // timeAvg,
t6[{86, 99}, ll] // timeAvg,
t7[{86, 99}][ll] // timeAvg
}
]
{0.00624, 0.005864, 0.005368}
I find it fairly impressive that t7
is twice is fast as MemberQ
in this application.
l
in the original definition oftestList
? $\endgroup$l
andtestList
are the same. $\endgroup$List
for the linked list to avoid the flattening issue $\endgroup$