You can use ReplaceRepeated
with a refined set of rules, e.g.:
l //. {
{a___, b_, c__, b_, e___} :> {a, b, {c}, b, e} /; c == b + 1,
{a___, b_, c_, d___, c_, b_, e___} :> {a, b, {c, d, c}, b, e} /; c == b + 1
}
{1, 1, 1, {2, 2, {3, 3}, 2, 2, {3}, 2}, 1, 1, {2, 2, {3}, 2}, 1}
Actually, I am not sure if the rules treat all cases correctly. Personally, I would prefer the following parsing approach: A difference of 1
means open brace; a difference of -1
means closing brace. Spreading in an appropriate amount of commata converting to strings in order to fuse it, this could look like this:
f[l_] := ToExpression[
StringJoin[
"{",
Riffle[
IntegerString /@ l,
Differences[l] /. {0 -> ",", 1 -> ",{", -1 -> "},"}
],
"}"
]
];
This is also much more efficient, since the list has to be parsed only once and we save quite a bunch of copy operations. Let's compare the two approaches: Here, a function for applying the rules above and a random list generator:
g[l_] := l //. rules;
rand[n_] := Module[{l},
l = Accumulate[RandomChoice[{0, -1, 1}, n]];
l = l - Min[l] + 1;
Join[Range[1, l[[1]]], l, Range[l[[-1]], 1, -1]]
]
And here the speed test:
l = rand[1000];
a = f[l]; // AbsoluteTiming // First
b = g[l]; // AbsoluteTiming // First
a == b
0.001347
191.374
True
Edit
Kuba inspired me to add some further functionality: getting rid of the requirement that jumps must be of size $\pm 1$. The approach is very similar to the one for f
, but I employ memoization to avoid too many ConstantArray
s. It turns out that this algorithm's performance is also rather independent of the total depth of the final list:
ClearAll[s];
SetAttributes[s, Listable];
s[i_] := s[i] = Switch[Sign[i],
1, StringJoin[",", ConstantArray["{", i]],
-1, StringJoin[ConstantArray["}", -i], ","],
0, ","
];
h[l_] := ToExpression@StringJoin["{",
Riffle[IntegerString[l], s[Differences[l]]],
"}"];
list = Join[{1}, RandomInteger[{1, 200}, 10000], {1}];
a = h[list]; // AbsoluteTiming
b = foo[list]; // AbsoluteTiming
a == b
{0.077537, Null}
{5.94479, Null}
True