1
$\begingroup$

I have a nested list:

list = {1, 2, {3, 4}, f[a], {2, h[b]}}

I would like to apply a function g to all elements of the nested list (starting from top to bottom) that are not lists themselves. That is, I would like to obtain:

{g[1], g[2], {g[3], g[4]},  g[f[a]], {g[2], g[h[b]]}}

(Using Map[g, list, {-1}] does not work, as it maps g inside f and h)

The alternative I have ended up using is the following function:

mapAtLeavesOfList[g_, x_List] := Map[mapAtLeavesOfList[g, #] &, x]
mapAtLeavesOfList[g_, x_] := g[x]

mapAtLeavesOfList[g, list]
=> {g[1], g[2], {g[3], g[4]}, g[f[a]], {g[2], g[h[b]]}}

Any better suggestions?

$\endgroup$
1
  • $\begingroup$ This feels like a duplicate to me. Does anyone know what I may be remembering? $\endgroup$
    – Mr.Wizard
    Commented Jan 5, 2013 at 4:07

2 Answers 2

6
$\begingroup$

Contrary to what the title claims, your example shows that do not want to map at the "maximum depth" of the list, but rather, merely onto the elements of a List that are not lists themselves. I think you're over complicating things with your definition of mapAtLeavesOfList.

The solution is as simple as:

Clear@g
g[a_List] := g /@ a

g@list
(* {g[1], g[2], {g[3], g[4]}, g[f[a]], {g[2], g[h[b]]}} *)

If you want to use g as a blackbox function, the following should work:

Block[{mapg},
    mapg[a_List] := mapg /@ a;
    mapg@list /. mapg -> g
]

or even:

Block[{g},
    g[a_List] := g /@ a;
    g@list
]

The above solution temporarily modifies g to make it listable using Block and once outside the Block, the original definition of g kicks in.

You can also set the Listable attribute for g as in Leonid's answer.

$\endgroup$
4
  • $\begingroup$ In the spirit of your last suggestion, one could also do something like Block[{g}, g[list] //. x_g :> Thread[x]] $\endgroup$ Commented Jan 5, 2013 at 1:19
  • $\begingroup$ Thanks. I have changed the title to better reflect the question. $\endgroup$
    – ecoxlinux
    Commented Jan 5, 2013 at 1:28
  • $\begingroup$ @ecoxlinux But the title still misses the point - what you ask is not mapping on leaves, you rather ask to thread over the lists top to bottom until non-lists are encountered. $\endgroup$ Commented Jan 5, 2013 at 1:34
  • $\begingroup$ Thanks to both of you (leonid and hypnotoad). Hypnotoad, I was indeed overcomplicating the issue. $\endgroup$
    – ecoxlinux
    Commented Jan 5, 2013 at 2:49
5
$\begingroup$

A few other alternatives: either you can make g itself a Listable function by executing SetAttributes[g,Listable] (assuming that g is a symbol), or you can do something like this:

Function[Null, g[#], Listable][list]

where I don't make any assumptions on g (which may be a symbol but may be something else).

Note that there are subtle differences between setting g Listable and defining g[a_List] := g /@ a. The latter method is less general in two respects. First, you may not be able to add a new definition, for whatever reason. Second, this won't work if g carries Hold - attributes (HoldAll or HoldFirst) - because they will prevent the pattern g[a_List] from matching, if you pass a variable such as list. The methods based on Listable attribute are free from this particular limitation.

$\endgroup$
1
  • $\begingroup$ @Rojo Yes, you are right. Will edit. $\endgroup$ Commented Jan 5, 2013 at 21:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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