Au bout du Comment utiliser Définir une classe J'ai laissé entendre qu'il y avait quelque chose d'intéressant dans le fait que les instances de classe JSL étaient difficiles à copier. si X fait référence à une instance, Y=X fait à la fois Y et X se référer à au même exemple. La même chose se produit lorsque vous passez une instance à une fonction définie par l'utilisateur : la valeur de la fonction est pas une copie de l'instance ; il fait référence au même exemple. Ainsi, la modification de l'instance à l'intérieur de la fonction la modifie également pour l'appelant. Si vous avez déjà passé une matrice à une fonction et l'avez modifiée dans la fonction, en attendant que l'appelant copie pour être mis à jour, vous savez que JSL transmet les arguments aux fonctions utilisateur par valeur.
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 passe les arguments en faisant des copies, mais...
Notez que la matrice est copiée, donc les modifications apportées à la matrice à l'intérieur de la fonction sont apportées à la copie, pas à l'original. Notez que le gros point de référence noir est également copié, mais pas l'objet auquel il se réfère. Cela cause beaucoup de maux de tête aux programmeurs JAVA débutants parce qu'ils n'ont pas vu cette image. Dans la fonction, vous pouvez faire deux choses très différentes pour variable un .
- vous pouvez appeler une méthode, comme a:set(42);
- vous pouvez attribuer une valeur, comme a=42 ;
Dans le cas (1) vous modifiez le losanges valeur de l'objet de 3 à 42. C'est probablement ce que vous voulez faire. dans le cas (2), vous écrasez le gros point noir à côté de un avec 42. Vous ne vouliez probablement pas faire ça.
Le code suivant utilise des objets qui conservent des références à d'autres objets. Tous les objets ont une méthode next(), de sorte que les objets peuvent être composés de manière intéressante. La méthode next() produit la valeur suivante dans une séquence de valeurs. Certains objets représentent une constante et produisent toujours la même valeur suivante. D'autres, comme l'objet add, ont deux autres objets (le côté gauche et le côté droit de l'opérateur +). Add:next() appelle left:next() et right:next() et additionne les deux réponses.
Le JSL complet est joint. Le voici avec les images entrelacées. Commençons par la classe la plus simple.
// 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 est le nom de la classe, et il encapsule une valeur, soit un nombre, soit une matrice. Il a un setter qui rejettera les valeurs qu'il n'apprécie pas et un getter, nommé next(), pour renvoyer la valeur suivante dans une séquence. Comme il est un peu moche d'écrire NewObject(Vwrap(42)), créons également une fonction d'assistance pour créer des 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(...) sera souvent appelé, parfois avec une instance de classe, parfois avec un scalaire ou une matrice, et il doit renvoyer l'instance de classe inchangée lorsqu'il en obtient une. Il pourrait tout aussi bien vérifier qu'il y a un next() dans la classe en même temps, juste au cas où il y aurait d'autres classes qui pourraient être mélangées.
Ensuite, une fonction de plaque de chaudière en grande partie pour faire un graphique.
// 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>Vous pouvez voir les appels à v() pour les paramètres X et Y ; ceux-ci peuvent être de simples nombres qui seront enveloppés, ou d'autres classes avec une méthode next(). Ensuite, la fonction J() crée des matrices de N valeurs, et une table invisible est créée, et Graph Builder s'exécute, avec un peu de magie pour fermer la table cachée lorsqu'elle n'est plus nécessaire. Essayons.
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=42Un seul point, à x=13, y=42
Attendez, ça ira mieux... Créons une classe compteur qui renverra 0,1,2,3,... sur des appels successifs à 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
);
);La méthode _init_ prend un taux qui détermine la vitesse à laquelle le compteur s'incrémente. Rate peut être une constante, généralement 1, ou il peut s'agir d'une autre instance de classe. Voici une fonction d'assistance pour faire un compteur, et un premier graphique.
// global function to make a counter
ctr = Function( {rate = 1},
New Object( counter( rate ) )
);
gr( ctr(), ctr() , "count up A (equal steps)", 5 );Notez que deux instances de compteur distinctes ont été transmises à gr(...) pour les axes X et Y.
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.Deux compteurs, fonctionnant en parallèle, pour cinq étapes commençant à zéro.
Maintenant, pour quelque chose de différent : faisons en sorte que le taux du compteur soit un autre compteur.
// 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.La première étape est zéro car le premier résultat du compteur de taux est zéro.
L'axe Y de ce graphique utilise la composition fonctionnelle de deux compteurs. Ajoutons une classe sin().
// sin() wrapper class
Define Class(
"sin",
m_rate = .; // 0<STEP><PI. m_amp=".;"> Sin( m_pos += m_rate:next() ) // return
);
);</PI.></STEP>
Sin produit une onde sinusoïdale infinie à une fréquence et une amplitude déterminées par des constantes ou d'autres instances de classe. Il garde également une position interne. Vous pouvez dire d'après les commentaires que cela pourrait nécessiter plus de réflexion, mais cela fonctionne pour cette démo. Voici une forme d'onde à cycle unique.
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 waveCycle unique d'onde sinusoïdale
Semblable au compteur avec un incrément d'un autre compteur, une onde sinusoïdale peut avoir une fréquence variable.
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 waveAugmenter la fréquence en utilisant le premier bit d'une autre onde sinusoïdale
Et deux ondes sinusoïdales de fréquences différentes peuvent être tracées l'une par rapport à l'autre.
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 patternsLes rapports entiers simples font de beaux motifs
Une autre classe et une fonction d'assistance ; la classe ajoute une ou plusieurs autres classes ensemble et l'assistant de décalage ajoute une constante... ou une autre fonction, probablement... non testée.
// 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))
);Avec l'assistant de décalage, l'un des premiers graphiques qui avait un pas initial de zéro peut être retravaillé pour avoir un pas initial de un.
// 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.Maintenant, toutes les étapes sont plus grandes qu'avant.
// 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.La sommation des harmoniques impairs à amplitude réduite se rapproche d'une onde carrée.
// 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 upPlus d'harmoniques aident à la mise en place
// 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.Multipliez deux fréquences; la fréquence inférieure crée l'enveloppe.
// 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.Vous pouvez également contrôler l'amplitude de la fréquence supérieure à l'aide de la fréquence inférieure.
Enfin, créez un vwrap contenant une matrice de fréquences harmoniques impaires et créez une nouvelle classe pour additionner la matrice en un scalaire.
// 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.Il faudra beaucoup plus d'harmoniques pour vraiment réduire le dépassement et les ondulations.
Bon doc : https://www.jmp.com/support/help/en/15.1/index.shtml#page/jmp/classes.shtml