Here's a way of checking the heads "on the way down", and only mapping through expressions with head head
; I tried to account for Mathematica's usual level specification, but didn't manage to account for negative level specification (which proceeds from the innermost level).
(* Exact levels {n}: *)
headMap[f_, expr_, head_, {0}] := f[expr]
headMap[f_, expr_, head_, {level_Integer}] :=
Replace[expr, x_head :> Map[headMap[f, #, head, {level - 1}] &, x, {1}]] /; level > 0
(* Range of levels {n1, n2}: *)
headMap[f_, expr_, head_, {n1_Integer, n2 : (_Integer | Infinity)}] :=
Which[
n2 == 0, f[expr],
n1 == 0,
Construct[f, Replace[expr, x_head :> Map[headMap[f, #, head, {0, n2 - 1}] &, x, {1}]]],
True, Replace[expr, x_head :> Map[headMap[f, #, head, {n1, n2} - 1] &, x, {1}]]
] /; n2 >= n1 >= 0
(* Levels given by an integer n or Infinity, i.e. levels 1 through n *)
headMap[f_, expr_, head_, 0] := f[expr]
headMap[f_, expr_, head_, Infinity] := headMap[f, expr, head, {1, Infinity}]
headMap[f_, expr_, head_, level : _Integer] := headMap[f, expr, head, {1, level}] /; level > 0
Note: this could be generalized to accept patterns matching heads instead of explicit heads by using, say, x_ :> Map[...] /; MatchQ[Head[x], head]
everywhere instead.
And then we can define
ListMap[f_, expr_, level_] := headMap[f, expr, List, level]
Examples:
ListMap[{#, x} &, {a, b, c}, {1}]
(* Output: {{a, x}, {b, x}, {c, x}} *)
ListMap[{#, x} &, {a, 3 b, c}, Infinity]
(* Output: {{a, x}, {3 b, x}, {c, x}} *)
ListMap[f, {e, d, {a, {b, x}, g[c, {d, h}]}}, {3}]
(* Output: {e, d, {a, {f[b], f[x]}, g[c, {d, h}]}} *)
ListMap[f, {e, d, {a, {b, x}, g[c, {d, h}]}}, Infinity]
(* Output: {f[e], f[d], f[{f[a], f[{f[b], f[x]}], f[g[c, {d, h}]]}]} *)
ListMap[f, {e, d, {a, {b, x}, g[c, {d, h}]}}, {2, 3}]
(* Output: {e, d, {f[a], f[{f[b], f[x]}], f[g[c, {d, h}]]}} *)
As you can check, these are the analogues of what Map
does with the corresponding level specifications, but only ever "maps through" heads that are List
!
Replace[{a, 3 b, {c, d}, e}, x_List :> x + 7, {1}]
$\endgroup$ListMap
have to encounterList
s all the way down? For instance, in{c, f[{a}, b]}
, listmappingh
over all lists at level 3 naively would produce{c, f[{h[a]}, b]}
; but it would have to "go through" the headf
to get there. Should listmap be defined such that it can only "go through"List
heads when trying to reach the level it's seeking, or not? $\endgroup$