Am Ende von So verwenden Sie „Klasse definieren“. Ich habe angedeutet, dass es etwas Interessantes daran gibt, dass JSL-Klasseninstanzen schwer zu kopieren sind. Wenn sich X auf eine Instanz bezieht, ergibt Y=X sowohl Y als auch X verweisen zum Dasselbe Beispiel. Das Gleiche passiert, wenn Sie eine Instanz an eine benutzerdefinierte Funktion übergeben: Der Wert der Funktion ist nicht eine Kopie der Instanz; Es verweist zum Dasselbe Beispiel. Wenn Sie also die Instanz innerhalb der Funktion ändern, ändert sich diese auch für den Aufrufer. Wenn Sie jemals eine Matrix an eine Funktion übergeben und diese 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 passes arguments by making copies, but...JMP passes arguments by making copies, but...JMP passes arguments by making copies, but...JMP passes arguments by making copies, but...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 und nicht am Original vorgenommen werden. Beachten Sie, dass der große schwarze Referenzpunkt ebenfalls kopiert wird, jedoch nicht das Objekt, auf das er verweist. Dies bereitet Anfängern in der JAVA-Programmierung viele Kopfschmerzen, da sie dieses Bild noch nicht gesehen haben. Innerhalb der Funktion können Sie mit Variablen zwei sehr unterschiedliche Dinge tun A .
- Sie können eine Methode aufrufen, z. B. a:set(42);
- Sie können einen Wert zuweisen, z. B. a=42;
Im Fall (1) ändern Sie die Argyle Objektwert von 3 bis 42. Das ist wahrscheinlich das, was Sie vorhaben. Im Fall (2) überschreiben Sie den großen schwarzen Punkt daneben A mit 42. Das wolltest du wahrscheinlich nicht tun.
Der folgende Code verwendet Objekte, die Verweise auf andere Objekte behalten. Alle Objekte verfügen über eine next()-Methode, sodass die Objekte auf interessante Weise zusammengesetzt werden können. Die Methode next() erzeugt den nächsten Wert in einer Wertefolge. Manche Objekte stellen eine Konstante dar und erzeugen immer den gleichen nächsten Wert. Andere, wie das Add-Objekt, haben zwei weitere Objekte (die linke und rechte Seite des +-Operators). Add:next() ruft left:next() und right:next() auf und fügt die beiden Antworten hinzu.
Die vollständige JSL ist beigefügt. Hier ist es mit den verschachtelten Bildern. 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 verfügt über einen Setter, der Werte ablehnt, die er nicht akzeptiert, 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 oft aufgerufen, manchmal mit einer Klasseninstanz, manchmal mit einem Skalar oder einer Matrix, und es muss die Klasseninstanz unverändert zurückgeben, wenn es eine erhält. Es könnte genauso gut überprüft werden, ob gleichzeitig ein next() in der Klasse vorhanden ist, nur für den Fall, dass es andere Klassen gibt, die durcheinander geraten könnten.
Als nächstes folgt eine weitgehend einfache Funktion zum Erstellen eines Diagramms.
// 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, erstellt eine unsichtbare Tabelle und führt den Graph Builder aus, mit etwas Magie, um die verborgene Tabelle zu schließen, wenn sie nicht mehr benötigt wird. Lass es uns versuchen.
gr( 13, 42, "Value 13,42", 1 );
A single point, at x=13, y=42A single point, at x=13, y=42A single point, at x=13, y=42A single point, at x=13, y=42Ein einzelner Punkt bei x=13, y=42
Moment, es wird besser ... Erstellen wir eine Zählerklasse, die bei aufeinanderfolgenden Aufrufen von next() 0,1,2,3,... zurückgibt.
// 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 _init_-Methode verwendet eine Rate, die bestimmt, wie schnell der Zähler erhöht wird. 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.
Two counters, running in parallel, for five steps starting at zero.Two counters, running in parallel, for five steps starting at zero.Two counters, running in parallel, for five steps starting at zero.Two counters, running in parallel, for five steps starting at zero.Zwei parallel laufende Zähler für fünf Schritte beginnend bei 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 );
The first step is zero because the rate counter's first result is zero.The first step is zero because the rate counter's first result is zero.The first step is zero because the rate counter's first result is zero.The first step is zero because the rate counter's first result is zero.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=".;"> Sin( m_pos += m_rate:next() ) // return
);
);</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 bei. Den Kommentaren kann man entnehmen, dass vielleicht mehr Überlegung nötig ist, aber für diese Demo funktioniert es. Hier ist eine Wellenform mit einem einzelnen Zyklus.
gr( ctr(), New Object( Sin( 2 * Pi() / 100, 3 ) ), "sin()", 101 );
Single cycle of sine waveSingle cycle of sine waveSingle cycle of sine waveSingle cycle of sine waveEinzelner Zyklus einer 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 );
Ramping up the frequency using the first bit of another sine waveRamping up the frequency using the first bit of another sine waveRamping up the frequency using the first bit of another sine waveRamping up the frequency using the first bit of another sine waveErhöhen der Frequenz mithilfe des ersten Bits einer anderen Sinuswelle
Und es können zwei Sinuswellen unterschiedlicher Frequenz gegeneinander aufgetragen werden.
gr( New Object( Sin( 4 * Pi() / 99, 3 ) ), New Object( Sin( 6 * Pi() / 99, 3 ) ), "2X3 sin()", 100 );
Simple integer ratios make nice patternsSimple integer ratios make nice patternsSimple integer ratios make nice patternsSimple integer ratios make nice patternsEinfache ganzzahlige Verhältnisse ergeben schöne Muster
Eine weitere Klasse 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 einer der frühen Graphen, der anfänglich einen Nullschritt hatte, so überarbeitet werden, dass er einen anfänglichen Schritt von Eins hat.
// compared to a previous example, this one increments earlier
gr( ctr(), ctr( offset(1,ctr( 1 )) ), "count up C (first step is 1)", 5 );
Now all the steps are one bigger than before.Now all the steps are one bigger than before.Now all the steps are one bigger than before.Now all the steps are one bigger than before.Jetzt sind alle Schritte eins 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 );
Summing odd harmonics at reduced amplitude approximates a square wave.Summing odd harmonics at reduced amplitude approximates a square wave.Summing odd harmonics at reduced amplitude approximates a square wave.Summing odd harmonics at reduced amplitude approximates a square wave.Durch die Summierung ungerader Harmonischer mit reduzierter Amplitude entsteht annähernd eine Rechteckwelle.
// 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 );
More harmonics help square it upMore harmonics help square it upMore harmonics help square it upMore harmonics help square it upMehr Harmonische helfen, das Ganze in Ordnung zu bringen
// 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 );
Multiply two frequencies; the lower frequency creates the envelope.Multiply two frequencies; the lower frequency creates the envelope.Multiply two frequencies; the lower frequency creates the envelope.Multiply two frequencies; the lower frequency creates the envelope.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 );
Alternatively, control the amplitude of the higher frequency using the lower frequency.Alternatively, control the amplitude of the higher frequency using the lower frequency.Alternatively, control the amplitude of the higher frequency using the lower frequency.Alternatively, control the amplitude of the higher frequency using the lower frequency.Alternativ können Sie die Amplitude der höheren Frequenz über die niedrigere Frequenz steuern.
Erstellen Sie abschließend einen vwrap, der eine Matrix ungerader harmonischer Frequenzen enthält, und erstellen Sie eine neue Klasse, um die Matrix zu einem Skalar zu summieren.
// 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 );
It will take a lot more harmonics to really reduce the over-shoot and the ripples.It will take a lot more harmonics to really reduce the over-shoot and the ripples.It will take a lot more harmonics to really reduce the over-shoot and the ripples.It will take a lot more harmonics to really reduce the over-shoot and the ripples.Es werden viel mehr Harmonische benötigt, um das Überschwingen und die Wellen wirklich zu reduzieren.
Gutes Dokument: https://www.jmp.com/support/help/en/15.1/index.shtml#page/jmp/classes.shtml