1
$\begingroup$

first time stack exchange poster here! So I was working on a problem, the purpose of which was to write a function in the procedural style to rotate right the ith row of a matrix by i - 1 spaces. Ok, easy enough here was my implementation:

rotateRowsProcedural[mat_?MatrixQ] := ( clonedMat = mat; i = 1; While[i <= Dimensions[clonedMat][[1]], clonedMat[[i]] = RotateRight[clonedMat[[i]], i - 1]; i++; ]; clonedMat ) 

This was too boring so I decided I wanted to try and solve the problem using pattern matching approach and I came up with

rotateRowsPattern[matrix_?MatrixQ] := matrix /. mat : {x__List} :> (RotateRight[#, Position[mat, #][[1, 1]] - 1] & /@ {x}) ` 

Now I thought this was pretty awesome I mean to me its the essence of Mathematica. However, I then decided to compare the efficiency of the two functions

Mean[Table[Timing[rotateRowsPattern[RandomInteger[{0, 10},{1000, 1000}]]][[1]], {100}]] 

0.038844

Mean[Table[Timing[rotateRowsProcedural[RandomInteger[{0,10},{1000,1000}]]][[1]], {100}]] 

0.0165361

I was surprised that the procedural implementation was faster. Then I thought it over and I think it is the case that calling Position in the pattern implementation must slow things down. My question then is if I want to use pattern matching how can I reference the location of x (from x__List) so that I don't have to call Position.

Also, I'm aware that combining Map and Range would be the most concise way to do this but I'm interested in the pattern matching.

$\endgroup$
5
  • 1
    $\begingroup$ "Map and Range would be the most concise way..." - nope. There's MapIndexed[]... $\endgroup$ Commented May 30, 2013 at 6:00
  • $\begingroup$ This isn't using pattern matching but for a speed up this may help: rr[rin_] := (RotateRight[rin[[#]], # - 1] & /@ Range[Length[rin]]). $\endgroup$ Commented May 30, 2013 at 6:07
  • 2
    $\begingroup$ I would also define your functions as Module as you are defining global variables (clonedmat) within them. It's best to keep track of the scope of such variables when you can. $\endgroup$ Commented May 30, 2013 at 6:09
  • $\begingroup$ @LarryS, please feel free to ignore my comment in that case. You are right that you did explicitly say that and I had missed that point in your post, my apologies. $\endgroup$ Commented May 30, 2013 at 6:22
  • 6
    $\begingroup$ @LarryS Be civil. People are only attempting to be helpful. Leaving stray global symbols in your function is bad programming practice and code exhibiting such will often be remarked upon. $\endgroup$ Commented May 30, 2013 at 6:27

5 Answers 5

8
$\begingroup$

Since the summary of your question is:

My question then is if I want to use pattern matching how can I reference the location of x (from x__List) so that I don't have to call Position.

I believe it is a duplicate of: Position of a pattern-matched part of an expression

Nevertheless in an effort to be helpful I would like to comment on your code.

Your procedural function should use Module to localize the Symbols that you use. It would also be more efficient to calculate the dimensions only once:

rotateRowsProcedural[mat_?MatrixQ] := Module[{clonedMat = mat, i = 1, dims = Dimensions[mat][[1]]}, While[i <= dims, clonedMat[[i]] = RotateRight[clonedMat[[i]], i - 1]; i++;]; clonedMat ] 

Your pattern based function is a bit confusing. The pattern matching is purely incidental to the operation of the function itself which could be written simply:

rotateRowsPattern[matrix_?MatrixQ] := RotateRight[#, Position[matrix, #][[1, 1]] - 1] & /@ matrix 

But both this function and yours will fail of there are repeated rows in the matrix.

You will see from the linked question above that there is no known easy and general solution to the problem of getting the position of a matched pattern. However, for this simple application you can simply increment a counter for each row that is matched, which is perhaps in the spirit of what you intend:

rotateRows1[m_?MatrixQ] := Module[{i = 0}, m /. x_?VectorQ :> RotateRight[x, i++]] 

Still, this might be better formulated without the pattern matching as:

rotateRows2[m_?MatrixQ] := Module[{i = 0}, RotateRight[#, i++] & /@ m] 

But this is really nothing more than a manual implementation of MapIndexed:

rotateRows3[m_?MatrixQ] := MapIndexed[RotateRight[#, #2[[1]] - 1] &, m] 
$\endgroup$
3
  • $\begingroup$ One could of course do Length[mat] instead of Dimensions[mat][[1]]. Using While[] here looks a bit clunky; I'd have used Do[] myself. $\endgroup$ Commented May 30, 2013 at 6:39
  • $\begingroup$ @J.M. Agreed. I was attempting to work with the OP's style, for what it's worth. $\endgroup$ Commented May 30, 2013 at 6:55
  • $\begingroup$ @Mr.Wizard (of Oz) thanks for the link. I'm somewhat disappointed that matched elements from the pattern matcher don't know by default their position but thus is life. $\endgroup$ Commented May 30, 2013 at 15:16
2
$\begingroup$

You could consider using MapIndexed just to "tag" each row with its row number and then use a replacement rule from there:

rot[mat_] := MapIndexed[List, mat] /. {x_, i_} :> RotateRight[x, i - 1] 

Faster, but less readable, is to use Transpose and Range instead of MapIndexed:

rot[mat_] := Transpose[{mat, Range@Length@mat}] /. {x_, i_} :> RotateRight[x, i - 1] 
$\endgroup$
2
$\begingroup$
SeedRandom[1]; (m = RandomChoice[Alphabet[], {6, 5}]) // MatrixForm i = 4; 

Using Pattern matching:

res3 = m /. {k : Repeated[_, {i - 1}], h_, g___} :> {k, RotateRight[h, i - 1], g} 

More canonical solutions:

Using MapAt:

res1 = MapAt[RotateRight[#, i - 1] &, m, i] 

Using SubsetMap:

res2 = SubsetMap[RotateRight[#, {0, i - 1}] &, m, {i}] 

Result

MatrixForm /@ {m, res1, res2, res3} 

enter image description here

$\endgroup$
2
$\begingroup$
m = Partition[Range @ 25, 5]; i = 2; 

1. If we can permanently change m:

m[[i]] = RotateRight[m[[i]], i - 1]; m // MatrixForm 

enter image description here

2. Otherwise:

rot[x_, i_] := Module[{m = x}, m[[i]] = RotateRight[m[[i]], i - 1]; m] rot[m, 2] // MatrixForm 

enter image description here

$\endgroup$
2
$\begingroup$
 ReplacePart[#, {i} -> RotateRight[#[[i]], i - 1]] & @ mat // Thanks to @eldo 

With i=2

mat = Partition[Range @ 25, 5]; With[{i=2},ReplacePart[#, {i} -> RotateRight[#[[i]], i - 1]] &] (*{ {1,2,3,4,5}, {10,6,7,8,9}, {11,12,13,14,15}, {16,17,18,19,20}, {21,22,23,24,25} } *) 
$\endgroup$
2
  • 1
    $\begingroup$ It should be ReplacePart[#, {i} -> RotateRight[#[[i]], i - 1]] & @ mat $\endgroup$ Commented Oct 14, 2023 at 9:17
  • $\begingroup$ @eldo. Thanks! You are of course right (as usual!) $\endgroup$ Commented Oct 14, 2023 at 10:14

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.