I'm pretty sure this is a duplicate but I spent 15 minutes looking for it and couldn't find it, so I'm just going to answer for now.
Instead of using Listable
you can manually map over non-vector lists:
f[v_?VectorQ] := oper[v]
f[ls_List] := f /@ ls;
f[{{1, 2}, {3, 4}, {5, 6, 7}}]
{oper[{1, 2}], oper[{3, 4}], oper[{5, 6, 7}]}
Extension
In an attempt to make this answer more unique, consider the advanced behavior of Listable
functions wherein non-list arguments are distributed:
SetAttributes[ff, Listable]
ff["x", {1, 2, 3}, {4, 5, 6}]
{ff["x", 1, 4], ff["x", 2, 5], ff["x", 3, 6]}
We may wish to have this behavior extended, in a fashion at least, to our "vector atoms" as used above. The simplest method I can think of is to use Thread
, which has the same behavior as Listable
though only at level one, and guard the vectors form recognition and threading. Here an unoptimized first pass at implementation:
g[a___] /; MemberQ[{a}, x_List /; ! VectorQ[x]] :=
Module[{h},
g @@@ Replace[Thread@Replace[Hold[a], v_?VectorQ :> h[v], {1}], h@x_ :> x, {2}]
]
Now:
g[1, {2, 3}, {{{4}}, {5, 6}, 7}]
{{g[1, {2, 3}, {4}]}, g[1, {2, 3}, {5, 6}], g[1, {2, 3}, 7]}
Note that the vector {2, 3}
is distributed as 1
is, while the nested lists are threaded. When {4}
is encountered it is also treated as atomic, but its surrounding List
is transferred to the output: {g[1, {2, 3}, {4}]}
.
fromDig2[ls_List] := 10^Range[Length@# - 1, 0, -1].# & /@ ls; fromDig2@{{1, 2}, {3, 4}, {5, 6}}
? (orFromDigits /@ {{1, 2}, {3, 4}, {5, 6}}
:) ) $\endgroup$fromDig3[ls__] := 10^Range[Length@{ls} - 1, 0, -1].{ls}; fromDig3 @@@ {{1, 2}, {3, 4}, {5, 6}}
$\endgroup$