Group Abstract Group Abstract

Message Boards Message Boards

2
|
7.3K Views
|
11 Replies
|
16 Total Likes
View groups...
Share
Share this post:

How to Thread[f[x,{a,b,c}]] if x,a,b,c are lists themselves?

I am a Wolfram L core language learner (and always a beginner), this is my numbered problem/question no.1, thanks for your attention. The Thread function looks suitable for my task:

In[1]:= Thread[ f[x, { a,b,c }] ]
Out[1]:= {f[x,a], f[x,b], f[x,c]}

When all placeholders are lists, the negative result is not unexpected ("Thread::tdlen: Objects of unequal length in f[{1,2,3,4},{{0,9},{8,7},{6,5}}] cannot be combined."), since i've studied the documentation of the Thread-function and didn't understand everything.

In[2]:= Thread[ f[{1,2,3,4}, { {0,9},{8,7},{6,5} }] ] (*gives error message*)
In[3]:= f[{1,2,3,4}, { {0,9},{8,7},{6,5} }] (* this is where i am standing *)

I do want to achieve the following output Out[3] from In[3], since the function f (e.g. Complement) does indeed require 2 lists as arguments.

Out[3]:= { f[{1,2,3,4},{0,9}], f[{1,2,3,4},{8,7}], f[{1,2,3,4},{6,5}] } (* desired output, how get there? *)

How can I achieve the above output?

POSTED BY: Raspi Rascal
11 Replies

A totally different approach

urne = Range[10];
s4 = Subsets[urne, {4}];
s3 = Subsets[urne, {3}];
s2 = Subsets[urne, {2}];

cand = Reap[
  Do[
   x = s4[[i]];
   Do[
    y = s3[[j]];
    If[Intersection[x, y] == {},
     Do[
      z = s2[[k]];
      If[Intersection[x, z] == {} && Intersection[y, z] == {},
       Sow[{x, y, z}]],
      {k, 1, Length[s2]}]
     ],
    {j, 1, Length[s3]}],
   {i, 1, Length[s4]}]
  ]

Now you haver to get rid of Null and fill in the missing element, e.g.

cand1 = Flatten[Drop[cand, 1], 2]
Complement[urne, Flatten[cand1[[5]]]]
POSTED BY: Hans Dolhaine

Great nested procedural coding, thanks for the alternative! The final line in your code would be:

sol2 = Append[#, Complement[urne, Flatten[#]]] & /@ cand1;
sol2 == sol (* True *)

I have added your code with comments in my book including a hyperlink to your post, loving it. It is always great to have alternatives in a book, or in life. Happy Xmas season, thanks again! @Hans Dolhaine

POSTED BY: Raspi Rascal

highschool text problem: " In how many ways can 10 numbered balls be distributed in four numbered compartments so that the first compartment contains 4, the second 3, the third 2 and the fourth 1 balls each (4+3+2+1=10)? The sequence of balls within the compartments be irrelevant."

Well, the answer is simple, 210×20×3×1=12600. A valid 4-tuple would be, for example:

( {1, 6, 9, 10}, {2, 3, 8}, {5, 7}, {4} )

Today i tried to build the list with 12600 such entries, and it involved a lot of MapThread, Thread, so i revisted this thread to check how we did things. I managed to produce the list but my coding feels awkward because i always worked with 2 lists at a time. The question is, is there a more elegant way of producing the output, maybe by feeding all four lists (sub4, sub3, sub2, rest2) at the same time in a single MapThread[Thread[]] statement?

del[from_List, this_List] := DeleteCases[from, Alternatives @@ this] ;
nUrne = Range[10] ;
sub4 = Subsets[nUrne, {4}] ; (*210 elements e.g. {1,2,3,4}*)
rest4 = del[nUrne, #] & /@ sub4 ; (* 210 elements e.g. {5,6,7,8,9,10}*)
sub3 = Subsets[#, {3}] & /@ rest4 ;
rest3 = MapThread[Function[{u, v}, del[u, #] & /@ v], {rest4, sub3}] ;
sub2 = MapAt[Subsets[#, {2}] &, rest3, {All, All}] ;
rest2 = MapThread[Function[{u, v}, del[u, #] & /@ v], {rest3, sub2}, 2] ;
sub2rest2 = MapThread[List, {sub2, rest2}, 3] ;
sub3sub2rest2 = MapThread[Thread[{##}, List, -1] &, {sub3, sub2rest2}, 2] ;
rest6 = MapAt[Sequence @@ # &, sub3sub2rest2, {All, All, All, 2}] ;
rest6neu = Flatten[#, 1] & /@ rest6 ;
almost = Flatten[MapThread[Thread[{##}, List, -1] &, {sub4, rest6neu}], 1] ;
finally = MapAt[Sequence @@ # &, almost, {All, 2}]

Heavily shorted output:

{{{1, 2, 3, 4}, {5, 6, 7}, {8, 9}, {10}}, {{1, 2, 3, 4}, {5, 6, 7}, {8, 10}, {9}},
 {{1, 2, 3, 4}, {5, 6, 7}, {9, 10}, {8}}, <···12593···> ,
 {{7, 8, 9, 10}, {3, 5, 6}, {2, 4}, {1}}, {{7, 8, 9, 10}, {4, 5, 6}, {1, 2}, {3}},
 {{7, 8, 9, 10}, {4, 5, 6}, {1, 3}, {2}}, {{7, 8, 9, 10}, {4, 5, 6}, {2, 3}, {1}}}

Thanks for your attention. In any case, this problem was a good beginner's exercise in juggling with Map, MapAt, Thread, MapThread and their combinations.

POSTED BY: Raspi Rascal

@everyone, how about a related Thread challenge with nested lists (the placeholders a1, A1, etc be integer numbers): You can see the structure of the input lists p1, p2, p3 and the desired output structure in Out[5]. I got Out[5] by way of Flatten[Table[]] approach using two iterators. I am not happy with that coding solution. Is there any way to get Out[5] by using Thread on p1, p2, p3? Thanks in advance for looking into this.

In[1]:= p1 = { {a1, A1}, {b1, B1}, {c1, C1} };
p2 = { {{a2, a3}, {a4, a5}},
       {{b2, b3}, {b4, b5}},
       {{c2, c3}, {c4, c5}} };
p3 = { {{A2, A3}, {A4, A5}},
       {{B2, B3}, {B4, B5}},
       {{C2, C3}, {C4, C5}} };
{p, q, r} = Dimensions[p2];

sol1 = Flatten[Table[ (* not elegant way to get Out[5] *)
   {p1[[i]], p2[[i, j]], p3[[i, j]]}
   , {i, p}, {j, q}], 1]

Out[5]= { {{a1, A1}, {a2, a3}, {A2, A3}}, {{a1, A1}, {a4, a5}, {A4, A5}},
          {{b1, B1}, {b2, b3}, {B2, B3}}, {{b1, B1}, {b4, b5}, {B4, B5}},
          {{c1, C1}, {c2, c3}, {C2, C3}}, {{c1, C1}, {c4, c5}, {C4, C5}} }
POSTED BY: Raspi Rascal

This quite nice example demonstrating what is invariant after Thread operation and powerful idea of functional programming in Wolfram Language.

Thread[f[{a, b, c}]] => {f[a],f[b],f[c]}
Thread[f[{a, b, c}, {x, y, z}]] => {f[a,x],f[b,y],f[c,z]}
Thread[f[{1,2,3,4},{{0,9},{8,7},{6,5}}],List,-1] => {f[{1,2,3,4},{0,9}],f[{1,2,3,4},{8,7}],f[{1,2,3,4},{6,5}]}

Thread outputs a list with the same length of the expanded part (or parts if their lengths are equal) from input. In your example p1 is treated as list of 3 and the output is a list of 6 items, therefore we do need something like nested iteration in your code and one level of Thread is not enough. In your output I feel it is safe to break down to three steps.

  1. Pick up the first element from each list and generate a parent list (length of 3)
  2. Then Thread the last two elements (each becomes length of 2)
  3. Remove the outmost level of list to make it a list of 6 items (flatten the first level, same as your code)

MapThread function comes handy in the first task. Here is an example:

In[2]:= MapThread[f,{p1,p2,p3}]
Out[2]= {f[{a1,A1},{{a2,a3},{a4,a5}},{{A2,A3},{A4,A5}}],f[{b1,B1},{{b2,b3},{b4,b5}},{{B2,B3},{B4,B5}}],f[{c1,C1},{{c2,c3},{c4,c5}},{{C2,C3},{C4,C5}}]}

The result is very promising. Eventually, I can replace f withSlotSequnce + Thread[...,List,-2] to expand each element. The following code wraps up everything in one line to generate the desired result .

In[3]:= Flatten[MapThread[Thread[{##},List,-2]&,{p1,p2,p3}],1]
Out[3]= {{{a1,A1},{a2,a3},{A2,A3}},{{a1,A1},{a4,a5},{A4,A5}},{{b1,B1},{b2,b3},{B2,B3}},{{b1,B1},{b4,b5},{B4,B5}},{{c1,C1},{c2,c3},{C2,C3}},{{c1,C1},{c4,c5},{C4,C5}}}
POSTED BY: Shenghui Yang

Beautiful functional programming, avoiding nested pure functions (i know the basics of pure functions but am not conversant with SlotSequence or nested pure functions), thank you so much!

That's "my point of being": not to be really knowledgeable in stochastics, and not to quickly obtain the end result of a textbook problem, but to (day by day) learn more about Wolfram L coding (core language practice), gain more experience, getting better at it. Solving the end of chapter problems in my stochastics highschool textbook, one after the other, beginning from Chapter 1, is just my vehicle to practice using Mathematica as my text processing editor instead of handwriting solutions or using M$ Word/Excel/Powerpoint, or LaTeX (ouch). @Shenghui Yang you totally helped me in this regard, on my path to learning/understanding more of the language. So thanks again for your kind attention!! I think readers of this thread can learn quite something about recognizing the need for Thread no pun intended and how to apply it correctly. I really learned something here! The last time i did some coding was 2.0 yrs ago, and it seems that i didn't lose all my essential knowledge during that downtime. I am motivated now to carry on with my stochastics highschool textbook solutions manual project. Should be completed within 6 months.

And finally here are the coding solutions, developed in this thread, to solve the original textbook problem "Show all possible hands, dealing out 2 cards to 3 players from a deck of 6 different cards (90 possibilities)", thanks for your interest:

In[113]:= deck = {1, 2, 3, 4, 5, 6};
player1 = Subsets[deck, {2}];

rest1 = Complement[deck, #] & /@ player1;
player2 = Subsets[#, {2}] & /@ rest1;
trans = Transpose[{rest1, player2}];
player3 = (Map[Thread[Apply[f, #], List, -1] &, trans]) /. 
   f -> ( Complement[#1, #2] &);

rest1h = Hold[#] & /@ rest1;
player2h = Thread[Hold[#]] & /@ player2; (* ugly coding *)
transh = Transpose[{rest1h, player2h}];
player3h = (Map[Thread[Apply[f, #]] &, transh] // ReleaseHold) /. 
   f -> ( Complement[#1, #2] &);

{p, q, r} = Dimensions[player2];

sol1 = (* straight-forward easy to understand but inelegant coding *)
  Flatten[Table[
    {player1[[i]], player2[[i, j]], player3[[i, j]]}
    , {i, p}, {j, q}], 1];

sol2 = (* straight-forward easy to understand but inelegant coding *)
  Flatten[Table[
    {player1[[i]], player2[[i, j]], player3h[[i, j]]}
    , {i, p}, {j, q}], 1];

sol3 = Flatten[
   MapThread[Thread[{##}, List, -2] &, {player1, player2, player3}], 
   1]; (* beautiful, not easy to understand because of clever double threadings *)

sol4 = Map[Sort, Map[Partition[#, 2] &, Permutations[deck]], {2}] // 
  DeleteDuplicates; (* easy to code and understand *)

sol1 == sol2 == sol3 == sol4

Out[126]= True
POSTED BY: Raspi Rascal

I am very glad to be part of your learning experience with Wolfram Language through our discussion about your analysis on the playing card problem. I highly recommend enthusiastic users like you to join our daily study group for further growth

*Some playing card related resources:

POSTED BY: Shenghui Yang

You can keep the Thread function with additional arguments:

In[10]:= Thread[f[{1,2,3,4},{{0,9},{8,7},{6,5}}],List,-1]
Out[10]= {f[{1,2,3,4},{0,9}],f[{1,2,3,4},{8,7}],f[{1,2,3,4},{6,5}]}

It simply tells the function to hold the first list {1,2,3,4} and only thread through the last argument (in your case the second list {{0,9},{8,7},{6,5}}).This is documented under Thread -> Scope -> Sequence Specification

POSTED BY: Shenghui Yang

Thank you very much, this solves the op perfectly. I didn't understand the Thread documentation there, neither made it clear to me that a {-2} would solve my problem. The documentation doesn't show {-1} nor nested lists, like my example (this llp001). Maybe it's an idea to add my example to the documentation? Good practical/instructive examples in the documentation are valuable.

POSTED BY: Raspi Rascal

What about this?

f[{1, 2, 3, 4}, #] & /@ {{0, 9}, {8, 7}, {6, 5}}
POSTED BY: Hans Dolhaine

Thank you, almost there with your suggestion! Maybe a nested pure function is needed? I cannot improve In[184], my bad. Maybe you could edit the left side of In[184] such that it gives Out[3]? Would be very interesting to see how this can be done, i.e. going from In[3] to Out[3] without the Thread function.

In[184]:= Apply[f[#1] /@ #2 &, {{1, 2, 3, 4}, {{0, 9}, {8, 7}, {6, 5}}}]
Out[184]= {f[{1, 2, 3, 4}][{0, 9}], f[{1, 2, 3, 4}][{8, 7}], f[{1, 2, 3, 4}][{6, 5}]} (*ouch haha!*)

Btw, by wrapping all lists with Hold and ReleaseHold in my initial Thread approach, i get the output Out[3] as desired. Of course, Shenghui Yang's solution below is just perfect to solve the op (original post, original problem).

POSTED BY: Raspi Rascal
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard