11
$\begingroup$

Consider the following case:

list1 = {q, r, s};
list2 = {{a, 2}, {b, 3}, {c, 1, f}};
Inner[{#1, #2} &, list1, list2, List]
(* Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1, f}}} *)

However, if the last element of list2 has Length 2:

list1 = {q, r, s};
list2 = {{a, 2}, {b, 3}, {c, 1}};
Inner[{#1, #2} &, list1, list2, List]
(* Out[] := {{{q, a}, {r, b}, {s, c}}, {{q, 2}, {r, 3}, {s, 1}}} *)

I would have expected (and wanted) a result like:

(* Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}} *)

So,

  1. Why does this Inner operation change if the list2 entries are all of the same length?
  2. How can I generate an output like the expected result?

Edit I came up with a potential answer to question 2:

Partition[Riffle[list1, list2], 2]
Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}}

but this seems somehow too-complicated.

$\endgroup$
  • $\begingroup$ Inner[] in your second case is just being consistent with the behavior of Dot[]; contrast with {q, r, s}.{{a, 2}, {b, 3}, {c, 1}}. $\endgroup$ – J. M. will be back soon Aug 8 '12 at 16:08
9
$\begingroup$

Inner is a generalization of Dot, and Dot has specific behavior for vectors, matrices and tensors:

Mathematica graphics

{a, b, c} . {x[1, 2], x[3, 4], x[5, 6]}

{a, b, c} . {{1, 2}, {3, 4}, {5, 6}}
a x[1, 2] + b x[3, 4] + c x[5, 6]

{a + 3 b + 5 c, 2 a + 4 b + 6 c}

When Inner detects a complete tensor it changes behavior in a similar fashion.
Likewise the second and third arguments of Inner are order-dependent like the arguments of Dot.

Just as x was used in Dot above, one can use an arbitrary head to "shield" the tensor depth from Inner. Unevaluated is a nice choice in this case because it will be automatically stripped by &.

list1 = {q, r, s};
list2 = {{a, 2}, {b, 3}, {c, 1}};

Inner[{#1, #2} &, list1, Unevaluated /@ list2, List]
{{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}}

Another option would be:

Inner[{#, #2[[1]]} &, list1, "" /@ list2, List]

As already mentioned by J. M., for #2:

{list1, list2}\[Transpose]
{{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}}
$\endgroup$
  • $\begingroup$ +1. It's probably a good idea to use MapAt with Unevaluated since you only need one modification for it to stop being a full array $\endgroup$ – Rojo Aug 9 '12 at 0:29
  • $\begingroup$ @Rojo that could work but Unevaluated is not a general solution (for example with List in place of {#1, #2} & which does not evaluate) so I think the second method may be more appropriate, and doing that for only one element would complicate things. (thanks for the vote.) $\endgroup$ – Mr.Wizard Aug 9 '12 at 3:18
  • $\begingroup$ I see your point. Perhaps this as ugly as it is could be more general Inner[List, list1, #, List] &[ MapAt[Sequence, Unevaluated[#] &[list2], {1, 1}]]. More general is still not general. It wouldn't work with HoldComplete instead of the first List $\endgroup$ – Rojo Aug 9 '12 at 3:32
4
$\begingroup$

I came up with a better answer to the second part of my question:

Thread[{list1, list2}]
(* Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}} *)

which works equally well if the entries in the lists are of different lengths.

$\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.