Although I solved the following problem using MapThread
, I'm curious about why Thread
does not work as I expect it to here.
Given two lists of length n, where the first list is a list of lists of lists and the second is a list of atomic expressions, such as:
list1 = {{{1, 2}, {3, 4, 5}}, {{1, 3, 5}, {9, 8}}};
list2 = {"a", "b"};
and where list1[[n]]
can contain 1 or more lists of atomic expressions. I would like to create a single list that places the nth element of the second list at the end of every sublist of list1[[n]]
, producing:
{{{1, 2, "a"}, {3, 4, 5, "a"}}, {{1, 3, 5, "b"}, {9, 8, "b"}}}
I first tried the following
appendTo[ll_, item_] := Map[Append[#, item] &, ll];
Thread[appendTo[list1, list2]]
But this returns:
{{{1, 2}, {1, 3, 5}}, {{3, 4, 5}, {9, 8}}, {{"a", "b"}, {"a", "b"}}}
even though threading an undefined function seems to produce the right expressions:
Thread[f[a, b]]
{f[{{1, 2}, {3, 4, 5}}, "a"], f[{{1, 3, 5}, {9, 8}}, "b"]}
MapThread
works fine:
MapThread[appendTo, {list1, list2}]
{{{1, 2, "a"}, {3, 4, 5, "a"}}, {{1, 3, 5, "b"}, {9, 8, "b"}}}
So why is Thread
producing the behavior shown above instead of giving the same result as MapThread
?
Threading Rule
on the same lists also produces what I expect:
Thread[Rule[list1, list2]]
{{{1, 2}, {3, 4, 5}} -> "a", {{1, 3, 5}, {9, 8}} -> "b"}
Answer
The simple answer: Thread
does not have the attribute HoldAll
, so its contents are executed before it processes them.
But, it is not quite that simple. If you add HoldAll
, you still don't get what you want
Internal`InheritedBlock[{Thread},
SetAttributes[Thread, HoldAll];
Thread[appendTo[list1, list2]]
]
(* {{{1, 2}, {3, 4, 5}, {"a", "b"}}, {{1, 3, 5}, {9, 8}, {"a", "b"}}} *)
which implies that in this form only the first list is threaded over.
Note, I used Internal`InheritedBlock
to retain the behavior of Thread
while not allowing my modifications to leak.
Comments
Post a Comment