Am Ende von Verwendung der Klasse definieren Ich habe angedeutet, dass JSL-Klasseninstanzen schwer zu kopieren sind. Wenn X sich auf eine Instanz bezieht, macht Y = X sowohl Y als auch X. verweisen zum gleich Beispiel. Dasselbe passiert, wenn Sie eine Instanz an eine benutzerdefinierte Funktion übergeben: Der Wert der Funktion ist nicht eine Kopie der Instanz; es verweist zum gleich Beispiel. Wenn Sie also die Instanz innerhalb der Funktion ändern, ändert sich dies auch für den Aufrufer. Wenn Sie jemals eine Matrix an eine Funktion übergeben und in der Funktion geändert haben, erwarten Sie die des Aufrufers Kopieren Um aktualisiert zu werden, wissen Sie, dass JSL Argumente nach Wert an Benutzerfunktionen übergibt.
JMP übergibt Argumente, indem es Kopien erstellt, aber ...
Beachten Sie, dass die Matrix kopiert wird, sodass Änderungen an der Matrix innerhalb der Funktion an der Kopie vorgenommen werden, nicht am Original. Beachten Sie, dass der große schwarze Referenzpunkt ebenfalls kopiert wird, jedoch nicht das Objekt, auf das er verweist. Dies verursacht beginnenden JAVA-Programmierern viele Kopfschmerzen, weil sie dieses Bild nicht gesehen haben. Innerhalb der Funktion können Sie zwei sehr unterschiedliche Dinge mit Variablen tun ein .
- Sie können eine Methode wie a aufrufen: set (42);
- Sie können einen Wert wie a = 42 zuweisen.
In Fall (1) ändern Sie die Argyle Objektwert von 3 bis 42. Dies ist wahrscheinlich, was Sie tun wollen. In Fall (2) überschreiben Sie den großen schwarzen Punkt neben ein mit 42. Das wollten Sie wahrscheinlich nicht.
Der folgende Code verwendet Objekte, die Verweise auf andere Objekte enthalten. Alle Objekte haben eine next () -Methode, sodass die Objekte auf interessante Weise zusammengesetzt werden können. Die next () -Methode erzeugt den nächsten Wert in einer Folge von Werten. Einige Objekte stellen eine Konstante dar und erzeugen immer den gleichen nächsten Wert. Andere, wie das Objekt add, haben zwei weitere Objekte (die linke und die rechte Seite des Operators +). Add: next () ruft left: next () und right: next () auf und fügt die beiden Antworten hinzu.
Die komplette JSL ist beigefügt. Hier ist es mit den Bildern verschachtelt. Beginnen wir mit der einfachsten Klasse.
// 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 ist der Name der Klasse und umschließt einen Wert, entweder eine Zahl oder eine Matrix. Es hat einen Setter, der Werte ablehnt, die er nicht schätzt, und einen Getter namens next (), der den nächsten Wert in einer Sequenz zurückgibt. Da es etwas hässlich ist, NewObject (Vwrap (42)) zu schreiben, erstellen wir auch eine Hilfsfunktion zum Erstellen von 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 (...) wird häufig aufgerufen, manchmal mit einer Klasseninstanz, manchmal mit einem Skalar oder einer Matrix, und es muss die Klasseninstanz unverändert zurückgeben, wenn sie eine erhält. Es kann genauso gut überprüft werden, ob gleichzeitig ein next () in der Klasse vorhanden ist, nur für den Fall, dass andere Klassen durcheinander geraten.
Als nächstes eine weitgehend Kesselplattenfunktion, um ein Diagramm zu erstellen.
// 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>Sie können die Aufrufe von v () für die X- und Y-Parameter sehen. Dies können einfache Zahlen sein, die umbrochen werden, oder andere Klassen mit einer next () -Methode. Dann erstellt die Funktion J () Matrizen mit N Werten, und es wird eine unsichtbare Tabelle erstellt, und der Graph Builder wird mit etwas Magie ausgeführt, um die versteckte Tabelle zu schließen, wenn sie nicht mehr benötigt wird. Lass es uns versuchen.
gr( 13, 42, "Value 13,42", 1 );
Ein einzelner Punkt bei x = 13, y = 42
Moment mal, es wird besser ... Lassen Sie uns eine Gegenklasse erstellen, die 0,1,2,3, ... zurückgibt. bei aufeinanderfolgenden Aufrufen von 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
);
);Die Methode _init_ verwendet eine Rate, die bestimmt, wie schnell der Zähler inkrementiert. Die Rate kann eine Konstante sein, normalerweise 1, oder eine andere Klasseninstanz. Hier ist eine Hilfsfunktion zum Erstellen eines Zählers und ein erstes Diagramm.
// global function to make a counter
ctr = Function( {rate = 1},
New Object( counter( rate ) )
);
gr( ctr(), ctr() , "count up A (equal steps)", 5 );Beachten Sie, dass zwei separate Zählerinstanzen für die X- und Y-Achse an gr (...) übergeben wurden.
Zwei parallel laufende Zähler für fünf Schritte ab Null.
Nun zu etwas anderem: Lassen Sie uns die Rate des Zählers zu einem anderen Zähler machen.
// this variation begins to show the power of composition
gr( ctr(), ctr( ctr() ), "count up B (first step is 0)", 5 );
Der erste Schritt ist Null, da das erste Ergebnis des Ratenzählers Null ist.
Die Y-Achse dieses Diagramms verwendet die funktionale Zusammensetzung zweier Zähler. Fügen wir eine sin () -Klasse hinzu.
// sin() wrapper class
Define Class(
"sin",
m_rate = .; // 0<STEP><PI. m_amp=".;"></PI.></STEP>
Sin erzeugt eine unendliche Sinuswelle mit einer Frequenz und Amplitude, die durch Konstanten oder andere Klasseninstanzen bestimmt werden. Es behält auch eine interne Position. Sie können den Kommentaren entnehmen, dass möglicherweise weitere Überlegungen erforderlich sind, dies funktioniert jedoch für diese Demo. Hier ist eine Wellenform mit einem Zyklus.
gr( ctr(), New Object( Sin( 2 * Pi() / 100, 3 ) ), "sin()", 101 );
Einzelner Zyklus der Sinuswelle
Ähnlich wie der Zähler mit einem Inkrement eines anderen Zählers kann eine Sinuswelle eine variable Frequenz haben.
gr( ctr(), New Object( Sin( New Object( Sin( .0002, 1 ) ), 3 ) ), "variable frequency", 1000 );
Hochfahren der Frequenz mit dem ersten Bit einer anderen Sinuswelle
Und zwei Sinuswellen mit unterschiedlichen Frequenzen können gegeneinander aufgetragen werden.
gr( New Object( Sin( 4 * Pi() / 99, 3 ) ), New Object( Sin( 6 * Pi() / 99, 3 ) ), "2X3 sin()", 100 );
Einfache ganzzahlige Verhältnisse ergeben schöne Muster
Eine andere Klassen- und Hilfsfunktion; Die Klasse fügt eine oder mehrere andere Klassen hinzu und der Offset-Helfer fügt eine Konstante hinzu ... oder eine andere Funktion, wahrscheinlich ... ungetestet.
// 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))
);Mit dem Offset-Helfer kann eines der frühen Diagramme mit einem anfänglichen Nullschritt überarbeitet werden, um einen anfänglichen Schritt von Eins zu erhalten.
// compared to a previous example, this one increments earlier
gr( ctr(), ctr( offset(1,ctr( 1 )) ), "count up C (first step is 1)", 5 );
Jetzt sind alle Schritte größer als zuvor.
// 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 );
Das Summieren ungerader Harmonischer bei reduzierter Amplitude nähert sich einer Rechteckwelle an.
// 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 );
Weitere Harmonische helfen dabei, dies auszugleichen
// 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 );
Multiplizieren Sie zwei Frequenzen; Die niedrigere Frequenz erzeugt die Hüllkurve.
// 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 );
Alternativ können Sie die Amplitude der höheren Frequenz mit der niedrigeren Frequenz steuern.
Erstellen Sie schließlich einen Vwrap mit einer Matrix ungerader harmonischer Frequenzen und erstellen Sie eine neue Klasse, um die Matrix zu einem Skalar zusammenzufassen.
// 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 );
Es werden viel mehr Harmonische benötigt, um das Überschießen und die Wellen wirklich zu reduzieren.
Guter Doc: https://www.jmp.com/support/help/en/15.1/index.shtml#page/jmp/classes.shtml