Background:
- "experienced" Pascal-programmer (DOS-era, never got into OOP)
- proud owner of Mathematica ver 11.2
- have used Mathematica for years to do my share of number crunching and plotting to produce material for my university courses (as well as for Math.SE :-)
Next task:
I need to add a few things to my Mathematica repertoire. The immediate goal is to build the necessary Mathematica functions to produce animated GIFs like
That is, animated sequences of moves on a Rubik cube. The shown sequence of moves cyclically permutes the positions of $3$ corner pieces. The GIF is included here to give the general idea, and also to prove that I know enough to code this :-)
Currently the animation is achieved by manipulating a variable descriptively named vertexlist
that contains the 3D-positions of all the eight corners of all the small moving parts. It is clear that to produce the desired animations, I simply need to manipulate the contents of this variable, and have Mathematica Show
the cube in all the intermediate states in sequence.
The snake in the paradise is that currently vertexlist
is, in old-timer-speak, a global variable. I probably can do everything I need in the immediate future that way, but I have reached a point, where that feels A) unprofessional, B) inefficient. For example, in a classroom setting I surely want to have several different Rubik's cubes of varying sizes, and in various current states. To that end I need a data structure not unlike a Pascal record (similar to struct in C), say (I know this is unsyntactic, because the value of a field cannot really be used as a limit of an array, but we can safely ignore that here I think):
cube=RECORD
size:2..5;
vertexlist: ARRAY[1..size,1..size,1..size,1..8] OF...
...(* information about which vertices form a polygon of which color *)
END;
With that taken care of I can then easily code functions roughly like
MicroRotation[c_, Axes_, Layer_, Angle_]
, where c
would be the cube I want to modify.
How do I achieve something like that type declaration in Mathematica? I know that in Mathematica everything really is a list, and that I can simply define my objects with a new list header like JyrkisRubikCube
(ok, that would be kludgy, but anyway). But doing that would not really help here! For I need my code to refer to and modify the individual fields of the data structure. If I use a List
, I guess I can, using the freedom built into Mathematica's lists just assume that the first entry is the size, the second is the vertexlist et cetera. Is that really the only way to go? Is it the recommended way? Having descriptive names for the fields of the record would, at the very least, help me if I want to return to this project a few years down the road...
Searching the site gave some promising looking hits:
- Here is a similar question about C structs. There is a lot of material in the answers, but at first reading they appeared to be a bit too high level to me. More about that below.
- In loc.linked Leonid Shifrin referred to this thread in SO for something similar.
I will keep studying those. If I try and use Association
as described by Szabolcs how do I:
- create/declare a variable of the prescribed type,
- access the chosen field of a variable of this type, and
- modify the value of (a component of) the chosen field of a variable of this type?
I realize that this question may be too broad to be answered well in the limited space available here, but I also appreciate pointers and links. Even a key buzzword might do. Given that outside academia (and in spite of the best efforts of Borland) Pascal never got much traction, probably eplaining why my searches did not produce anything very useful.
Edit: The posts I found leave me with the impression that using Association
creates a data structure that is very kludgy to modify. Consider the following snippet from my current notebook. This function
rotates the layer number v
(an integer in the range from $1$ to size
)
by an angle given by the variable x
rotateX[v_, x_] := Module[{i, j, k, t},
m = {{1, 0, 0}, {0, Cos[x], -Sin[x]}, {0, Sin[x], Cos[x]}};
For[i = 1, i < size + 1, i++,
For[j = 1, j < size + 1, j++,
For[k = 1, k < size + 1, k++,
If[Floor[vertexlist[[i, j, k]][[1]][[1]]] == v,
For[t = 1, t < 9, t++,
vertexlist[[i, j, k]][[t]] =
m.(vertexlist[[i, j, k]][[t]] - {0, center, center}) + {0,
center, center}
]]]]]]
You see that I modify the components of the global vertexlist
in the 4-fold loop according to whether the small part is currently in the layer being rotated.
I need a way of modifying not the global vertexlist
but the vertexlist of a cube of my choice to be passed as a third parameter to rotateX
. Something like rotateX[c_,x_,cube_]
that will modify cube.vertexlist
instead of vertexlist
, where cube
is a data structure that has at least size
and vertexlist
as fields.
Is there a way of doing this other than using, say,
cube[[2]]
, everywhere in places of the more naturalcube.vertexlist
?
One more animated sequence of moves. I am using ListAnimate
to generate these. I chose to do eight frames per a quarter turn, so an animation of 8 quarter turns has 64 frames. The sequence of moves below also cyclically permutes the positions of three small cubes. This time those small cubes are on the faces of the big cube. Two of those small cubes show a white face and one of them shows a blue face. Because one white cube moved to the place initially occupied by another white cube, visually the result looks like a blue and a white piece traded places, but, in fact, it is a 3-cycle. Meaning, that you need to perform the sequence of moves three times to return the cube to its initial state. The algebra of permutations plays out in a way that 3-cycles are simple to produce. Ask me, if you want to know more :-)
Association
wrapped by a head to indicate the "class". So an instance of the class would look likeX = JyrkisRubikCube[Association[...]]
. Then you can useTagSetDelayed
for defining and overloading methods for this class. A field/property"foo"
ofX
can then accessed withX[[1]][["foo"]]
and set withX[[1]][["foo"]] = bar
. It is not like you have a fixed set of allowed keys in the association; fields can be created and deleted (KeyDrop
) dynamically. But I guess that's what comes closed to what you ask for. $\endgroup$