Mathematica, 52 4747 45 bytes
±___=!(±1=1>0)
a__±b__/;{a}=={b};a!==b!||{a}==-{b}:=±a
This will also throw a few warnings which can be ignored.
There might be away to shorten the somewhat annoying {a}=={b}!==b!||{a}==-{b}
part, but I'm not finding anything right now. Keywords like SubsetQ
and MatrixRank
are simply too long. :/
±___=False;
±1=True;
a__±b__/;{a}=={b};a!==b!||{a}==-{b}:=±a
PlusMinus[___]=False;
PlusMinus[1]=True;
PlusMinus[a__,b__]/;;a!a====b!b||||{a}==-{b}:=PlusMinus[a]
Finally, the third definition applies only to lists that can be decomposed into X ++ X
or X ++ -X
, and recursively uses the result for X
. The definition is limited to these lists by ensuring they can be split into subsequences a
and b
with a__±b__
and then attaching the condition (/;
) that either {a}=={b}
or {a}==-{b}
. Defining PlusMinus
as a variadic function in this weird way via an operator saves a whopping 5 bytes over defining a unary operator ±
on lists.
But wait, there's more. We're using a!==b!
instead of {a}=={b}
. Clearly, we're doing this because it's two bytes shorter, but the interesting question is why does it work. As I've explained above, all operators are just syntactic sugar for some expression with a head. {a}
is List[a]
. But a
is a sequence (like I said, sort of like a splat in other languages) so if a
is 1,-1,1
then we get List[1,-1,1]
. Now postfix !
is Factorial
. So here, we'd get Factorial[1,-1,1]
. But Factorial
doesn't know what to do when it has a different number of arguments than one, so this simply remains unevaluated. ==
doesn't really care if the thing on both sides are lists, it just compares the expressions, and if they are equal it gives True
(in this case, it won't actually give False
if they aren't, but patterns don't match if the condition returns anything other than True
). So that means, the equality check still works if there are at least two elements in the lists. What if there's only one? If a
is 1
then a!
is still 1
. If a
is -1
then a!
gives ComplexInfinity
. Now, comparing 1
to itself still works fine of course. But ComplexInfinity == ComplexInfinity
remains unevaluated, and doesn't give true even though a == -1 == b
. Luckily, this doesn't matter, because the only situation this shows up in is PlusMinus[-1, -1]
which isn't a valid OVSF anyway! (If the condition did return True
, the recursive call would report False
after all, so it doesn't matter that the check doesn't work out.) We can't use the same trick for {a}==-{b}
because the -
wouldn't thread over Factorial
, it only threads over List
.