Update:
System`Private`ConstructNoEntry
will enter recursion in some cases, See here. System`Private`SetNoEntry
is more robust and simple.
Also Association
is a special construct, normal replacement does not work for it. Alexey Popkov provides a perfect workaround in his comments. All credits to Alexey Popkov.
I just organize some examples for quick reference
Below is the final form of makeAtomic
makeAtomic[head_] :=
a_head :> RuleCondition@System`Private`SetNoEntry[a]
Now, no matter List or Association expression both works
expr = {0, Quantity[1, "kg"]};
Map[g, expr /. makeAtomic[Quantity], {-1}]
Map[g, <|1 -> expr|> /. makeAtomic[Quantity], {-1}]
gives
{g[0], g[Quantity[1, "Kilograms"]]}
<|1 -> {g[0], g[Quantity[1, "Kilograms"]]}|>
Previous:
Completely inspired by Alexey Popkov. I just make a simpler interface.
We could define
makeAtomic[head_] :=
HoldPattern[head[args___]] :>
System`Private`ConstructNoEntry[head, args]
Now define
expr = {0, Quantity[1, "kg"], BesselJ[3, x]};
then
Map[g, expr, {-1}]
gives
{g[0], Quantity[g[1], g["Kilograms"]], BesselJ[g[3], g[x]]}
and
Map[g, expr /. {makeAtomic[Quantity], makeAtomic[BesselJ]}, {-1}]
gives
{g[0], g[Quantity[1, "Kilograms"]], g[BesselJ[3, x]]}
What is more, we can really make an atomic Head like
atomicHead[x___] := System`Private`ConstructNoEntry[atomicHeadCore, x]
then
Map[g, {0, atomicHead[1, 2, 3]}, {-1}]
gives
{g[0], g[atomicHeadCore[1, 2, 3]]}
Note the Head is atomicHeadCore
not atomicHead
.
If you want to change to some other Head, you can do
Map[g, {0, atomicHead[1, 2, 3]}, {-1}] /.
atomicHeadCore[x___] :> anyOtherHead[x]
which gives
{g[0], g[anyOtherHead[1, 2, 3]]}
Compress
? :D $\endgroup$Compress
in this case? Actually, if I have anatomicHead
. What I want to do is temporarily replace some Head withatomicHead
, and doMap
, finally replace the Heads back. $\endgroup$Compress
doesn't help. What's in my mind is something likeMap[g, {0, {Compress@atomicHead[1, 2, 3]}}, {-1}] /. str_String :> Uncompress@str
As you can see, it's not even comparable with Alexey Popkov's solution below. $\endgroup$Compress
solution can be improved as an alternative solution. I foundCompress
always returns string begin with "1:eJ"(though I not that sure, because the documentation does not say this) which is very special. So we can do thisMap[g, {0, {h[1, 2, 3]}} /. h -> Compress@*h, {-1}] /. str_String?(StringMatchQ["1:eJ" ~~ __]) :> Uncompress[str]
. It is not as 100 percent robust as Popkov's solution, but I think it also very good as long as you do not really have "1:eJ" string as normal data. $\endgroup$