Quanto segue è per lo più giusto; Potrei essermi perso qualcosa o aver frainteso qualcosa; leggi il documento collegato (è troppo breve) e spara nei commenti - grazie.
Cattiva analogia : una classe è uno stampino per biscotti. Un'istanza è un biscotto fatto con lo stampino per biscotti.
Problema con l'analogia : Chiamerò anche i JSL classe un prototipo . Un prototipo è un cookie vero e proprio. Il biscotto prototipo potevo essere mangiato o modificato, ma normalmente non vorresti farlo. Invece, dovresti tracciarne il contorno nell'impasto dei biscotti e tagliarlo con le forbici per creare nuove istanze di biscotti. Ma se dai un morso al cookie prototipo, anche tutte le future istanze di cookie sembreranno morse (secondo esempio). Inoltre, il metodo _init_(...) non viene eseguito per il cookie prototipo, quindi potrebbe essere cotto a metà, ma ha la forma corretta.
Il documentazione utilizza modello piuttosto che prototipo . Preferisco il prototipo al modello; un prototipo è un oggetto utilizzabile mentre un modello mostra solo dove dovrebbe andare la vernice spray. Questa sottile distinzione non ha importanza per il primo esempio.
Un semplice esempio
Questo è un semplice esempio a classe singola per bolla . Il prototipo della classe è realizzato da DefineClass(). NewObject() rende 61 istanze effettive dal prototipo. Ognuna delle 61 istanze ha un raggio e un centro x,y. Il prototipo fornisce anche metodi per _init_(...), drift() e draw(). Viene visualizzato un grafico utilizzando l'oggetto metodo draw per ogni oggetto. Quindi viene eseguito un ciclo, chiamando il metodo di deriva dell'oggetto per ogni oggetto e quindi dicendo al grafico di ridisegnare. Il risultato è un grafico animato come questo:
Il JSL per il grafico:
// define a simple class (like a template) to represent a 2D bubble
Define Class("bubble",
// class member variables
m_radius = .; // class member variables are specified in
m_xpos = .; // the class definition with initial values.
m_ypos = .; // these values are copied into instances created by NewObject(...)
// class methods
_init_ = Method( {radius, x, y}, // called by NewObject(bubble(r,x,y)) below to initialize an instance
this:m_xpos = x; // this: is the namespace of an instance
m_ypos = y; // you don't need "this:" unless the member name is
m_radius = radius; // the same as a parameter name
);
drift = Method( {}, // a method to move the bubble
m_xpos = m_xpos * 0.995;// tentatively drift to center
m_ypos = m_ypos * 0.995;
);
draw = Method( {}, // a method to draw the bubble
Circle( {m_xpos, m_ypos}, m_radius )
);
);
// make a bunch of instances of the bubble class, keep them in a list
bubblelist = {};
For( i = 1, i <= 61, i += 1,
Insert Into( bubblelist,
// each instance of bubble is initialized with a random radius and position
New Object( bubble( Random Uniform( .03, .8 ), Random Uniform( -.9, .9 ), Random Uniform( -.9, .9 ) ) )
)
);
picno = 0; // need an ID for each picture
// make a window to display all the instances of the bubble class
New Window( "bubble class instances",
gb = Graph Box( // the graphic script for the graph box scales and sizes the axes and...
xaxis( {Add Ref Line( 0, "Solid", "Black", "", 1 )} ),
yaxis( {Add Ref Line( 0, "Solid", "Black", "", 1 )} ),
framesize( 600, 600 ),
X Scale( -1, 1 ),
Y Scale( -1, 1 ),
// the graphic script tells each bubble to draw itself
For( i = 1, i <= N Items( bubblelist ), i += 1,
bubblelist[i]:draw()
);
text({-.9,-.9},char(picno)),
<<ONCLOSE></ONCLOSE>Il mio modello mentale per come funziona: sotto le coperte, DefineClass() mantiene un elenco nascosto di tutti i prototipi di DefineClass() e sembrano Un sacco come istanze. Maggiori informazioni su questo in un momento; l'esempio precedente vi accede tramite la funzione NewObject(). NewObject() clona una nuova istanza dell'oggetto prototipo ed esegue il metodo _init_.
Cosa c'è questo: ?
Nell'esempio sopra ho chiamato le variabili membro con m_ prefisso. Non è necessario, ma può essere utile sia per ricordare quali variabili sono variabili membro, sia per prevenire collisioni con nomi di parametri e nomi di variabili locali. Ma ho anche usato questo:m_xpos su una delle assegnazioni del metodo _init_(). Il questo: namespace significa "la variabile membro dell'istanza corrente". Così questo: x = x; potrebbe essere utilizzato se si desidera che la variabile membro x e il parametro x abbiano lo stesso nome. m_ sarà più facile per la prossima persona che mantiene il tuo codice.
Cosa c'è bolla: ?
C'è un altro spazio dei nomi che per lo più non ti serve questo mostrerà perché ho detto che i prototipi DefineClass() nell'elenco nascosto sembrano Un sacco come le istanze: il nome della classe è uno spazio dei nomi. Usando bolla:m_radius accederà al m_radius del prototipo, anche da un metodo di istanza. Se pensi di aver bisogno di una variabile membro statica, questo è un modo per crearne una ... se stai attento.
Utilizzo del prototipo per i valori statici
L'esempio successivo utilizza una variabile m_ID statica. Come suggeriscono i commenti, probabilmente avrei dovuto fare una variazione diversa, ma questa è comunque utile perché spiega l'avvertimento "se stai attento". Ecco cosa succede (il mio modello mentale, di nuovo):
Quando _init_() viene eseguito di seguito, la classe bubble2 ha già copiato tutto il contenuto del prototipo nella nuova istanza. La nuova istanza ha una copia del m_id del prototipo. _init_() cambia quindi m_id() nel prototipo. L'istanza successiva ottiene il valore modificato. Le altre funzioni membro devono scegliere tra m_id nel prototipo (bubble2:m_id) e this:m_id (o solo m_id senza namespace). Qui usano m_id che è una copia di un numero intero univoco per l'istanza.
Nel video, l'ultimo pezzo del puzzle va a posto alle 5:15 sul bordo inferiore dello schermo. I cerchi neri sono bubble2 che mostrano il loro valore m_id. Usano il m_id per assicurarsi di ignorare se stessi quando studiano tutti i vicini vicini.
// twist is a scaling divisor for a shearing rotation that moves
// the outer edge faster.
// this value needs to get bigger with more beehive cells.
global:twist = 600;
// choose the size to make them fit, but it also interacts with twist
// because the distance from the center grows
global:bubblesize = 0.057;
// use a defined class to represent each cell in the beehive.
Define Class("bubble2",
m_radius = .; // in the video, all beehive cells have the same radius
m_xpos = .; // the beehive cell's position
m_ypos = .;
//
// this is answering the question "can a JSL Class have a static member variable?"
// this could have been done more intuitively for this example by passing the
// index of the creation loop to the _init_ code, but here it is...initialize the
// "static" value to 1...
m_id = 1; // keep reading to understand the two-faced nature of this variable...
// What actually happens: when newObject(bubble2(...)) makes a new instance, the _init_(...)
// is called.
// First, the m_id is cloned from the prototype to the instance. The first instance gets m_id==1.
// Second, the bubble2: namespace causes the prototype class to increment the
// prototype's m_id. The next instance gets the new value from the prototype, m_id==2.
// this is a little odd, coming from a C++ -like language; in the drift method below,
// "other:m_id != m_id" both m_id values are regular non-static instance members.
// if you want the static behaviour, use the bubble2:m_id approach (bubble2 is the class
// name and access the singleton in the prototype class.)
// Normally you *never* want to use the class name as a name space qualifier because
// you set up the class correctly and initialize via _init_( parameters ). And normally
// a static value is also a constant value, so using a copy is just fine. But if
// multiple instances need to communicate via a singleton static, AND you always use
// the namespace, this is a reasonable place to keep the static.
// Having written all that, I now dislike this particular example because it is using
// a confusing combination of bubble2:m_id and instance values of m_id.
_init_ = Method( {radius, x, y},
m_radius = radius;
this:m_xpos = x; // this: (or nothing) operates on an instance
m_ypos = y;
bubble2:m_id++; // bubble2: operates on the prototype. The next newobject(bubble2(...)) uses the modified prototype
);
distanceBetweenCenters = Method( {other},
/*return*/ Sqrt( (m_xpos - other:m_xpos) ^ 2 + (m_ypos - other:m_ypos) ^ 2 )
);
distanceBetweenEdges = Method( {other}, // negative: overlaps
/*return*/ distanceBetweenCenters( other ) - (m_radius + other:m_radius)
);
drift = Method( {},
// bubbles should pull to 0,0 AND move away from near bubbles
angle = ATan( m_ypos, m_xpos );
radius = Sqrt( m_ypos ^ 2 + m_xpos ^ 2 );
shear = global:twist; // also depends on number of circles
shrink = .99950;
// radius/shear creates a tiny amount of rotation, more on the edges. That rotational
// shearing helps the circles bounce about. /400 is not enough and /100 is too much.
// radius*shrink pulls all the circles to the origin. It is balanced by push, below.
x = Cos( angle + radius / shear ) * radius * shrink;
y = Sin( angle + radius / shear ) * radius * shrink;
// check neighbors
{neighbors, distances} = kdt << kNearestRows( 7, m_id );
tension = 0;
ntension = 0; // calculate a tension, or stress, for contour to use as a color
For( iN = 1, iN <= N Items( neighbors ), iN += 1,
i = neighbors[iN];
other = bubblelist[i];
If( other:m_id != m_id, // <<<<<<<<<<<<<<<< NOT the bubble2:m_id value !!!
dist = distanceBetweenEdges( other );
// if the dist is zero, it is perfect. if the other guy is far away, ignore him.
If( Abs( dist ) < global:bubblesize * 0.8,
tension += dist ^ 2; // this will be a RMS value at the bottom
ntension += 1;
);
If( dist < 0, //push
angle = ATan( m_ypos - other:m_ypos, m_xpos - other:m_xpos );
x += Cos( angle ) * Abs( dist ) ^ .4 * .05;
y += Sin( angle ) * Abs( dist ) ^ .4 * .05;
);//
, //
Beep()
);
);
m_xpos = x;
m_ypos = y;
If( ntension == 0,
ntension = 1;
tension += (global:bubblesize / 2) ^ 2;
);
Sqrt( tension / ntension ); // RMS
);
draw = Method( {},
Transparency( .8 );
Circle( {m_xpos, m_ypos}, m_radius, "FILL" );
Transparency( 1 );
Text( center justified, {m_xpos, m_ypos - m_radius * .2}, Char( m_id ) );
);
);
bubblelist = {};
n = 12; // how many "rings" of hex cells
m = -26 + 21 * n + 3 * (n - 3) ^ 2; // yes, I used JMP to get this formula
For( i = 1, i <= m, i += 1,
Insert Into(
bubblelist,
New Object( bubble2( Random Uniform( global:bubblesize, global:bubblesize ), Random Uniform( -.9, .9 ), Random Uniform( -.9, .9 ) ) )
)
);
// need a table for contour plot, and it will include 100 values in a circle around the hive
ring = 100;
dt = New Table( "tension",
Add Rows( m + ring ),
New Column( "x", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "y", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "tension", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) )
);
For( i = 1, i <= ring, i += 1,
angle = 2 * Pi() * i / ring;
dt:x[m + i] = 1.25 * Sin( angle );
dt:y[m + i] = 1.25 * Cos( angle );
dt:tension[m + i] = 0;
);
cplot = dt << Contour Plot(
X( :x, :y ),
Y( :tension ),
Show Data Points( 0 ),
Fill Areas( 1 ),
Label Contours( 0 ),
Transform( "Range Normalized" ),
Color Theme( "Green Yellow Red" ),
Specify Contours( Min( 0 ), Max( .01 ), N( 50 ) )
);
Report( cplot )[framebox( 1 )] << Frame Size( 1920, 1080 ) << maximizewindow( 1 ) << backgroundcolor( "black" );
Report( cplot )[framebox( 1 )] << xaxis(
{Format( "Fixed Dec", 12, 3 ), Min( -1.4*1920/1080 ), Max( 1.4*1920/1080 ), Inc( 0.5 ), Label Row(
{Show Major Grid( 0 ), Show Major Labels( 0 ), Show Major Ticks( 0 ), Show Minor Grid( 0 ), Show Minor Labels( 0 ), Show Minor Ticks( 0 )}
)}
);
Report( cplot )[framebox( 1 )] << yaxis(
{Format( "Fixed Dec", 12, 3 ), Min( -1.4 ), Max( 1.4 ), Inc( 0.5 ), Label Row(
{Show Major Grid( 0 ), Show Major Labels( 0 ), Show Major Ticks( 0 ), Show Minor Grid( 0 ), Show Minor Labels( 0 ), Show Minor Ticks( 0 )}
)}
);
// here is the script that makes the bubbles draw
Report( cplot )[framebox( 1 )] << addgraphicsscript(
Fill Color( "black" );
text Color( "white" );
pen Color( rgbcolor(64,64,64) );
For( i = 1, i <= N Items( bubblelist ), i += 1,
bubblelist[i]:draw()
);
hline(0);
vline(0);
);
cplot << onclose(
keepRunning = 0;
Close( dt, nosave );
1;
);
Wait( 0 );
//dir = "$temp\ClassWithStatic" || Char( global:twist ) || "\";
//Delete Directory( dir );
//Create Directory( dir );
//Write( Convert File Path( dir, "windows" ) );
picno = 0;
keepRunning = 1;
While( keepRunning,
xy = J( N Items( bubblelist ), 2, . );
For( i = 1, i <= N Items( bubblelist ), i += 1,
xy[i, 1] = bubblelist[i]:m_xpos;
xy[i, 2] = bubblelist[i]:m_ypos;
);
// the kdtable makes it faster to find near neighbors
kdt = KDTable( xy );
For( i = 1, i <= N Items( bubblelist ), i += 1,
dt:tension[i] = bubblelist[i]:drift();
dt:x[i] = bubblelist[i]:m_xpos;
dt:y[i] = bubblelist[i]:m_ypos;
);
Report( cplot )[framebox( 1 )] << inval << updatewindow;
picno += 1;
// Report( cplot )[framebox( 1 )] << save picture( dir || Char( (picno) + 1e6 ) || ".png" );
Wait( .01 );
);
Entrambi gli esempi precedenti potrebbero essere costruiti altrettanto facilmente (forse più facilmente) con righe di una tabella di dati per rappresentare oggetti e colonne per rappresentare variabili membro e funzioni utente JSL per i metodi. L'uso delle classi, specialmente senza alcuna sottoclasse, potrebbe rendere il tuo JSL più difficile da seguire per qualcun altro se non si sente a proprio agio con le classi in JSL.
Parlando di funzioni, anche il documento per DefineClass le menziona. Per quanto posso capire, non portano quasi nulla sul tavolo e probabilmente dovrebbero essere evitati. Penso che ogni istanza di una classe stia probabilmente ricevendo una copia separata di ogni metodo e di ogni variabile membro. Se definisci un Function() e lo memorizzi in una variabile membro, stai solo creando qualcos'altro da copiare che (forse) avrebbe potuto essere in uno spazio dei nomi più globale con una sola copia. Ma se ci sarà solo una singola (o pochissime) istanze e hai davvero bisogno di una funzione per la classe che non sia a conoscenza di nulla tranne i suoi parametri, beh forse una funzione in una classe è ciò di cui hai bisogno.
Passando all'esempio finale, eseguiamo alcune sottoclassi e osserviamo l'ultimo spazio dei nomi, super: , che ho dovuto cercare (vedi i commenti in JSL).
Esempio intuitivo : una classe base potrebbe essere un veicolo, le sottoclassi (o classi derivate) sono camion e automobili. Un camion potrebbe sostituire il metodo di navigazione della classe base con uno che sappia evitare le strade residenziali. Un'auto potrebbe sfruttare le corsie HOV. Un elenco di veicoli farà ciascuno la cosa giusta quando gli viene detto di navigare da A a B.
Define Class ha un argomento interessante: classe base(...) . Nell'esempio seguente, forma sarà la classe base e quadrato e cerchio sarà derivato dalla forma. L'esempio crea un elenco di quadrati e cerchi e li visualizza.
two squares, two circles, two outlined, two filleddue quadrati, due cerchi, due contornati, due pieni
La seguente descrizione si basa su tentativi ed errori; Credo che sia abbastanza corretto essere un modello utile per prevedere come funzionerà qualcosa.
Cosa c'è super: ?
Quando viene creata una classe derivata, JMP viene eseguito fino alla fine del file baseclass chain e torna all'inizio, creando prima le classi di base e nei propri spazi dei nomi. Gli spazi dei nomi sono collegati tra loro internamente in modo che una classe derivata possa fare riferimento a una variabile membro cercando nel proprio spazio dei nomi, quindi seguendo la catena dello spazio dei nomi fino alla classe base fino a trovare la variabile. Il super : namespace significa semplicemente saltare in avanti una volta nella catena dello spazio dei nomi senza guardare nello spazio dei nomi corrente. Ecco perché il JSL di seguito dice "non usare super: per chiamare un metodo di base se non è necessario"; il metodo di base non avrà accesso allo spazio dei nomi derivato, quindi non può chiamare una funzione derivata.
Ereditarietà multipla: se stai usando "interfacce" ed eviti nomi duplicati, potrebbe funzionare. Buona fortuna. Non sono sicuro di dove una classe base di pari livello si inserisca nella catena di spazi dei nomi nel modello sopra.
// demonstrates JMP class inheritance and overriding a method
// and calling the super class. A circle and a square class
// are derived from a shape class. Explains when to NOT use super:.
//
// I found super: in https://github.com/sassoftware/jsl-hamcrest/blob/master/Source/Core/Matchers/EqualTo.jsl
// it isn't documented anywhere else, but works great for calling base class _init_
// Also, https://github.com/sassoftware/jsl-hamcrest/blob/master/Source/Core/Utils.jsl
// shows a "self" implementation idea. This is a bad idea because it creates a circular reference
// that must be cleared before the object is lost/deleted...or it will leak.
// avoid making members and methods in base and derived classes with the same name.
// _init_, _to string_, _show_ are exceptions, but they must not try to use a derived
// class member or method. (you would not normally want to, so no problem.)
//
// avoid needless upcast (super:); it cripples the base class because there is no downcast or
// virtual mechanism. Without the upcast, a baseclass function runs with access to the
// derived class members and methods. (see square's draw method calling docolor calling record.)
//
// it appears that the derived chain is searched *after* the current class; in a two class
// (base,derived) example that inverts the search order for members and methods when a
// derived class uses "super:" to call a base class method. If the base class calls a "virtual"
// function that exists in both base and derived, it won't get the derived one if "super:" was
// used but will if no scope is used. You only need "super:" typically when you want to call
// the base class method from an overridden method of the same name. If the base class method
// isn't calling back to derived via a virtual, everything is good.
// deep details
//
// the output shows lines like this:
// record square=square(shape=(s= x=30 y=30 r=5 s=4 c=yellow f=1))
// record circle=circle(shape=(s= x=30 y=60 r=10 s=99 c=green f=0))
//
// the square and the circle both get the right answer via different
// paths. Because of way draw() is overridden by square and not by circle,
// the initial call, objects[window:i] << draw(), is calling either a
// derived member or a base member. In both cases, "this" is for the
// derived member. in square's draw(), I made an error, now commented
// out: /*super:*/docolor(), that caused docolor to run with "this"
// for the base class, not the derived class. docolor then calls record().
// all three classes have record() members. The intent is to override the
// base class version ("never seen"). The error caused the base class
// version to run for square because "this" of docolor() was for the base class.
Define Class( "shape", // base class for both shapes
m_Xcenter = .;
m_Ycenter = .;
m_radius = .;
m_sides = .;
m_color = .;
m_filled = .;
_init_ = Method( {x, y, radius, sides, color, fill},
m_Xcenter = x;
m_Ycenter = y;
m_radius = radius;
m_sides = sides;
m_color = color;
m_filled = fill;
);
_to string_ = Method( {},
"shape=(s=" ||
" x=" || Char( m_Xcenter ) || " y=" || Char( m_Ycenter ) ||
" r=" || Char( m_radius ) || " s=" || Char( m_sides ) ||
" c=" || Char( m_color ) || " f=" || Char( m_filled ) || ")"
);
_show_ = _to string_;
// the following should NOT ever get called because square and circle override it
// try changing square to call super:docolor and then you'll see this
record=method({},show("never seen, overridden by square and circle"));
// both shapes understand filled vs outlined
// see note in square:draw method about calling docolor without super:
docolor = Method( {},
this:record(); // this: doesn't change anything,
If( this:m_filled, // it is mostly for disambiguating
Fill Color( /*this:*/m_color );// parameter of same name
, //
Pen Color( this:m_color )
)
);
// the base class has a draw method that circle uses;
// square provides an override because this draws a
// diamond-oriented square
draw = Method( {},
docolor();
p = J( m_sides, 3, 2 );//2==draw
p[1, 3] = 1;//1==move
p[m_sides, 3] = -2;//-2==draw and close
For( i = 1, i <= m_sides, i += 1,
p[i, 1] = m_Xcenter + m_radius * Cos( 2 * Pi() * i / m_sides );
p[i, 2] = m_Ycenter + m_radius * Sin( 2 * Pi() * i / m_sides );
);
Path( p, m_filled );
);
);
Define Class( "circle", baseclass( shape ),
_init_ = Method( {x, y, radius, color, fill},
super:_init_( x, y, radius, 99/*sides*/, color, fill )
);
_to string_ = Method( {},
"circle(" || super:_tostring_() || ")"
);
_show_ = _to string_;
record=method({},write("\!nrecord circle=",_show_()));
// circle has no override for draw()
);
Define Class( "square", baseclass( shape ),
_init_ = Method( {x, y, radius, color, fill},
super:_init_( x, y, radius, 4/*sides*/, color, fill )
);
_to string_ = Method( {},
"square(" || super:_tostring_() || ")"
);
_show_ = _to string_;
record=method({},write("\!nrecord square=",_show_()));
// square overrides the base class draw
draw = Method( {},
/*super:*/docolor();// don't up-cast, docolor can't down-cast and gets base class record()
Rect(
m_Xcenter - m_radius,
m_Ycenter - m_radius,
m_Xcenter + m_radius,
m_Ycenter + m_radius,
m_filled
);
);
);
objects = {};
Insert Into( objects, New Object( square( 30, 30, 5, "yellow", 1 ) ) );
Insert Into( objects, New Object( Circle( 30, 60, 10, "green", 0 ) ) );
Insert Into( objects, New Object( square( 60, 30, 15, "blue", 0 ) ) );
Insert Into( objects, New Object( Circle( 60, 60, 20, "red", 1 ) ) );
New Window( "objects",
gb = Graph Box( framesize( 500, 500 ),
For( window:i = 1, window:i <= N Items( objects ), window:i += 1,
//Show( objects[i] );
objects[window:i] << draw(); // uses the base class or the derived class draw
)
)
);
//stop();
// animate the color of object 4, the big red circle
For( i = 0, i <= 1, i += (1 / 256), //show(i);
objects[4]:m_color = HLS Color( i, .5, 1 );
gb << inval << updatewindow;
Wait( 0 );
);
Che dire questo ?
C++ ha un valore chiamato "this" (altri linguaggi usano "self" e "me") che a volte è necessario affinché un oggetto faccia riferimento a se stesso. Non è uguale a questo: prefisso dello spazio dei nomi. Non penso che la classe JSL offra un "questo" all'interno della classe, e potresti essere tentato di crearne uno come questo:
x = newObject(myClass(17));
x::self = x;
o qualcosa di simile. Quindi un metodo myClass potrebbe usare "self" per fare cose che richiedono un'istanza di classe, come la clonazione.
Scripting index showing messages that can be sent to a class instanceIndice di scripting che mostra i messaggi che possono essere inviati a un'istanza di classe
Mantenere una variabile membro in un'istanza di classe che contiene l'istanza di classe (puntatore a) può creare una perdita di memoria. Questo ci porta a un nuovo argomento... argomenti in realtà.
Quando viene eliminata un'istanza?
Un'istanza viene eliminata quando non ci sono riferimenti ad essa. Nella sezione precedente, un riferimento "self" all'interno di un'istanza impedirà l'eliminazione dell'istanza, anche se hai perso tutti i tuoi handle per l'istanza . Non c'è modo di ripulirlo, se non chiudendo JMP.
Quando viene copiata un'istanza?
Ecco la parte migliore: un'istanza viene copiata solo se usi il metodo <Ciò significa che le istanze vengono passate per riferimento.Sìì! Molto meno goffo creare una classe semplice per avvolgere una matrice di grandi dimensioni e passare un'istanza della classe a una funzione utente, invece di fare in modo che JMP crei una copia dell'argomento della matrice nel valore del parametro e possibilmente restituisca un'altra copia.