Arreglos 2

En este capítulo se resuelven algunos problemas que involucran vectores. Para ese fin se emplean las estructuras for y for-of, así como los métodos iterativos disponibles para matrices estudiados en el capítulo anterior. En general, los problemas que involucran arreglos, se resuelven de forma más sencilla (aunque no necesariamente más eficiente) empleando los métodos disponibles.

Por ejemplo, para calcular el valor de la siguiente expresión:

\[ \sum_{i=1}^{n}{v_i} \]

Es decir, calcular la sumatoria de los elementos de un vector "v", como el siguiente:

var v = [1, 2, 4, 5, 6, 8, 12, 23, 25, 42, 2, 6, 7, 3, 112];

La forma más simple consiste en llamar al método sum:

v.sum()

Igualmente (sobre todo si no se trabaja en la calculadora), se puede recurrir al método reduce:

v.reduce((s,e)=>s+e)

Como se explicó en el capítulo antetior, el método reduce ejecuta la función para cada uno de los elementos (e) del array (v) y asigna el resultado de cada iteración al primer parámetro (s) de la siguiente iteración, excepto (por supuesto) en la última iteración donde simplemente devuelve el último resultado. En la primera iteración, si no se manda un valor incicial (como ocurre en el ejemplo) asigna al primer parámetro (en el ejemplo "s") el valor del primer elemento (en el ejemplo 1) y el método comienza las iteraciones a partir del segundo elemento (en el ejemplo 2). Si se manda un valor inicial, ese valor es asignado al primer parámetro (s) y las iteraciones comienzan con el primer elemento (1).

Es posible, también, emplear el método forEach:

var s = 0;
v.forEach(e => s+=e);
s

En este caso, como forEach simplemente ejecuta su función para cada uno de los elementos del array (v), el acumulador "s" debe ser iniciado antes del método y mostrado al final del mismo.

Se puede emplear también la estructura for-of, que es una variante de la estructura for y que tiene la siguiente sintaxis:

for (e of array) instrucciones;

Donde las instrucciones son ejecutadas para cada uno de los elementos e del array. Estando el elemento e disponible en las instrucciones del ciclo.

Empleando esta estructura, la solución es similar a la del método forEach:

var s = 0;
for (let e of v) s+=e;
s

Como for-of no recibe una función, sino directamente las instrucciones, se escribe directamente la instrucción que suma, uno a uno (en cada iteración) los elementos "e" del vector.

Como ya se vio previamente, una sumatoria (o productoria) con límites definidos, puede ser traducida directamente en un ciclo for, siendo los límites del ciclo los límites de la sumatoria (o productoria) y la variable de la sumatoria (o productoria) el contador del ciclo. El acumulador (la variable que almacena el resultado) debe iniciar en 0 si es una sumatoria y en 1 si es una productoria.

Así, para calcular la sumatoria, con la estructura for, se escribe:

var s = 0;
for (let i=0; i<v.length; i++) s+=v[i];
s

Es importante recordar que, cuando se trabaja con vectores (y objetos en general), las variables son punteros, es decir variables que sólo guardan la dirección de memoria del lugar donde están almacenados los datos, pero no los datos en sí. Por esa razón, para efectivamente copiar un vector, se debe emplear el método slice (o el método copy).

Ejemplos

Promedio de un vector de números

En este ejemplo se programa un método para calcular el promedio de los valores almacenados en un vector "x", programando la expresión:

\[ \bar{x} = \dfrac{\displaystyle\sum_{i=0}^{n-1}{x_i}}{n} \]

Al ser un problema sencillo, puede ser codificado directamente (sin necesidad de elaborar un algoritmo): el contador del ciclo "i", debe comenzar en 0 e ir hasta "n-1", incrementando en 1 en cada repetición del ciclo. El acumulador que se emplee, por ejemplo "s", debe comenzar en cero y en cada repetición del ciclo se le debe añadir el valor de x[i].

Sólo para que la lógica resulte aún más clara, se elabora el diagrama de flujo respectivo:

gojsGraph({divi, modelo: {"class":"go.GraphLinksModel","linkFromPortIdProperty":"fromPort","linkToPortIdProperty":"toPort","nodeDataArray":[{"category":"Start","text":"promedio","key":-1,"loc":"172 -460"},{"category":"Input","text":"vector de números: x","key":-3,"loc":"171.99999999999994 -410.4666427612303"},{"category":"Conditional","text":"n==0 ","key":-5,"loc":"171.99999999999994 -301.6332496643064"},{"category":"Output","text":"lanzar error:\n \"vector vacío\"","key":-4,"loc":"319.0000000000004 -142.99999999999994"},{"category":"Process","text":"s = 0","key":-2,"loc":"38.85404968261713 -254.33321380615212"},{"category":"Process","text":"i = i+1","key":-7,"loc":"-44.00000000000014 -145.44982967376689"},{"category":"Start","text":"fin","key":-9,"loc":"171.99999999999986 41.65581652323428"},{"category":"Output","text":"s/n","key":-10,"loc":"38.85404968261713 -11.22199312845845"},{"category":"Process","text":"n = num. de elementos en x","key":-11,"loc":"171.99999999999994 -358.9332855224607"},{"category":"For","text":"i<n","key":-6,"loc":"38.85404968261713 -145.44982967376689"},{"category":"Process","text":"i = 0","key":-12,"loc":"38.85404968261713 -199.7998565673826"},{"category":"Process","text":"s = s+x[i]","key":-13,"loc":"38.85404968261713 -76.09980278015117"}],"linkDataArray":[{"from":-5,"to":-4,"fromPort":"R","toPort":"T","visible":true,"points":[208.15933227539057,-301.6332496643064,218.15933227539057,-301.6332496643064,319.0000000000004,-301.6332496643064,319.0000000000004,-243.42775586446112,319.0000000000004,-185.22226206461585,319.0000000000004,-175.22226206461585],"text":"Si"},{"from":-5,"to":-2,"fromPort":"L","toPort":"T","visible":true,"points":[135.84066772460932,-301.6332496643064,125.84066772460932,-301.6332496643064,38.85404968261713,-301.6332496643064,38.85404968261713,-291.61657104492167,38.85404968261713,-281.5998924255369,38.85404968261713,-271.5998924255369],"text":"No"},{"from":-4,"to":-9,"fromPort":"B","toPort":"R","points":[319.0000000000004,-119.79997131347653,319.0000000000004,-109.79997131347653,319.0000000000004,-108,319.0000000000004,-108,319.0000000000004,41.655816523234265,205.55952284071176,41.655816523234265,195.55952284071176,41.655816523234265]},{"from":-10,"to":-9,"fromPort":"B","toPort":"L","points":[38.85404968261713,1.4580212148032716,38.85404968261713,11.45802121480327,38.85404968261713,12,38.85404968261713,12,38.85404968261713,41.655816523234265,138.44047715928815,41.655816523234265,148.44047715928815,41.655816523234265]},{"from":-1,"to":-3,"fromPort":"B","toPort":"T","points":[171.99999999999994,-444.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-424.73332138061505]},{"from":-3,"to":-11,"fromPort":"B","toPort":"T","points":[171.99999999999994,-396.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-376.1999641418455]},{"from":-11,"to":-5,"fromPort":"B","toPort":"T","points":[171.99999999999994,-341.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-321.66660690307594]},{"from":-2,"to":-12,"fromPort":"B","toPort":"T","points":[38.85404968261713,-237.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-217.06653518676737]},{"from":-12,"to":-6,"fromPort":"B","toPort":"T","points":[38.85404968261713,-182.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-162.53317794799784]},{"from":-6,"to":-13,"fromPort":"B","toPort":"T","visible":true,"points":[38.85404968261713,-128.36648139953593,38.85404968261713,-118.36648139953593,38.85404968261713,-110.86648139953593,38.85404968261713,-110.86648139953593,38.85404968261713,-103.36648139953593,38.85404968261713,-93.36648139953593],"text":"Si"},{"from":-13,"to":-7,"fromPort":"L","toPort":"B","points":[2.690673828124943,-76.09980278015117,-7.309326171875057,-76.09980278015117,-44.00000000000014,-76.09980278015117,-44.00000000000014,-97.14147691726666,-44.00000000000014,-118.18315105438214,-44.00000000000014,-128.18315105438214]},{"from":-7,"to":-6,"fromPort":"R","toPort":"L","points":[-16.1634902954103,-145.44982967376689,-6.163490295410298,-145.44982967376689,-1.506393432617287,-145.44982967376689,-1.506393432617287,-145.44982967376689,3.1507034301757244,-145.44982967376689,13.150703430175724,-145.44982967376689]},{"from":-6,"to":-10,"fromPort":"R","toPort":"T","visible":true,"points":[64.55739593505854,-145.44982967376689,74.55739593505854,-144.72491483688344,106,-144.72491483688344,106,-47,38.85404968261713,-47,38.85404968261713,-38.833124160766396,38.85404968261713,-28.833124160766396],"text":"No"}]} })

La condición del ciclo es "i<n", porque las iteraciones deben repetirse mientras el contador "i" es menor a "n", porque "n" es el número de elementos del vector, pero el último elemento es n-1 (debido a que el primer índice es 0).

El código respectivo, implementado como un método getter de la clase SV, es:

var SV = class { #x; constructor(...elementos) { this.#x = elementos; } get x() { return this.#x; } get promedio() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); let s = 0; for (let i=0; i<n; i++) s+=x[i]; return s/n; } }

Observe que en el constructor se ha empleado un parámetro de tipo rest (resto), el cual se identifica escribiendo 3 puntos delante del nombre del parámetro (...elementos). Los parámetros de tipo rest deben ser o el único parámetro de la función o el último de los parámetros de la función. Un parámetro rest crea un array con todos los datos que recibe, por eso es empleado en el constructor, para que todos los datos que se pasen al mismo sean convertidos en un array.

Llamando al método con algunos valores de prueba, se obtiene:

new SV(1, 9, 12, 3, 5, 7, 12).promedio
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promedio

Observe que en el último ejemplo se ha empleado el operador spread (esparcir), que se representa con tres puntos (...), igual que el parámetro rest. Sin embargo, como operador hace lo contrario de rest: convierte un array en una secuencia de valores separados con comas. Se emplea en el ejemplo para convertir el vector "b" en una secuencia de sus elementos (la cual es convertida de nuevo en un array en la clase).

Observe también que, antes de generar el vector con números aleatorios, se ha iniciado la semilla (randseed) en 1, para que siempre se genere la misma secuencia de números pseudoaleatorios (recuerde que, para volver a generar números realmente aleatorios, se debe asignar null arandseed ).

var c = [].range(17, 51, 2); print(c); new SV(...c).promedio
var d = [].linspace(5, 10, 21); print(d); new SV(...d).promedio

El problema resuelto con la estructura for-of, es:

Object.defineProperty(SV.prototype, "promedioa", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); let s = 0; for (let e of x) s+=e; return s/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promedioa
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promedioa

El problema resuelto con el método forEach, es:

Object.defineProperty(SV.prototype, "promediob", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); let s = 0; x.forEach(e=>s+=e); return s/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promediob
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promediob

Como se vio en el anterior capítulo, además del valor del elemento ("e"), forEach manda a la función (en este caso la función flecha: e=>s+=e) el índice (la posición) del elemento actual, así como el array en sí, sin embargo, en este ejemplo, no se reciben esos valores, porque no se utilizan en la función.

El problema resuelto con el método reduce, es:

Object.defineProperty(SV.prototype, "promedioc", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); return x.reduce((s,e)=>s+e)/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promedioc
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promedioc

En este caso, reduce devuelve la sumatoria, la cual se divide entre el número de elementos para obtener el promedio.

Como sucede con casi todos los métodos iterativos disponibles para arrays, reduce, además del elemento (e), manda el índice y el array, pero como no se emplean en la función, no son recibidos.

Aunque en este caso no es necesario, se puede asignar un valor inicial a reduce (en este caso 0, pues se trata de una sumatoria):

Object.defineProperty(SV.prototype, "promediod", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); return x.reduce((s,e)=>s+e, 0)/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promediod
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promediod

Por supuesto, el resultado es el mismo, sólo que ahora el acumulador (s) comienza en 0 (en lugar del valor del primer elemento) y debido a ello, la función es ejecutada una vez más.

El problema resuelto con el método sum, es:

Object.defineProperty(SV.prototype, "promedioe", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); return x.sum()/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promedioe
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promedioe

Igualmente, el problema puede ser resuelto con el método map:

Object.defineProperty(SV.prototype, "promediof", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); s = 0; x.map(e=>s+=e); return s/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promediof
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promediof

Desde el punto de vista de la eficiencia, esta no es la mejor opción, porque map genera un nuevo array con el mismo número de elementos que el array original, sin embargo, en este ejemplo, ese array es simplemente ignorado, porque sólo se requiere la sumatoria (s) de sus elementos.

También se puede emplear el método every:

Object.defineProperty(SV.prototype, "promediog", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); s = 0; x.every(e=>(s+=e,true)); return s/n; }, configurable: true });;
new SV(1, 9, 12, 3, 5, 7, 12).promediog
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promediog

Como el método every termina cuando el resultado de la función es falso y en este caso es necesario que se itere para todos los elementos, la función debe devolver siempre un valor verdadero (true, 1 o cualquier valor equivalente a verdadero), porque, aunque no es frecuente, el resultado puede ser 0 (cuando se tienen valores positivos y negativos) por lo que, al final, la función debe devolver true (o 1) .

Para ello, dado que la función tiene dos instrucciones, se las encierra entre paréntesis y las instrucciones se separan con comas. No obstante, la función puede ser programada también en la forma tradicional, es decir con llaves y el comando return:

Object.defineProperty(SV.prototype, "promedioh", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); s = 0; x.every(e=>{s+=e; return true}); return s/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promedioh
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promedioh

Igualmente, se puede emplear el método some (que es el complemento de every):

Object.defineProperty(SV.prototype, "promedioi", { get() { const x = this.x; const n = x.length; if (n===0) throw new Error("vector vacío"); s = 0; x.some(e=>(s+=e,false)); return s/n; }, configurable: true });
new SV(1, 9, 12, 3, 5, 7, 12).promedioi
randseed = 1; var b = [].rand(20, 1, 30).round(); print(b); new SV(...b).promedioi

En este caso, como some termina cuando el resultado es verdadero, el resultado de la función debe ser siempre falso (false, 0 o cualquier valor equivalente a falso).

De forma similar se pueden emplear los métodos find, findIndex, findLast, findLastIndex y filter. Sin embargo, ninguno de estos métodos es adecuado para resolver este tipo de problemas, porque no han sido creados para ese fin, razón por la cual es necesario forzar la iteración a través de todos sus elementos. Sólo se emplean de esa forma, en estos capítulos, para ayudar a comprender su lógica, pero no deben ser empleados en la práctica, para resolver problemas de este tipo.

Vector de números aleatorios

En este ejemplo se elabora un método que genera un vector con "n" números aleatorios comprendidos entre 1 y 1000.

El problema puede ser resuelto fácilmente con el método rand: simplemente se generan "n" números aleatorios comprendidos entre 1 y 1000 y se redondea el vector resultante, tal como se muestra en la siguiente función:

function rand1000(n) { if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); return [].rand(n, 1, 1000).round(); };

Llamando al método con algunos valores de prueba, se obtiene:

randseed = 10;
rand1000(5)
rand1000(10)
rand1000(20)
rand1000(25)
rand1000(-10)
rand1000(5.7)

Si no se tiene acceso al método rand, porque no se está trabajando en la calculadora o no se ha importado la librería array.js, el problema puede ser resuelto empleando la estructura for y la función estándar random.

Para transformar el número aleatorio comprendido entre 0 y 1, que es el resultado devuelto por random, en un número comprendido entre "li" y "ls", se aplica la siguiente expresión:

\[ \begin{aligned} \text{Nú}&\text{mero aleatorio comprendido entre li y ls}=\\ &(\text{Número aleatorio comprendido entre 0 y 1})\times (\text{ls}-\text{li})+\text{li} \end{aligned} \]

Entonces, en cada repetición del ciclo for, que va desde 0 hasta "n-1", se genera y guarda un número aleatorio, como se muestra en el siguiente diagrama de flujo:

gojsGraph({divi, modelo: {"class":"go.GraphLinksModel","linkFromPortIdProperty":"fromPort","linkToPortIdProperty":"toPort","nodeDataArray":[{"category":"Start","text":"rand1000","key":-1,"loc":"172 -460"},{"category":"Input","text":"número de elementos a generar: n","key":-3,"loc":"171.99999999999994 -410.4666427612303"},{"category":"Process","text":"v = []","key":-2,"loc":"171.99999999999997 -359.3332138061521"},{"category":"Process","text":"i = i+1","key":-7,"loc":"57.99999999999987 -250.44982967376689"},{"category":"Start","text":"fin","key":-9,"loc":"172.00000000000009 -63.34418347676573"},{"category":"Output","text":"v","key":-10,"loc":"171.99999999999997 -116.22199312845845"},{"category":"For","text":"i<n","key":-6,"loc":"171.99999999999997 -250.44982967376686"},{"category":"Process","text":"i = 0","key":-12,"loc":"171.99999999999997 -304.79985656738245"},{"category":"Process","text":"v[i] = random()*999+1","key":-13,"loc":"171.99999999999997 -181.09980278015115"}],"linkDataArray":[{"from":-10,"to":-9,"fromPort":"B","toPort":"T","points":[171.99999999999997,-103.5419787851967,171.99999999999997,-93.5419787851967,171.99999999999997,-91.07642044067359,171.99999999999997,-91.07642044067359,171.99999999999997,-88.61086209615047,171.99999999999997,-78.61086209615047]},{"from":-1,"to":-3,"fromPort":"B","toPort":"T","points":[172.00000000000006,-444.73332138061534,172.00000000000006,-434.73332138061534,172.00000000000006,-434.73332138061517,171.99999999999997,-434.73332138061517,171.99999999999997,-434.73332138061505,171.99999999999997,-424.73332138061505]},{"from":-2,"to":-12,"fromPort":"B","toPort":"T","points":[171.99999999999997,-342.06653518676734,171.99999999999997,-332.06653518676734,171.99999999999997,-332.06653518676734,171.99999999999997,-332.0665351867674,171.99999999999997,-332.0665351867674,171.99999999999997,-322.0665351867674]},{"from":-12,"to":-6,"fromPort":"B","toPort":"T","points":[171.99999999999997,-287.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-267.53317794799784]},{"from":-6,"to":-13,"fromPort":"B","toPort":"T","visible":true,"points":[171.99999999999997,-233.36648139953593,171.99999999999997,-223.36648139953593,171.99999999999997,-215.86648139953593,171.99999999999997,-215.86648139953593,171.99999999999997,-208.36648139953593,171.99999999999997,-198.36648139953593],"text":"Si"},{"from":-13,"to":-7,"fromPort":"L","toPort":"B","points":[93.007453918457,-181.09980278015115,83.007453918457,-181.09980278015115,57.99999999999986,-181.09980278015115,57.99999999999986,-202.14147691726663,57.99999999999986,-223.18315105438214,57.99999999999986,-233.18315105438214]},{"from":-7,"to":-6,"fromPort":"R","toPort":"L","points":[85.8365097045897,-250.44982967376689,95.8365097045897,-250.44982967376689,116.06658172607413,-250.44982967376689,116.06658172607413,-250.44982967376689,136.29665374755857,-250.44982967376689,146.29665374755857,-250.44982967376689]},{"from":-6,"to":-10,"fromPort":"R","toPort":"T","visible":true,"points":[197.70334625244138,-250.44982967376689,207.70334625244138,-249.72491483688344,270,-249.72491483688344,270,-152,171.99999999999997,-152,171.99999999999997,-143.83312416076637,171.99999999999997,-133.83312416076637],"text":"No"},{"from":-3,"to":-2,"fromPort":"B","toPort":"T","points":[171.99999999999997,-396.1999641418455,171.99999999999997,-386.1999641418455,171.99999999999997,-386.1999641418455,171.99999999999997,-386.5998924255369,171.99999999999997,-386.5998924255369,171.99999999999997,-376.5998924255369]}]} })

El código respectivo, implementado como un método getter de la clase "RAND", así como los resultados obtenidos con algunos valores de prueba, son:

var RAND = class { #n; constructor(número) { this.#n = número; } get n() { return this.#n; } get rand1000() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); const v= []; for (let i=0; i<n; i++) v[i] = Math.round(Math.random()*999+1); return v; } };
randseed = 10;
new RAND(5).rand1000
new RAND(10).rand1000
new RAND(20).rand1000
new RAND(25).rand1000

La solución, implementada empleando el método push (en lugar de una asignación directa), es:

Object.defineProperty(RAND.prototype, "rand1000a", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); const v = []; for (let i=0; i<n; i++) v.push(Math.round(Math.random()*999+1)); return v; }, configurable: true });
randseed = 10;
new RAND(5).rand1000

El problema puede ser resuelto, también, empleando la estructura for-of:

Object.defineProperty(RAND.prototype, "rand1000b", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); const v = []; for (let _ of Array.from({length:n})) v.push(Math.round(Math.random()*999+1)); return v; }, configurable: true });
randseed = 10;
new RAND(5).rand1000b

Como el método for-of itera para cada uno de los elementos de un array y en este caso no existe ese array, se crea uno, con Array.from({length:n}). Pero como estos elemento no se emplean en las instrucciones del ciclo, se denota ese hecho empleando la variable "_" para el nombre de los elementos.

Los "n" elementos del array pueden ser generados también con cualquiera de los métodos disponibles: const, range, zeros, etc.

Por ejemplo, con el método forEach y range (para generar el array), la solución es:

Object.defineProperty(RAND.prototype, "rand1000c", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); const v = []; [].range(1,n).forEach(()=>v.push(Math.round(Math.random()*999+1))); return v; }, configurable: true });
randseed = 10;
new RAND(5).rand1000c

Donde, con range, se crea un vector con "n" elementos (del 1 al n) y se llama al método desde ese vector. Como no se emplean los elementos del array, la función no recibe ningún elemento.

El problema resuelto con el método reduce y zeros (para generar el array), es:

Object.defineProperty(RAND.prototype, "rand1000d", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); return [].zeros(n).reduce(v=>(v.push(Math.round(Math.random()*999+1)),v),[]); }, configurable: true });
randseed = 10;
new RAND(5).rand1000d

Como no se emplean los elementos del array, la función solo recibe el primer parámetro (el acumulador "v"). En este caso es necesario que el valor inicial del acumulador sea un vector vacío ([]), no el primer elemento del array, para que en cada iteración se le añada un nuevo número aleatorio. La función devuelve el vector "v" (con el valor añadido) para que en la siguiente se le añada otro elemento (hasta que se generan los "n" elementos).

El problema puede ser resuelto, también, con el método some, aunque, como se explicó previamente, no es la opción correcta para este tipo de problemas:

Object.defineProperty(RAND.prototype, "rand1000e", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); const v = []; Array.from({length:n}).some(()=>(v.push(Math.round(Math.random()*999+1)),false)); return v; }, configurable: true });
randseed = 10;
new RAND(5).rand1000e

Igual que en el caso anterior, no se emplean los elementos del array, por lo que la función no recibe nada y para que el método itere para todos los elementos, devuelve siempre false.

De la misma forma, se puede emplear el método every:

Object.defineProperty(RAND.prototype, "rand1000f", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); const v = []; [].range(1,n).every(()=>(v.push(Math.round(Math.random()*999+1)),true)); return v; }, configurable: true });
randseed = 10;
new RAND(5).rand1000f

Que es prácticamente igual a la solución anterior, solo que en este ahora (a manera de ejemplo) el array se genera con range y com es every devuelve true.

El problema puede ser resuelto, igualmente, con el método map.

Object.defineProperty(RAND.prototype, "rand1000g", { get() { const n = this.n; if (n%1!==0 || n<0) throw new Error("El número debe ser entero positivo"); return Array.from({length:n}).map(()=>Math.round(Math.random()*999+1)); }, configurable: true });
randseed = 10;
new RAND(5).rand1000g

Iguamente, como no se emplean los elementos del array, la función no recibe ningún valor, pero en este caso se aprovecha el array generado por map, siendo ese array el resultado de la función.

Como en casi todos los casos, el problema puede ser resuelto también con find, findIndex, findLast, findLastIndex y filter, aunque, como también se dijo, no son los métodos más adecuados para resolver este tipo de problemas.