11
$\begingroup$

For example,

Map[g, {0, {h[1, 2, 3]}}, {-1}]

gives

{g[0], {h[g[1], g[2], g[3]]}}

I want a atomicHead that can prevent deeper inspection of its sub parts, so that

Map[g, {0, {atomicHead[1, 2, 3]}}, {-1}]

gives

{g[0], {g[atomicHead[1, 2, 3]]}}

Is it possible to have such an atomicHead? Or even better, to make Head h seem atomic to Map function.

$\endgroup$
4
  • $\begingroup$ Use Compress? :D $\endgroup$
    – xzczd
    Jun 26 at 12:13
  • $\begingroup$ @xzczd How to use Compress in this case? Actually, if I have an atomicHead. What I want to do is temporarily replace some Head with atomicHead, and do Map, finally replace the Heads back. $\endgroup$
    – matheorem
    Jun 26 at 12:26
  • $\begingroup$ Hmm… Then Compress doesn't help. What's in my mind is something like Map[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$
    – xzczd
    Jun 26 at 13:02
  • 1
    $\begingroup$ @xzczd I think your Compress solution can be improved as an alternative solution. I found Compress 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 this Map[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$
    – matheorem
    Jun 26 at 13:46

2 Answers 2

13
$\begingroup$

Following this tutorial,

tmp = atomicHead[1, 2, 3];
System`Private`SetNoEntry@tmp
Map[g, {0, {tmp}}, {-1}]
{g[0], {g[atomicHead[1, 2, 3]]}}

Update in response to comments

Alternatively, you can automatically make expressions atomic based on their Heads:

expr = {0, {atomicHead[1, 2, 3], {atomicHead[1, 2, 3, 4]}}};
Map[g, expr /. 
  atomicHead[args___] :> System`Private`ConstructNoEntry[atomicHead, args], {-1}]
{g[0], {g[atomicHead[1, 2, 3]], {g[atomicHead[1, 2, 3, 4]]}}}

Update 2

I just realized that I overlooked the simplest and most direct solution:

expr = {0, {atomicHead[1, 2, 3], {atomicHead[1, 2, 3, 4]}}};
Map[g, expr /. a_atomicHead :> System`Private`SetNoEntry[a], {-1}]
{g[0], {g[atomicHead[1, 2, 3]], {g[atomicHead[1, 2, 3, 4]]}}}
$\endgroup$
10
  • $\begingroup$ Hi, Alexey Popkov, Thank you for answering. I found this solution before. But I do not want change whole expression to a tmp, then make it atomic. As I replied to xzczd, I want to do Head replacement $\endgroup$
    – matheorem
    Jun 26 at 12:41
  • $\begingroup$ @matheorem I updated the answer with an alternative solution. $\endgroup$ Jun 26 at 13:21
  • $\begingroup$ Oh, Yeah! That is great. Thank you very much for your clever solution : ) $\endgroup$
    – matheorem
    Jun 26 at 13:28
  • $\begingroup$ One more thing. Why can not this atomicHead be replaced directly? I mean Map[g, expr /. atomicHead[args___] :> SystemPrivateConstructNoEntry[atomicHead, args], {-1}]/.atomicHead->anyOtherHead $\endgroup$
    – matheorem
    Jun 26 at 14:01
  • 1
    $\begingroup$ @matheorem Here is a solution: expr={0,{Quantity[1,"kg"]}};Map[g,<|1->expr|>/. a_Quantity:>RuleCondition@System`Private`SetNoEntry[a],{-1}]. $\endgroup$ Jun 27 at 1:42
6
$\begingroup$

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]]}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.