al final de Cómo usar Definir clase Insinué que había algo interesante en el hecho de que las instancias de la clase JSL fueran difíciles de copiar. si X se refiere a una instancia, Y=X hace tanto a Y como a X referirse hacia mismo instancia. 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 hacia mismo instancia. 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 ser actualizado, sabe que JSL pasa argumentos a las funciones de usuario por valor.
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 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 gran punto de referencia negro también se copia, pero no el objeto al que se refiere. Esto causa muchos dolores de cabeza a los programadores principiantes de JAVA porque no han visto esta imagen. Dentro de la función, puedes hacer dos cosas muy diferentes a la variable a .
- puede llamar a un método, como a:set(42);
- puede asignar un valor, como a=42;
En caso de que (1) cambie 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 junto a a 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 add, tienen otros dos objetos (el lado izquierdo y el lado derecho del operador +). Add:next() llama a left:next() y right:next() y agrega las dos respuestas.
Se adjunta el JSL completo. Aquí está con las imágenes intercaladas. 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)), hagamos también 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 necesita devolver la instancia de clase sin cambios cuando obtiene una. También podría verificar que haya 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 ajustarán u otras clases con un método next(). Luego, la función J() crea matrices de N valores, se crea una tabla invisible y se ejecuta Graph Builder, con un poco de magia para cerrar la tabla oculta cuando ya no se necesita. Vamos a intentarlo.
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 solo punto, en x=13, y=42
Espera, mejorará... Hagamos una clase de contador que devuelva 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 se incrementa el contador. Rate puede ser una constante, típicamente 1, o puede ser otra instancia de clase. Aquí hay una función de ayuda 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 se pasaron dos instancias de contador independientes a gr(...) para los ejes X e 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.Dos contadores, funcionando en paralelo, durante cinco pasos que comienzan en cero.
Ahora, 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 );
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.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=".;"> Sin( m_pos += m_rate:next() ) // return
);
);</PI.></STEP>
Sin produce una onda sinusoidal infinita a una frecuencia y amplitud determinadas por constantes u otras instancias de clase. También mantiene una posición interna. Puede notar por los comentarios que podría necesitar más pensamiento, pero funciona para esta demostración. Aquí hay una forma de onda de un solo ciclo.
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 waveCiclo ú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 );
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 waveAumentando la frecuencia usando el primer bit de otra onda sinusoidal
Y dos ondas sinusoidales de frecuencia diferente 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 );
Simple integer ratios make nice patternsSimple integer ratios make nice patternsSimple integer ratios make nice patternsSimple integer ratios make nice patternsLas proporciones enteras simples crean buenos patrones
Otra clase y función auxiliar; la clase agrega una o más clases juntas y el ayudante 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 se puede volver a trabajar 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 );
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.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 );
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 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 );
More harmonics help square it upMore harmonics help square it upMore harmonics help square it upMore harmonics help square it upMá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 );
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.Multiplica 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 );
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.Alternativamente, controle la amplitud de la frecuencia más alta usando la frecuencia más baja.
Finalmente, haga un vwrap que contenga una matriz de frecuencias armónicas impares y haga 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 );
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.Se necesitarán muchos más armónicos para reducir realmente el sobreimpulso y las ondas.
Buen doctor: https://www.jmp.com/support/help/en/15.1/index.shtml#page/jmp/classes.shtml