Al final de Cómo usar Definir clase Insinué que había algo interesante sobre las instancias de clase JSL que eran difíciles de copiar. si X se refiere a una instancia, Y = X hace que Y y X referir al mismo ejemplo. Lo mismo sucede cuando pasa una instancia a una función definida por el usuario: el valor de la función es no una copia de la instancia; eso se refiere al mismo ejemplo. Entonces, cambiar la instancia dentro de la función también la cambia para la persona que llama. Si alguna vez pasó una matriz a una función y la modificó en la función, esperando que la persona que llama Copiar para actualizarse, sabe que JSL pasa argumentos a las funciones de usuario por valor.
JMP pasa argumentos haciendo copias, pero ...
Observe que la matriz se copia, por lo que los cambios en la matriz dentro de la función se realizan en la copia, no en el original. Observe que el punto de referencia negro grande también se copia, pero no el objeto al que hace referencia. Esto causa muchos dolores de cabeza a los programadores principiantes de JAVA porque no han visto esta imagen. Dentro de la función, puede hacer dos cosas muy diferentes para variar una .
- puede llamar a un método, como a: set (42);
- puede asignar un valor, como a = 42;
En el caso (1) cambia el argyle valor del objeto de 3 a 42. Esto es probablemente lo que quieres hacer. en el caso (2), sobrescribe el gran punto negro al lado de una con 42. Probablemente no quisiste hacer eso.
El siguiente código usa objetos que mantienen referencias a otros objetos. Todos los objetos tienen un método next (), por lo que los objetos se pueden componer de formas interesantes. El método next () produce el siguiente valor en una secuencia de valores. Algunos objetos representan una constante y siempre producen el mismo valor siguiente. Otros, como el objeto agregar, tienen otros dos objetos (el lado izquierdo y el lado derecho del operador +). Agregar: next () llama a la izquierda: next () y a la derecha: next () y agrega las dos respuestas.
Se adjunta el JSL completo. Aquí está con las imágenes entrelazadas. Comencemos con la clase más 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 es el nombre de la clase y envuelve un valor, ya sea un número o una matriz. Tiene un setter que rechazará los valores que no aprecia y un getter, llamado next (), para devolver el siguiente valor en una secuencia. Debido a que es un poco feo escribir NewObject (Vwrap (42)), también hagamos una función auxiliar para hacer 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 (...) se llamará mucho, a veces con una instancia de clase, a veces con un escalar o matriz, y debe devolver la instancia de clase sin cambios cuando la obtenga. También podría comprobar que hay un next () en la clase al mismo tiempo, en caso de que haya otras clases que puedan confundirse.
A continuación, una función de placa de caldera en gran parte para hacer un gráfico.
// 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>Puede ver las llamadas a v () para los parámetros X e Y; esos pueden ser números simples que se incluirán u otras clases con un método next (). Luego, la función J () crea matrices de valores N, se crea una tabla invisible y se ejecuta Graph Builder, con un poco de magia para cerrar la tabla oculta cuando ya no es necesaria. Vamos a intentarlo.
gr( 13, 42, "Value 13,42", 1 );
Un solo punto, en x = 13, y = 42
Espera, mejorará ... Hagamos una clase de contador que arroje 0,1,2,3, ... en llamadas sucesivas 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
);
);El método _init_ toma una tasa que determina qué tan rápido aumenta el contador. La velocidad puede ser una constante, generalmente 1, o puede ser otra instancia de clase. Aquí hay una función auxiliar para hacer un contador y un primer gráfico.
// global function to make a counter
ctr = Function( {rate = 1},
New Object( counter( rate ) )
);
gr( ctr(), ctr() , "count up A (equal steps)", 5 );Observe que dos instancias de contador separadas se pasaron a gr (...) para los ejes X e Y.
Dos contadores, que se ejecutan en paralelo, para cinco pasos que comienzan en cero.
Ahora, para algo diferente: hagamos que la tasa del contador sea otro contador.
// this variation begins to show the power of composition
gr( ctr(), ctr( ctr() ), "count up B (first step is 0)", 5 );
El primer paso es cero porque el primer resultado del contador de tasas es cero.
El eje Y de este gráfico utiliza la composición funcional de dos contadores. Agreguemos una clase sin ().
// sin() wrapper class
Define Class(
"sin",
m_rate = .; // 0<STEP><PI. m_amp=".;"></PI.></STEP>
Sin produce una onda sinusoidal infinita a una frecuencia y amplitud determinada por constantes u otras instancias de clase. También mantiene una posición interna. Puede decir por los comentarios que podría necesitar más reflexión, pero funciona para esta demostración. Aquí hay una forma de onda de ciclo único.
gr( ctr(), New Object( Sin( 2 * Pi() / 100, 3 ) ), "sin()", 101 );
Ciclo único de onda sinusoidal
Similar al contador con un incremento de otro contador, una onda sinusoidal puede tener una frecuencia variable.
gr( ctr(), New Object( Sin( New Object( Sin( .0002, 1 ) ), 3 ) ), "variable frequency", 1000 );
Incrementando la frecuencia usando el primer bit de otra onda sinusoidal
Y dos ondas sinusoidales de frecuencia diferentes se pueden trazar una contra la otra.
gr( New Object( Sin( 4 * Pi() / 99, 3 ) ), New Object( Sin( 6 * Pi() / 99, 3 ) ), "2X3 sin()", 100 );
Las relaciones enteras simples hacen buenos patrones
Otra clase y función auxiliar; la clase agrega una o más clases juntas y el asistente de desplazamiento agrega una constante ... u otra función, probablemente ... no probada.
// 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 el asistente de desplazamiento, uno de los primeros gráficos que tenía un paso cero inicial puede modificarse para tener un paso inicial de 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 );
Ahora todos los pasos son uno más grande que antes.
// 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 );
La suma de armónicos impares a amplitud reducida se aproxima a una onda cuadrada.
// 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 );
Más armónicos ayudan a cuadrarlo
// 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 );
Multiplicar dos frecuencias; La frecuencia más baja crea la envolvente.
// 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 );
Alternativamente, controle la amplitud de la frecuencia más alta usando la frecuencia más baja.
Finalmente, haga una vwrap que contenga una matriz de frecuencias armónicas impares y cree una nueva clase para sumar la matriz en un escalar.
// 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 );
Se necesitarán muchos más armónicos para reducir realmente el exceso de disparos y las ondas.
Buen documento: https://www.jmp.com/support/help/en/15.1/index.shtml#page/jmp/classes.shtml