Alla fine di Come usare Definisci classe Ho suggerito che c'era qualcosa di interessante nel fatto che le istanze di classe JSL fossero difficili da copiare. se X fa riferimento a un'istanza, Y = X crea sia Y che X fare riferimento al stesso esempio. La stessa cosa accade quando si passa un'istanza a una funzione definita dall'utente: il valore della funzione è non una copia dell'istanza; esso si riferisce al stesso esempio. Quindi la modifica dell'istanza all'interno della funzione la modifica anche per il chiamante. Se hai mai passato una matrice a una funzione e l'hai modificata nella funzione, aspettandoti il chiamante copia per essere aggiornato, sai che JSL passa argomenti alle funzioni utente in base al valore.
JMP trasmette argomenti facendo copie, ma ...
Si noti che la matrice viene copiata, quindi le modifiche alla matrice all'interno della funzione vengono apportate alla copia, non all'originale. Si noti che viene copiato anche il grande punto di riferimento nero, ma non l'oggetto a cui si riferisce. Ciò causa all'inizio ai programmatori JAVA molti mal di testa perché non hanno visto questa immagine. All'interno della funzione, puoi fare due cose molto diverse da variabili un' .
- puoi chiamare un metodo, come un: set (42);
- puoi assegnare un valore, come a = 42;
Nel caso (1) si cambia il argyle valore dell'oggetto da 3 a 42. Questo è probabilmente ciò che intendi fare. nel caso (2), sovrascrivi il grande punto nero accanto a un' con 42. Probabilmente non intendevi farlo.
Il codice seguente utilizza oggetti che mantengono riferimenti ad altri oggetti. Tutti gli oggetti hanno un metodo next (), quindi gli oggetti possono essere composti in modi interessanti. Il metodo next () produce il valore successivo in una sequenza di valori. Alcuni oggetti rappresentano una costante e producono sempre lo stesso valore successivo. Altri, come l'oggetto add, hanno altri due oggetti (il lato sinistro e il lato destro dell'operatore +). Aggiungi: next () chiama left: next () e right: next () e aggiunge le due risposte.
La JSL completa è allegata. Eccolo qui con le immagini interlacciate. Cominciamo con la classe più semplice.
// number/matrix wrapper class
Define Class(
"vwrap",
m_v = .; // holds the number or matrix
_init_ = Method( {v},
If( Type( v ) == "Class",
Throw( "vwrap is for number or matrix, not class " || Char( v << getname ) )
);
m_v = v;
);
set = Method( {v},
If( Type( v ) == "Class",
Throw( "vwrap is for number or matrix, not class " || Char( v << getname ) )
);
m_v = v;
);
next = Method( {},
m_v; // return
);
);Vwrap è il nome della classe e racchiude un valore, un numero o una matrice. Ha un setter che rifiuterà i valori che non apprezza e un getter, chiamato next (), per restituire il valore successivo in una sequenza. Poiché è un po 'brutto scrivere NewObject (Vwrap (42)), facciamo anche una funzione di aiuto per creare Vwraps.
// helper function v(...) converts a scalar or matrix to a vwrap, or returns an instance unchanged
v = Function( {x},
If( Type( x ) == "Class",
If( !(x << Contains( "next" )),
Throw( "v expects number, matrix, or one of the classes it supports, not class " || (x << getname) )
);
x; // x supports a next function, it is probably OK
, // else
If( !Is Number( x ) & !Is Matrix( x ),
Throw( "v expects number or matrix, not " || Type( x ) )
);
New Object( vwrap( x ) ); // wrap the number or matrix
)
);v (...) verrà chiamato molto, a volte con un'istanza di classe, a volte con uno scalare o una matrice, e deve restituire invariata l'istanza di classe quando ne ottiene una. Potrebbe anche verificare che ci sia next () nella classe allo stesso tempo, nel caso in cui ci siano altre classi che potrebbero confondersi.
Successivamente, una funzione della piastra della caldaia in gran parte per creare un grafico.
// global function gr(...) makes a graph
gr = Function( {Xvar, Yvar, title, n},
{Xs, Ys, dt}, // locals
Yvar = v( Yvar );
Xvar = v( Xvar );
Ys = J( n, 1, Yvar:next() );
Xs = J( n, 1, Xvar:next() );
dt = As Table( Ys || Xs, <<INVISIBLE></INVISIBLE>Puoi vedere le chiamate a v () per i parametri X e Y; quelli possono essere numeri semplici che verranno racchiusi o altre classi con un metodo next (). Quindi la funzione J () crea matrici di valori N, viene creata una tabella invisibile e viene eseguito Graph Builder, con un po 'di magia per chiudere la tabella nascosta quando non è più necessaria. Proviamolo.
gr( 13, 42, "Value 13,42", 1 );
Un singolo punto, in x = 13, y = 42
Aspetta, andrà meglio ... Facciamo una controclasse che restituirà 0,1,2,3, ... su chiamate successive a next ().
// counter class
Define Class(
"counter",
m_rate = .; // non zero increment
m_pos = empty(); // internal position
_init_ = Method( {rate},
m_rate = v( rate );
);
next = Method( {},
rate = m_rate:next();
// see peek() comment in "sin" class
If( Is Empty( m_pos ),
result = 0 * rate; // get scalar or matrix dimension on first call
m_pos = rate; // for next time
, // else
result = m_pos; // from last tim
m_pos += rate; // for next time
);
result; // return
);
);Il metodo _init_ prende una velocità che determina la velocità con cui il contatore aumenta. La frequenza può essere una costante, in genere 1, oppure può essere un'altra istanza di classe. Ecco una funzione di aiuto per creare un contatore e un primo grafico.
// global function to make a counter
ctr = Function( {rate = 1},
New Object( counter( rate ) )
);
gr( ctr(), ctr() , "count up A (equal steps)", 5 );Si noti che due istanze di contatore separate sono state passate a gr (...) per gli assi X e Y.
Due contatori, che corrono in parallelo, per cinque passi a partire da zero.
Ora, per qualcosa di diverso: facciamo in modo che il tasso del contatore sia un altro contatore.
// this variation begins to show the power of composition
gr( ctr(), ctr( ctr() ), "count up B (first step is 0)", 5 );
Il primo passo è zero perché il primo risultato del contatore della velocità è zero.
L'asse Y di questo grafico utilizza la composizione funzionale di due contatori. Aggiungiamo una classe sin ().
// sin() wrapper class
Define Class(
"sin",
m_rate = .; // 0<STEP><PI. m_amp=".;"></PI.></STEP>
Il peccato produce un'onda sinusoidale infinita con una frequenza e un'ampiezza determinate da costanti o altre istanze di classe. Mantiene anche una posizione interna. Dai commenti puoi dire che potrebbe aver bisogno di più pensiero, ma funziona per questa demo. Ecco una forma d'onda a ciclo singolo.
gr( ctr(), New Object( Sin( 2 * Pi() / 100, 3 ) ), "sin()", 101 );
Ciclo singolo di onda sinusoidale
Simile al contatore con un incremento di un altro contatore, un'onda sinusoidale può avere una frequenza variabile.
gr( ctr(), New Object( Sin( New Object( Sin( .0002, 1 ) ), 3 ) ), "variable frequency", 1000 );
Aumentare la frequenza usando il primo bit di un'altra onda sinusoidale
E due onde sinusoidali a frequenza diversa possono essere tracciate una contro l'altra.
gr( New Object( Sin( 4 * Pi() / 99, 3 ) ), New Object( Sin( 6 * Pi() / 99, 3 ) ), "2X3 sin()", 100 );
Rapporti interi semplici creano modelli piacevoli
Un'altra funzione di classe e aiuto; la classe aggiunge una o più altre classi insieme e l'helper offset aggiunge una costante ... o un'altra funzione, probabilmente ... non testata.
// add class
Define Class(
"add",
m_a = {};
//m_b = .;
_init_ = Method( {a, b = Empty()},
If( Is Empty( b ),
For( i = 1, i <= N Items( a ), i += 1,
Insert Into( m_a, v( a[i] ) )
),
m_a[1] = v( a );
m_a[2] = v( b );
)
);
next = Method( {},
result = m_a[1]:next();
For( i = 2, i <= N Items( m_a ), i += 1,
result = result + m_a[i]:next()
);
result;
);
);
// helper function uses add to offset another value by off
offset = function({off,x},
newobject(add(off,x))
);Con l'helper offset è possibile rielaborare uno dei primi grafici con un passo zero iniziale per avere un passo iniziale di uno.
// compared to a previous example, this one increments earlier
gr( ctr(), ctr( offset(1,ctr( 1 )) ), "count up C (first step is 1)", 5 );
Ora tutti i passaggi sono uno più grande di prima.
// building a square wave by summing odd harmonics
n = 100;
basefreq = 2 * Pi() / n;
freq1 = New Object( Sin( 1 * basefreq, 1 / 1 ) );
freq2 = New Object( Sin( 3 * basefreq, 1 / 3 ) );
freq3 = New Object( Sin( 5 * basefreq, 1 / 5 ) );
freq4 = New Object( Sin( 7 * basefreq, 1 / 7 ) );
sum = New Object( Add( {freq1, freq2, freq3, freq4} ) );
gr( ctr(), sum, "4 Odd Harmonics", n + 1 );
Sommando le armoniche dispari a ampiezza ridotta approssima un'onda quadra.
// same square wave, a lot more odd harmonics
n = 200;
basefreq = 2 * Pi() / n;
freqs = {};
For( i = 1, i <= 23, i += 2,
Insert Into( freqs, New Object( Sin( i * basefreq, 1 / i ) ) )
);
gr( ctr(), New Object( Add( freqs ) ), "12 Odd Harmonics", n + 1 );
Più armoniche aiutano a quadrare
// multiply class
Define Class(
"multiply",
m_a = {};
_init_ = Method( {a, b = Empty()},
If( Is Empty( b ),
For( i = 1, i <= N Items( a ), i += 1,
Insert Into( m_a, v( a[i] ) )
),
m_a[1] = v( a );
m_a[2] = v( b );
)
);
next = Method( {},
result = m_a[1]:next();
For( i = 2, i <= N Items( m_a ), i += 1,
result = result :*</img> m_a[i]:next()
);
result;
);
);
// multiply two waveforms
n = 1000;
freq1 = New Object( Sin( .1, 1 ) );
freq2 = New Object( Sin( 2 * Pi() / n, 1 ) );
prod = New Object( Multiply( freq1, freq2 ) );
gr( ctr(), prod, "multiply", n + 1 );
Moltiplica due frequenze; la frequenza più bassa crea l'inviluppo.
// similar, but control amplitude of freq1 using another sin wave
n = 1000;
freq2 = New Object( Sin( 2 * Pi() / n, 1 ) ); // lo freq
freq1 = New Object( Sin( .1, freq2 ) ); // hi freq, control amplitude with freq2
gr( ctr(), freq1, "amp is sin", n + 1 );
In alternativa, controlla l'ampiezza della frequenza più alta usando la frequenza più bassa.
Infine, crea un vwrap con una matrice di frequenze armoniche dispari e crea una nuova classe per sommare la matrice in uno scalare.
// try a matrix...use v(...) to make a vwrap to hold the matrix
n = 200;
mat = v( ((1 :: 49 :: 2) * (2 * Pi() / n)) );
// add the sin wrapper. sinmat:next() returns a row vector of 5 sin values
sinmat = New Object( Sin( mat, 1 / (1 :: 49 :: 2) ) );
// matrixSum assumes the argument class is returning a matrix and returns the scalar sum
Define Class(
"matrixSum",
m_mat = .;
_init_ = Method( {mat},
m_mat = v( mat )
);
next = Method( {},
Sum( m_mat:next() )
);
);
gr( ctr(), New Object( matrixSum( sinmat ) ), "25 Odd Harmonics", n + 1 );
Ci vorranno molte più armoniche per ridurre davvero gli over-shoot e le increspature.
Buon doc: https://www.jmp.com/support/help/en/15.1/index.shtml#page/jmp/classes.shtml