Selección 1

En este capítulo se estudian los operadores relacionales, los operadores lógicos, la estructura condicional if (si) y se las emplea en la resolución de problemas que implican la toma de decisiones, además, se complementa el estudio de las funciones introduciendo los parámetros por defecto y los parámetros nombrados.

Operadores relacionales

Los operadores relacionales son empleados en la escritura de condiciones en programación. Una condición es toda sentencia de la cual se puede determinar su veracidad (true) o falsedad (false).

Los operadores relacionales comparan dos valores (escritos a ambos lados del operador) y devuelven siempre un valor booleano (true o false). Javascript cuenta con los siguientes operadores relacionales:

Operadores Relacionales
OperadorDescripción
==Igual a
===Igual y del mismo tipo que
!=Diferente de
!==Diferente o de otro tipo que
>Mayor a
<Menor a
>=Mayor o igual a
<=Menor o igual a

Por defecto, JavaScript, realiza las conversiones de tipos que sean necesarias, antes de comparar los valores.

Por ejemplo, se pueden comparar cadenas con números:

"50.34">5.554
30.45<"3.43"
12>="15"
"23"<=64

En estas comparaciones, JavaScript, convierte primero las cadenas en números y luego compara los valores convertidos. No obstante, no es una buena práctica de programación el mezclar diferentes tipos de datos, por lo que se aconseja no hacerlo.

La diferencia entre los operadores == y != y los operadores === y !==, radica en que los últimos no realizan ningun tipo de conversión, lo que es más seguro desde el punto de vista de programación y la razón por la cual se recomienda su uso.

Por ejemplo, en las siguientes comparaciones, se obtienen resultados distintos con los dos tipos de operadores:

"23" == 23
"23" === 23
"68.5" != 68.5
"68.5" !== 68.5

Operadores lógicos

Los operadores lógicos comparan valores booleanos (o expresiones que pueden ser convertidas o interpretadas como booleanos). Conjuntamente los operadores relacionales, permiten escribir expresiones lógicas complejas.

Javascript cuenta con los siguientes operadores lógicos:

Operadores Lógicos
OperadorDescripción
!Negación
&&Y lógico
||O lógico

El operador de negación: !, cambia el valor o expresión booleana a la que precede, de verdadero a falso y viceversa. El operador "Y": &&, devuelve verdadero sólo si los dos valores que compara son verdaderos, en cualquier otro caso devuelve falso. El operador "O" lógico: ||, devuelve falso sólo si los dos valores que compara son falsos, en cualquier otro caso devuelve verdadero.

JavaScript interpreta como valores falsos: el cero (0), el texto vacío (""), el valor nulo (null) y el valor undefined e interperta como verdaderos cualquier cadena no vacía y cualquier valor no nulo o definido.

Por ejemplo, las siguientes instrucciones devuelven la negación de algunos de estos valores:

!""
!"cadena"
!undefined
!null
!54.23

Evaluación en corto circuito

En Javascript, al igual que en la mayoría de los lenguajes de programación, los operadores lógicos || e && se evalúan en cortocircuito, esto es, si con el primer valor se puede determinar (sin lugar a dudas) el resultado de la expresión, entonces el resultado es devuelto directamente, sin tomar en cuenta el segundo valor.

En el caso del operador &&, se sabe que el resultado es falso si el primer valor es falso (porque falso && cualquier otro valor es siempre falso). En el caso del operador ||, se sabe que el resultado es verdadero si el primer valor es verdadero (porque verdadero || cualquier otro valor es siempre verdadero). Tal como se puede comprobar en los siguientes ejemplos, donde, aunque los segundos valores son en realidad erróneos (son variables y/o funciones inexistentes) no generan ningún error, porque, al poder determinarse la veracidad o falsedad de las expresiones con el primer término, los segundos términos nunca son evaluados:

false && cos(3.2)
false && miVariable
true || tangente(6.43)
true || otraVariable

En JavaScript, el resultado de un operador lógico no siempre es un valor booleano (true/false) sino el valor con el cual se determina la veracidad o falsedad de la expresión.

Así, el operador "Y" (&&) devuelve el primer término, si el mismo equivale a falso y el segundo en caso contrario, mientras que el operador "O" (||) devuelve el primer término si es equivalente a verdadero y el segundo en caso contrario. Tal como se puede ver en los siguientes ejemplos:

"" && 3 > 2
undefined && true
null && "Hola"
Math.sin(0) && Math.cos(5.32)
Math.cos(0) || true
"Javascript" || false
"Aceptado" || "Rechazado"
Math.sqrt(25) || Math.cbrt(27)

Y las siguientes devuelven el segundo valor, pues con el primero no es posible determinar la veracidad o falsedad de la expresión:

true && "Resultado"
"Primero" && "Segundo"
1 && Math.sin(5.3)
false || "Resultado"
undefined || "Valor por defecto"
"" || "Rechazado"
"Aceptado" && "Rechazado"
0 || 2**5
45<23 || Math.sqrt(2)

Ejemplos

Número par

Como primer ejemplo, se elaborará una función que, haciendo uso de una expresión lógica, determine si un número es (true) o no (false) par:

Como se sabe, un número es divisible entre otro si el residuo de su división es cero y un número (n) es par si es divisible entre 2, es decir si se cumple que: n%2===0. Como el resultado de toda expresión relacional es siempre un valor booleano (true o false), el problema se resuelve simplemente devolviendo el resultado de esta expresión:

var esPar = n => n%2 === 0;

Llamando a la función con los números 124, 311 y 0, se obtiene:

esPar(124)
esPar(311)
esPar(0)

Qué son resultados correctos.

Número entero

Un número es entero si no tiene parte fraccionaria y un número no tiene parte fraccionaria si el residuo de su división entre 1 es cero, entonces para determinar si un número (n) es (true) o no (false) un entero, simplemente se verifica si es divisible entre 1: n%1===0:

var esEntero = function(n) {
return n%1 === 0;
};

LLamando al método desde los números 124, 911, 124.34, -457 y -93.672, se obtiene:

esEntero(124)
esEntero(911)
esEntero(124.24)
esEntero(-457)
esEntero(-93.672)

Que son los resultados correctos.

Número natural

Un número (n) es natural si es entero y positivo. La mayoría de las definiciones incluyen el número 0 en este conjunto, siendo esa la definición que se adopta en la asignatura.

Al igual que en los otros casos, el problema es directamente el resultado de una expresión lógica, donde se determina si el número es divisible entre 1 (si es entero) y si es positivo (mayor o igual a cero): n%1===0 && n>=0:

function esNatural(n) {
return n%1 === 0 && n >= 0;
};

Haciendo correr el programa con 27, 0, -45, 23.45 y -92.14, se obtiene:

esNatural(27)
esNatural(0)
esNatural(-45)
esNatural(23.45)
esNatural(-92.14)

Que son resultados correctos.

Parámetros

Los parámetros empleados hasta el momento, en todas las funciones elaboradas, son parámetros por valor. Como su nombre sugiere, estos parámetros sólo guardan el valor que se les manda, sin importar que dicho valor sea literal, esté guardado en una variable o sea el resultado de una función o expresión.

Por ejemplo, dada la función:

var sum2 = (x, y) => x+y;

Si es llamada, guardando previamente los valores a sumar en las variables "a" y "b":

var a = 7, b = 8;
sum2(a, b)

La función sum2, no guarda las variables "a" y "b" en las variables "x" y "y", sino únicamente sus valores (los números 7 y 8).

Las variables originales permanecen inalteradas porque, como se dijo, la función no recibe las variables, sino únicamente sus valores. Como se puede comprobar en la siguiente función, que recibe dos números en los parámetros "c" y "d", estos parámetros son modificados dentro de la función, pero, como se puede ver, las variables externas permanecen inalteradas.

var c = 5, d = 7;
print(c, d);
function fun1(c, d) {
c = c*3;
d = d*4;
return c+d;
}
print(fun1(c, d));
print(c, d)

Los parámetros pueden ser también parámetros por defecto, que son parámetros a los que se les asignan valores iniciales. De esa manera, si la función es llamada con un menor número de datos, los parámetros que no reciben un dato, toman su valor inicial (su valor por defecto).

Por ejemplo, la siguiente función calcula la suma de hasta 3 números, pero todos los parámetros tienen valores por defecto iguales a 0:

var suma = (a=0, b=0, c=0) => a+b+c;

Si no se manda uno o más de los 3 valores que requiere la función, los parámetros que no reciben un valor toman su valor por defecto (0). En este caso cero es un valor conveniente, porque al ser el valor neutro de la suma no altera el resultado y si, por ejemplo, no se manda el valor de "c", devuelve el resultado de "a+b", porque al ser "c" cero, no altera el resultado.

Así, a continuación, en la primera llamada que se hace a la función (con 3 valores) "a" es igual a 1, "b" es igual a 2 y "c" es igual a 3; en la segunda llamada (con 2 valores) "a" es igual a 1, "b" igual a 2 y "c" es igual a 0 (el valor por defecto); en la tercera llamada (con un valor) "a" es igual a 1, mientras que "b" y "c" son iguales a 0 (el valor por defecto) y en la última llamada (sin valores) "a", "b" y "c" son iguales a 0 (el por defecto):

suma(1, 2, 3)
suma(1, 2)
suma(1)
suma()

Como se puede ver, en todos los casos la función devuelve el resultado correcto y cuando no se manda ningún valor, devuelve 0.

Normalmente, los parámetros por defecto están al final de la lista de parámetros, sin embargo, en JavaScript los parámetros por defecto pueden estar en cualquier posición (no siempre al final), no obstante, para que un parámetro, que no está al final de la lista, emplee su valor por defecto, se le debe mandar el valor undefined. Por ejemplo, para que el segundo parámetro de suma emplee su valor por defecto, se le manda:

suma(12, undefined, 45)

En este caso "a" toma el valor 12, "b" toma el valor 0 (el valor por defecto) y "c" toma el valor 45.

Aunque estrictamente hablando, las funciones en JavaScript no admiten parámetros nombrados, pueden ser emulados recurriendo a los objetos literales, de acuerdo a la siguiente sintaxis:

({par1[=val1], par2[=val2], ..., parn[=valn]}={})

Donde, par1, par2, etc., son los nombres de los parámetros. Como se puede ver, los parámetros nombrados también pueden tener valores iniciales por defecto. Los parámetros nombrados se caracterizan porque, como su nombre sugiere, los datos deben ser mandados incluyendo el nombre del parámetro y porque esos nombres pueden ser mandados en cualquier orden (a diferencia de los parámetros por valor y por defecto, que deben ser mandados en el orden en que fueron declarados).

La igualdad y las llaves vacías escritas al final de la lista de parámetros nombrados: ={}, sólo son necesarias para aquellos casos en los que la función es llamada si ningún dato. Si se tiene la seguridad de que la función será llamada siempre, con algún dato, aunque ese dato sea un objeto vacío ({}), entonces la igualdad y las llaves pueden ser omitidas.

Los parámetros nombrados son particularmente útiles cuando la función consta de 3 o más parámetros, porque entonces es más fácil recordar el nombre de los parámetros, que la posición en la que fueron declarados.

Así la función suma, implementada con parámetros nombrados, es:

var suma_1 = ({a=0, b=0, c=0}={}) => a+b+c;

La cual puede ser llamada escribiendo los parámetros en desorden:

suma_1({c:3, b:2, a:1})
suma_1({b:2, a:1})
suma_1({c:3})
suma_1()

Si, al crear la función, no se escribe la igualdad al final de la lista de parámetros:

var suma_2 = ({a=0, b=0, c=0}) => a+b+c;

La función devuelve los mismos resultados que en el caso anterior, excepto cuando no se manda ningún dato a la función:

suma_2({c:3, b:2, a:1})
suma_2({b:2, a:1})
suma_2({c:3})
suma_2()

Para calcular la distancia (d) que alcanza un proyectil disparado con una velocidad inicial \(v_0\) (en m/s), con un ángulo de disparo θ (en grados), con la ecuación del movimiento parabólico:

\[ d = \dfrac{v^2_0 \sin(2\theta)}{2g} \]

Se pueden emplear parámetros nombrados. En este caso es conveniente declarar como parámetro nombrado, con un valor por defecto, la aceleración de la gravedad (g): 9.80665 (m/s2), porque casi siempre se emplea ese valor (la gravedad estándar en la tierra) en los cálculos:

var alcance = function({v0, theta, g=9.80665}={}) {
theta = Math.toRad(theta);
return v0**2*Math.sin(2*theta)/(2*g);
};

En esta función, como se puede ver, primero se transforma el ángulo de grados a radianes (con toRad), porque, como se sabe, todas las funciones trigonométricas operan con ángulos en radianes.

Llamando a la función, para calcular la distancia que alcanza un proyectil disparado con una velocidad inicial de 150 m/s y un ángulo de 30 grados, se obtiene:

alcance({theta:30, v0:150})

Por lo tanto, el proyectil alcanza una distancia de 993.5 m.

Como se puede ver en este ejemplo, se han mandado los datos a la inversa de como fueron declarados (se ha mandado primero theta y luego v0), lo que constituye, como se dijo, la principal ventaja de los parámetros nombrados: pueden ser mandados en cualquier orden.

Si se requiere un resultado más preciso, se manda además, la aceleración local de la gravedad, así, para Sucre, es aproximadamente: g=9.77721 m/s2):

alcance({g:9.77721, theta:30, v0:150})

Que es casi 3 metros mayor al resultado anterior y donde, una vez más, los datos han sido mandados en desorden.

La estructura if (si)

If es la estructura selectiva por excelencia, su lógica, en forma de diagrama de flujo, es:

gojsGraph({divi, modelo:{ "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Connector", "text":"...", "key":-8, "loc":"-184 -400"},{"category":"Conditional", "text":"condición", "key":-4, "loc":"-184 -341"},{"category":"Process", "text":"instrucciones A", "key":-2, "loc":"-71 -280"},{"category":"Process", "text":"instrucciones B", "key":-5, "loc":"-287 -280"},{"category":"Connector", "text":"...", "key":-6, "loc":"-178 -224"} ], "linkDataArray": [ {"from":-8, "to":-4, "fromPort":"B", "toPort":"T", "points":[-184,-385.3042461927547,-184,-375.3042461927547,-184,-373.16880171576213,-184,-373.16880171576213,-184,-371.03335723876955,-184,-361.03335723876955]},{"from":-4, "to":-2, "fromPort":"R", "toPort":"T", "visible":true, "points":[-119.50294494628906,-341,-109.50294494628906,-341,-71,-341,-71,-324.13333930969236,-71,-307.2666786193848,-71,-297.2666786193848], "text":"Si"},{"from":-4, "to":-5, "fromPort":"L", "toPort":"T", "visible":true, "points":[-248.49705505371094,-341,-258.49705505371094,-341,-287,-341,-287,-324.13333930969236,-287,-307.2666786193848,-287,-297.2666786193848], "text":"No"},{"from":-5, "to":-6, "fromPort":"B", "toPort":"L", "points":[-287,-262.7333213806152,-287,-252.73332138061522,-287,-224,-244.84787690362265,-224,-202.6957538072453,-224,-192.6957538072453,-224]},{"from":-2, "to":-6, "fromPort":"B", "toPort":"R", "points":[-71,-262.7333213806152,-71,-252.73332138061522,-71,-224,-112.15212309637735,-224,-153.3042461927547,-224,-163.3042461927547,-224]} ]}})

Es decir que if ejecuta las instrucciones A si la condición se cumple (si es verdadera), caso contrario (else), si es falsa, ejecuta las instrucciones B. Luego el programa continua con las instrucciones restantes.

En JavaScript, la sintaxis de esta estructura, es:

if (condición) { instrucciones A } else { instrucciones B }

Si en lugar de dos o más instrucciones sólo existe una, las llaves no son necesarias, sin embargo, el emplearlas no constituye un error:

if (condición) instrucción_1; else instrucción_2;

El caso contrario (las instrucciones B) es opcional y la estructura puede tener únicamente el caso verdadero (las instrucciones A):

gojsGraph({divi, modelo:{ "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Connector", "text":"...", "key":-8, "loc":"-184 -400"},{"category":"Conditional", "text":"condición", "key":-4, "loc":"-184 -341"},{"category":"Process", "text":"instrucciones A", "key":-2, "loc":"-71 -280"},{"category":"Connector", "text":"...", "key":-6, "loc":"-184 -224"} ], "linkDataArray": [ {"from":-8, "to":-4, "fromPort":"B", "toPort":"T", "points":[-184,-385.3042461927547,-184,-375.3042461927547,-184,-373.16880171576213,-184,-373.16880171576213,-184,-371.03335723876955,-184,-361.03335723876955]},{"from":-4, "to":-2, "fromPort":"R", "toPort":"T", "visible":true, "points":[-119.50294494628906,-341,-109.50294494628906,-341,-71,-341,-71,-324.13333930969236,-71,-307.2666786193848,-71,-297.2666786193848], "text":"Si"},{"from":-2, "to":-6, "fromPort":"B", "toPort":"R", "points":[-71,-262.7333213806152,-71,-252.73332138061522,-71,-224,-115.15212309637735,-224,-159.3042461927547,-224,-169.3042461927547,-224]},{"from":-4, "to":-6, "fromPort":"B", "toPort":"T", "visible":true, "points":[-184,-320.9666427612305,-184,-310.9666427612305,-184,-279.83119828423787,-184,-279.83119828423787,-184,-248.6957538072453,-184,-238.6957538072453], "text":"No"} ]}})

En cuyo caso, la sintaxis, es:

if (condición) { instrucciones A }

O si sólo existe una instrucción:

if (condición) instrucción_1;

Como se dijo, en JavaScript, si una instrucción termina con un salto de línea, el punto y coma al final de la misma no es imprescindible, sin embargo, para minimizar errores, se recomienda escribir siempre un punto y coma al final de cada instrucción (excepto cuando se escriben instrucciones sueltas en la calculadora, porque como se recordará, el punto y coma en la última instrucción, hace que el resultado no sea mostrado).

Ejemplos

Par, impar, cero

En este ejemplo se elabora una función que determina si un número entero es par, impar o cero, devolviendo el texto "par", si es par; "impar", si es impar y "cero", si es cero.

Como casi siempre ocurre en programación, el problema puede ser resuelto de diferentes formas, así, se puede verificar primero si el número es cero y de ser así el resultado es "cero". Sino (caso contrario) se puede verificar si es par y de ser así el resultado es "par", caso contrario el resultado es "impar", porque si el número no es cero ni par (por lógica) sólo puede ser impar (el caso por defecto).

Como ya se vio previamente, para determinar si un número entero es par, simplemente se verifica si el residuo de su división entre 2 sea igual a 0.

Igualmente, se puede verificar primero si el número es impar y luego si es cero, siendo el caso por defecto par.

Sin embargo, no se puede verificar primero si el número es par, porque el residuo de 0 entre 2 es 0 y por lo tanto el algoritmo devolvería par, también para el número 0.

El algoritmo, en forma de diagrama de flujo, donde se verifica primero si el número es cero y luego si es par, es:

gojsGraph({divi, modelo:{ "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Conditional", "text":"n==0", "key":-4, "loc":"-131 -319"}, {"category":"Start", "text":"parImparCero", "key":-1, "loc":"-131 -428"}, {"category":"Input", "text":"número entero: n", "key":-3, "loc":"-131 -377"}, {"category":"Output", "text":"\"cero\"", "key":-5, "loc":"-35 -236"}, {"category":"Conditional", "text":"n%2==0", "key":-6, "loc":"-224 -262"}, {"category":"Output", "text":"\"par\"", "key":-7, "loc":"-130 -195"}, {"category":"Output", "text":"\"impar\"", "key":-8, "loc":"-311 -197"}, {"category":"Start", "text":"fin", "key":-9, "loc":"-130 -109"} ], "linkDataArray": [ {"from":-1, "to":-3, "fromPort":"B", "toPort":"T", "points":[-131,-412.7333213806152,-131,-402.7333213806152,-131,-402,-131,-402,-131,-401.2666786193848,-131,-391.2666786193848]}, {"from":-3, "to":-4, "fromPort":"B", "toPort":"T", "points":[-131,-362.7333213806152,-131,-352.7333213806152,-131,-350.88333930969236,-131,-350.88333930969236,-131,-349.03335723876955,-131,-339.03335723876955]}, {"from":-4, "to":-5, "fromPort":"R", "toPort":"T", "visible":true, "points":[-94.84066772460938,-319,-84.84066772460938,-319,-35,-319,-35,-291.30556551615393,-35,-263.6111310323079,-35,-253.61113103230792], "text":"Si"}, {"from":-4, "to":-6, "fromPort":"L", "toPort":"T", "visible":true, "points":[-167.15933227539062,-319,-177.15933227539062,-319,-224,-319,-224,-305.5166786193848,-224,-292.03335723876955,-224,-282.03335723876955], "text":"No"}, {"from":-6, "to":-7, "fromPort":"R", "toPort":"T", "visible":true, "points":[-167.81512451171875,-262,-157.81512451171875,-262,-130,-262,-130,-242.305565516154,-130,-222.61113103230795,-130,-212.61113103230795], "text":"Si"}, {"from":-6, "to":-8, "fromPort":"L", "toPort":"T", "visible":true, "points":[-280.18487548828125,-262,-290.18487548828125,-262,-311,-262,-311,-243.305565516154,-311,-224.61113103230795,-311,-214.61113103230795], "text":"No"}, {"from":-5, "to":-9, "fromPort":"B", "toPort":"T", "points":[-35,-223.31998565673825,-35,-213.31998565673825,-35,-212,-35,-212,-35,-154,-130,-154,-130,-134.26667861938478,-130,-124.26667861938478]}, {"from":-7, "to":-9, "fromPort":"B", "toPort":"T", "points":[-130,-182.31998565673828,-130,-172.31998565673828,-130,-153.29333213806154,-130,-153.29333213806154,-130,-134.26667861938478,-130,-124.26667861938478]}, {"from":-8, "to":-9, "fromPort":"B", "toPort":"T", "points":[-311,-184.31998565673828,-311,-174.31998565673828,-311,-154.29333213806154,-130,-154.29333213806154,-130,-134.26667861938478,-130,-124.26667861938478]} ]}})

Como se puede ver en este ejemplo, en los diagramas, las expresiones relacionales se escriben empleando los operadores relacionales estándar de JavaScript, pero, para la igualdad y la diferencia, se emplean los operadores == y !=, NO === y !==, porque esos operadores son exclusivo de JavaScript, no obstante, al momento de programar, se aconseja emplear estos operadores, porque, al ser más estrictos, son más seguros (menos propensos a ocasionar errores).

Es importante notar que el algoritmo tiene una condición implícita, pues específica que el número recibido debe ser entero. En algunos lenguajes ello simplemente implica elegir el tipo de dato a emplear, pero en los lenguajes no tipificados, como JavaScript, implica una condición donde se debe verificar que el número sea entero y donde, de no ser así, se lanza un error (con throw), como se muestra en el siguiente código:

var parImparCero = n => {
if (n%1!==0) throw "El número debe ser entero";
if (n === 0) return "cero";
else
if (n%2 === 0) return "par";
else return "impar";
};

El comprobar que los datos sean de los tipos correctos y/o estén entre los límites permitidos, se conoce como validación y es una práctica obligada en programación, pues evita errores al impedir valores o tipos de datos incorrectos.

El comando throw, empleado en el ejemplo, genera un error con el texto que se escribe después del mismo (en el ejemplo: "El número debe ser entero"), luego pasa el control del programa al primer bloque de tratamiento de errores (un bloque try-catch que será estudiado posteriormente) y si dicho bloque no existe (como en el ejemplo), throw termina la ejecución del módulo y pasa el control del programa al primer bloque try-catch externo, en este caso el bloque try-catch de la calculadora, el cual simplemente muestra el mensaje de error.

Como se dijo, la estructura try-catch será estudiada en capítulos posteriores, por el momento es suficiente saber que esta estructura atrapa los errores y que si un módulo no tiene esta estructura, el error es atrapado por el bloque try-catch de la calculadora, el cual muestra el mensaje de error lanzado.

parImparCero(0)
parImparCero(115)
parImparCero(244)
parImparCero(-115)
parImparCero(-110)
parImparCero(34.53)
El número debe ser entero
parImparCero(-7.3265e2)
El número debe ser entero

Alternativamente, para determinar si un número es entero, se puede emplear el método estático isInteger de la clase Number, el cual devuelve true si el número es entero y false en caso contrario.

El diagrama de flujo (la lógica), verificando primero si el número es impar y luego si es cero, es:

gojsGraph({divi, modelo:{ "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Conditional", "text":"n%2==1", "key":-4, "loc":"-131 -319"}, {"category":"Start", "text":"parImparCero_", "key":-1, "loc":"-131 -428"}, {"category":"Input", "text":"número entero: n", "key":-3, "loc":"-131 -377"}, {"category":"Output", "text":"\"impar\"", "key":-5, "loc":"-35 -236"}, {"category":"Conditional", "text":"n==0", "key":-6, "loc":"-224 -262"}, {"category":"Output", "text":"\"cero\"", "key":-7, "loc":"-130 -195"}, {"category":"Output", "text":"\"par\"", "key":-8, "loc":"-311 -197"}, {"category":"Start", "text":"fin", "key":-9, "loc":"-130 -109"} ], "linkDataArray": [ {"from":-1, "to":-3, "fromPort":"B", "toPort":"T", "points":[-131,-412.7333213806152,-131,-402.7333213806152,-131,-402,-131,-402,-131,-401.2666786193848,-131,-391.2666786193848]}, {"from":-3, "to":-4, "fromPort":"B", "toPort":"T", "points":[-131,-362.7333213806152,-131,-352.7333213806152,-131,-350.8833393096924,-130.99999999999997,-350.8833393096924,-130.99999999999997,-349.0333572387696,-130.99999999999997,-339.0333572387696]}, {"from":-4, "to":-5, "fromPort":"R", "toPort":"T", "visible":true, "points":[-74.81512451171872,-319.00000000000006,-64.81512451171872,-319.00000000000006,-35.000000000000014,-319.00000000000006,-35.000000000000014,-291.30556551615393,-35.000000000000014,-263.6111310323078,-35.000000000000014,-253.6111310323078], "text":"Si"}, {"from":-4, "to":-6, "fromPort":"L", "toPort":"T", "visible":true, "points":[-187.18487548828122,-319.00000000000006,-197.18487548828122,-319.00000000000006,-223.99999999999994,-319.00000000000006,-223.99999999999994,-305.51667861938483,-223.99999999999994,-292.0333572387696,-223.99999999999994,-282.0333572387696], "text":"No"}, {"from":-6, "to":-7, "fromPort":"R", "toPort":"T", "visible":true, "points":[-187.84066772460932,-262.00000000000006,-177.84066772460932,-262.00000000000006,-130.00000000000003,-262.00000000000006,-130.00000000000003,-242.30556551615402,-130.00000000000003,-222.61113103230798,-130.00000000000003,-212.61113103230798], "text":"Si"}, {"from":-6, "to":-8, "fromPort":"L", "toPort":"T", "visible":true, "points":[-260.15933227539057,-262.00000000000006,-270.15933227539057,-262.00000000000006,-310.9999999999999,-262.00000000000006,-310.9999999999999,-243.30556551615393,-310.9999999999999,-224.61113103230778,-310.9999999999999,-214.61113103230778], "text":"No"}, {"from":-5, "to":-9, "fromPort":"B", "toPort":"T", "points":[-35.000000000000014,-223.31998565673814,-35.000000000000014,-213.31998565673814,-35.000000000000014,-212,-35.000000000000014,-212,-35.000000000000014,-155,-130,-155,-130,-134.26667861938478,-130,-124.26667861938476]}, {"from":-7, "to":-9, "fromPort":"B", "toPort":"T", "points":[-130.00000000000003,-182.3199856567383,-130.00000000000003,-172.3199856567383,-130,-172.3199856567383,-130,-172.3199856567383,-130,-134.26667861938478,-130,-124.26667861938476]}, {"from":-8, "to":-9, "fromPort":"B", "toPort":"T", "points":[-310.9999999999999,-184.3199856567381,-310.9999999999999,-174.3199856567381,-310.9999999999999,-154.29333213806143,-130,-154.29333213806143,-130,-134.26667861938478,-130,-124.26667861938476]} ]}})

Siendo el código respectivo (validando en este caso, que el número sea entero, con el método estático isInteger), es:

function parImparCero(n) {
if (!Number.isInteger(n)) throw "El número debe ser entero";
if (n%2 === 1) return "impar";
else
if (n === 0) return "cero";
else return "par";
};

Con el cual, por supuesto, se obtienen los mismos resultados:

parImparCero(0)
parImparCero(115)
parImparCero(244)
parImparCero(-115)
parImparCero(-110)
parImparCero(34.53)
El número debe ser entero.
parImparCero(-7.3265e2)
El número debe ser entero.

Analizando con más detenimiento la lógica, se puede ver que, en todos los casos, una vez que se cumple una de las condiciones (cualquiera) el resultado es devuelto directamente y la función termina, sin realizar ninguna operación o procesamiento posterior, por lo tanto, en este tipo de problemas, no se requieren los casos contrarios (los else), porque si no se cumple un caso, el flujo del programa pasa de todas formas al caso contrario (sin requerir el else).

Sin embargo, se aclara que esto ocurre únicamente porque no se ejecuta ninguna instrucción después del bloque if-else. Si antes de salir del programa, sería necesario ejecutar alguna instrucción (cualquiera) sería necesario emplear los casos contrarios (los else).

Por lo tanto, para estos casos, el algoritmo puede ser programado de la siguiente forma:

var parImparCero_ = function(n) {
if (!Number.isInteger(n)) throw "El número debe ser entero";
if (n%2 === 1) return "impar";
if (n === 0) return "cero";
return "par";
};

Año bisiesto

En este ejemplo se resuelve el problema de determinar si un año (un número entero positivo) es (true) o no (false) bisiesto.

Un año es bisiesto si es divisible entre 4, excepto cuando termina en dos ceros, en cuyo caso, se verifica si el número, sin los dos ceros, es divisible entre 4, o, lo que es lo mismo, se verifica si el número es divisible entre 400. Así, si el año es 1900, se verifica si 19 es divisible entre 4 (19%4==0) o (lo que es lo mismo) si 1900 es divisible entre 400 (1900%400 = 19%4 == 0).

Como casi siempre ocurre en programación, existe más de una forma de resolver el mismo problema. Así, se puede verificar primero si el año es divisible entre 100 y de ser así se verifica si el año es divisible entre 400, caso contrario, se verifica si es divisible entre 4, como se muestra en el siguiente diagrama de flujo:

gojsGraph({divi, modelo:{ "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Conditional", "text":"a%100==0", "key":-4, "loc":"-131 -319"},{"category":"Start", "text":"esBisiesto", "key":-1, "loc":"-131 -428"},{"category":"Input", "text":"año: a", "key":-3, "loc":"-131 -377"},{"category":"Output", "text":"a%400==0", "key":-5, "loc":"-13.000000000000014 -252.99999999999986"},{"category":"Output", "text":"a%4==0", "key":-8, "loc":"-238.9999999999999 -252.99999999999983"},{"category":"Start", "text":"fin", "key":-9, "loc":"-130.99999999999997 -149"} ], "linkDataArray": [ {"from":-1, "to":-3, "fromPort":"B", "toPort":"T", "points":[-131.00000000000003,-412.7333213806152,-131.00000000000003,-402.7333213806152,-131.00000000000003,-402,-130.99999999999994,-402,-130.99999999999994,-401.2666786193848,-130.99999999999994,-391.2666786193848]},{"from":-3, "to":-4, "fromPort":"B", "toPort":"T", "points":[-130.99999999999994,-362.7333213806152,-130.99999999999994,-352.7333213806152,-130.99999999999994,-350.8833393096924,-130.99999999999997,-350.8833393096924,-130.99999999999997,-349.0333572387696,-130.99999999999997,-339.0333572387696]},{"from":-4, "to":-5, "fromPort":"R", "toPort":"T", "visible":true, "points":[-58.52787780761716,-319.00000000000006,-48.52787780761716,-319.00000000000006,-13.000000000000014,-319.00000000000006,-13.000000000000014,-299.80556551615393,-13.000000000000014,-280.6111310323078,-13.000000000000014,-270.6111310323078], "text":"Si"},{"from":-5, "to":-9, "fromPort":"B", "toPort":"T", "points":[-13.000000000000014,-240.31998565673814,-13.000000000000014,-230.31998565673814,-13.000000000000014,-202.29333213806146,-131,-202.29333213806146,-131,-174.26667861938478,-131,-164.26667861938478]},{"from":-8, "to":-9, "fromPort":"B", "toPort":"T", "points":[-238.9999999999999,-240.31998565673808,-238.9999999999999,-230.31998565673808,-238.9999999999999,-202.29333213806143,-131,-202.29333213806143,-131,-174.26667861938478,-131,-164.26667861938478]},{"from":-4, "to":-8, "fromPort":"L", "toPort":"T", "visible":true, "points":[-203.47212219238278,-319.00000000000006,-213.47212219238278,-319.00000000000006,-238.9999999999999,-319.00000000000006,-238.9999999999999,-299.80556551615393,-238.9999999999999,-280.61113103230775,-238.9999999999999,-270.61113103230775], "text":"No"} ]}})

Al igual que en el ejemplo previo, aunque no está de forma explícita en el algoritmo, en el código se debe validar que el año sea un número entero positivo, porque no existen años ni reales ni negativos. El código respectivo, es:

var esBisiesto = function(a) {
if (a%1!==0 || a<0) throw "¡El año debe ser entero positivo!";
if (a%100 === 0) return a%400 === 0;
return a%4 === 0;
};

La función devuelve siempre un valor booleano (un valor lógico): true o false, porque al ser el resultado de una expresión relacional, es un valor booleano.

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

esBisiesto(1980)
esBisiesto(1982)
esBisiesto(1996)
esBisiesto(2000)
esBisiesto(2006)
esBisiesto(2012)
esBisiesto(2020)
esBisiesto(-2020)
¡El año debe ser entero positivo!
esBisiesto(2020.32)
¡El año debe ser entero positivo!

Otra posible solución, consiste en quitar los dos ceros del año cuando es divisible entre 100, luego, simplemente se verifica si el año (que ya no tiene los dos ceros) es divisible entre 4. Tal como se muestra en el siguiente diagrama de flujo:

gojsGraph({divi, modelo: {"class":"go.GraphLinksModel","linkFromPortIdProperty":"fromPort","linkToPortIdProperty":"toPort","nodeDataArray":[{"category":"Conditional","text":"a%100==0","key":-4,"loc":"-349 -354.00000000000006"},{"category":"Start","text":"esBisiesto","key":-1,"loc":"-349 -463"},{"category":"Input","text":"año: a","key":-3,"loc":"-348.99999999999994 -412"},{"category":"Output","text":"a%4==0","key":-8,"loc":"-349 -242.99999999999986"},{"category":"Start","text":"fin","key":-9,"loc":"-348.9999999999998 -189"},{"category":"Process","text":"a = a/100","key":-2,"loc":"-227.00000000000006 -296"}],"linkDataArray":[{"from":-1,"to":-3,"fromPort":"B","toPort":"T","points":[-349.00000000000006,-447.7333213806152,-349.00000000000006,-437.7333213806152,-349.00000000000006,-437,-348.99999999999994,-437,-348.99999999999994,-436.2666786193848,-348.99999999999994,-426.2666786193848]},{"from":-3,"to":-4,"fromPort":"B","toPort":"T","points":[-348.99999999999994,-397.7333213806152,-348.99999999999994,-387.7333213806152,-348.99999999999994,-385.8833393096924,-349,-385.8833393096924,-349,-384.0333572387696,-349,-374.0333572387696]},{"from":-8,"to":-9,"fromPort":"B","toPort":"T","points":[-348.9999999999999,-230.31998565673808,-348.9999999999999,-220.31998565673808,-348.9999999999999,-217.29333213806143,-349,-217.29333213806143,-349,-214.26667861938478,-349,-204.26667861938478]},{"from":-4,"to":-2,"fromPort":"R","toPort":"T","visible":true,"points":[-276.5278778076172,-354.00000000000006,-266.5278778076172,-354.00000000000006,-227,-354.00000000000006,-227,-338.6333393096924,-227,-323.2666786193848,-227,-313.2666786193848],"text":"Si"},{"from":-2,"to":-8,"fromPort":"B","toPort":"R","points":[-227,-278.7333213806152,-227,-268.7333213806152,-227,-242.9999999999998,-262.00836563110346,-242.9999999999998,-297.0167312622069,-242.9999999999998,-307.0167312622069,-242.9999999999998]},{"from":-4,"to":-8,"fromPort":"B","toPort":"T","visible":true,"points":[-349,-333.96664276123056,-349,-323.96664276123056,-349,-297.28888689676916,-348.9999999999999,-297.28888689676916,-348.9999999999999,-270.61113103230775,-348.9999999999999,-260.61113103230775],"text":"No"}]}})

Siendo el código respectivo (en forma de función flecha):

var esBisiesto_ = a => {
if (a%1 !==0 || a<0) throw "¡El año debe ser entero positivo!";
if (a%100 === 0) a = a/100;
return a%4 === 0;
};

Que, por supuesto, devuelve los mismos resultados que con la lógica anterior:

esBisiesto_(1980)
esBisiesto_(1982)
esBisiesto_(1996)
esBisiesto_(2000)
esBisiesto_(2006)
esBisiesto_(2012)
esBisiesto_(2020)
esBisiesto_(-2020)
¡El año debe ser entero positivo!
esBisiesto_(2020.32)
¡El año debe ser entero positivo!

Mayor de 3 números

En este ejemplo se elabora una función que recibe tres números y devuelve el mayor ellos.

Como de costumbre, se pueden plantear varias alternativas de solución. Probablemente la más sencilla consiste en determinar si el primer número es el mayor, de no ser así, se comprueba si el segundo es el mayor y de no ser así el mayor (por defecto) es el tercero.

Por supuesto, el orden puede ser cambiado sin que en realidad cambie la lógica. El algoritmo, en forma de diagrama de flujo, es:

gojsGraph({divi, modelo: {"class":"go.GraphLinksModel","linkFromPortIdProperty":"fromPort","linkToPortIdProperty":"toPort","nodeDataArray":[{"category":"Conditional","text":"a>=b y a>=c","key":-4,"loc":"-144 -345.00000000000006"},{"category":"Start","text":"mayor","key":-1,"loc":"-144 -454"},{"category":"Input","text":"números: a, b, c","key":-3,"loc":"-143.99999999999994 -403"},{"category":"Start","text":"fin","key":-9,"loc":"-145 -147"},{"category":"Output","text":"a","key":-7,"loc":"-15.999999999999886 -221.99999999999983"},{"category":"Output","text":"c","key":-10,"loc":"-388.9999999999999 -221.99999999999983"},{"category":"Output","text":"b","key":-11,"loc":"-144.9999999999999 -221.99999999999983"},{"category":"Conditional","text":"b>=a y b>=c","key":-8,"loc":"-271 -282.00000000000006"}],"linkDataArray":[{"from":-1,"to":-3,"fromPort":"B","toPort":"T","points":[-144,-438.7333213806152,-144,-428.7333213806152,-144,-428,-143.99999999999994,-428,-143.99999999999994,-427.2666786193848,-143.99999999999994,-417.2666786193848]},{"from":-3,"to":-4,"fromPort":"B","toPort":"T","points":[-143.99999999999994,-388.7333213806152,-143.99999999999994,-378.7333213806152,-143.99999999999994,-376.8833393096924,-144,-376.8833393096924,-144,-375.0333572387696,-144,-365.0333572387696]},{"from":-4,"to":-7,"fromPort":"R","toPort":"T","visible":true,"points":[-64.41780090332031,-345.00000000000006,-54.41780090332031,-345.00000000000006,-15.999999999999886,-345.00000000000006,-15.999999999999886,-297.30556551615393,-15.999999999999886,-249.61113103230775,-15.999999999999886,-239.61113103230775],"text":"Si"},{"from":-4,"to":-8,"fromPort":"L","toPort":"T","visible":true,"points":[-223.5821990966797,-345.00000000000006,-233.5821990966797,-345.00000000000006,-271,-345.00000000000006,-271,-328.51667861938483,-271,-312.0333572387696,-271,-302.0333572387696],"text":"No"},{"from":-8,"to":-11,"fromPort":"R","toPort":"T","visible":true,"points":[-190.65548706054688,-282.00000000000006,-180.65548706054688,-282.00000000000006,-144.9999999999999,-282.00000000000006,-144.9999999999999,-265.80556551615393,-144.9999999999999,-249.61113103230775,-144.9999999999999,-239.61113103230775],"text":"Si"},{"from":-8,"to":-10,"fromPort":"L","toPort":"T","visible":true,"points":[-351.3445129394531,-282.00000000000006,-361.3445129394531,-282.00000000000006,-388.9999999999999,-282.00000000000006,-388.9999999999999,-265.80556551615393,-388.9999999999999,-249.61113103230775,-388.9999999999999,-239.61113103230775],"text":"No"},{"from":-11,"to":-9,"fromPort":"B","toPort":"T","points":[-144.9999999999999,-209.31998565673808,-144.9999999999999,-199.31998565673808,-144.9999999999999,-185.79333213806143,-145,-185.79333213806143,-145,-172.26667861938478,-145,-162.26667861938478]},{"from":-7,"to":-9,"fromPort":"B","toPort":"R","points":[-15.999999999999886,-209.31998565673808,-15.999999999999886,-199.31998565673808,-15.999999999999886,-147,-63.72024857964403,-147,-111.44047715928818,-147,-121.44047715928818,-147]},{"from":-10,"to":-9,"fromPort":"B","toPort":"L","points":[-388.9999999999999,-209.31998565673808,-388.9999999999999,-199.31998565673808,-388.9999999999999,-147,-283.77976142035584,-147,-178.5595228407118,-147,-168.5595228407118,-147]}]}})

El código respectivo, implementado en forma de una función estándar, es:

function mayor(a, b, c) {
if (!Number.isFinite(a) || !Number.isFinite(b) || !Number.isFinite(c))
throw "Los datos deben ser de tipo numérico."
if (a>=b && a>=c) return a;
if (b>=a && b>=c) return b;
return c;
};

En este caso, aunque los números pueden ser enteros o reales, se valida que efectivamente sean números (no booleanos, cadenas o arreglos). Con ese fin, se recurre al método estático isFinite de la clase Number, el cual devuelve true si es un número válido y false en caso contrario.

Llamando a la función con algunos valores (arrays) de prueba, se obtiene:

mayor(1, 2, 3)
mayor(1, 3, 2)
mayor(3, 2, 1)
mayor(5, 4, 5)
mayor(4, 7, 4)
mayor(8, 8, 8)

Ecuación cuadrática

En este ejemplo se elabora una función que devuelve las dos soluciones de la ecuación cuadrática:

\[ a x^2 + b x + c= 0 \]

Programando la solución general:

\[ x_{1,2} = \dfrac{-b \pm \sqrt{b^2-4 a c}}{2 a} \]

Las soluciones de la ecuación dependen del valor de la expresión que se encuentra dentro de la raíz cuadrada, denominado discriminante ("d = b2−4ac"). Si el discriminante es positivo, las dos soluciones son reales y distintas (porque la raíz es mayor a cero), si es cero, las soluciones son iguales (porque la raíz es cero) y si es negativo, las soluciones son complejas: un par conjugado (porque la raíz es negativa: imaginaria).

Entonces, para resolver el problema se calcula el valor del discriminante, luego se puede determinar si el discriminantes es positivo, negativo o cero, en cualquier orden, así en la siguiente solución, se determina primero si el discriminante es negativo y luego si es cero, siendo el caso por defecto positivo:

gojsGraph({divi, modelo:{ "class": "go.GraphLinksModel", "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"category":"Start", "text":"cuadratica", "key":-1, "loc":"172 -460"},{"category":"Input", "text":"coeficientes: a, b, c", "key":-3, "loc":"172.00000000000009 -410.9999999999998"},{"category":"Conditional", "text":"d<0", "key":-5, "loc":"171.99999999999994 -297.99999999999994"},{"category":"Start", "text":"fin", "key":-9, "loc":"171 15.000000000000043"},{"category":"Output", "text":"r1, r2", "key":-10, "loc":"171 -36"},{"category":"Process", "text":"d = b**2-4*a*c", "key":-11, "loc":"172 -357"},{"category":"Process", "text":"r = -b/(2*a)", "key":-12, "loc":"303 -245"},{"category":"Process", "text":"im = sqrt(-d)/(2*a)", "key":-13, "loc":"303 -190"},{"category":"Process", "text":"r1 = Complex(r,im)", "key":-14, "loc":"303 -136"},{"category":"Process", "text":"r2= Complex(r,-im)", "key":-15, "loc":"303 -79"},{"category":"Conditional", "text":"d==0", "key":-16, "loc":"61.99999999999994 -237.99999999999994"},{"category":"Process", "text":"r2 = r1", "key":-17, "loc":"171 -110"},{"category":"Process", "text":"r1 = -b/(2*a)", "key":-18, "loc":"171 -165"},{"category":"Process", "text":"r1 = (-b+sqrt(d))/(2*a)", "key":-19, "loc":"-25 -165"},{"category":"Process", "text":"r2 = (-b-sqrt(d))/(2*a)", "key":-20, "loc":"-25 -110"} ], "linkDataArray": [ {"from":-1, "to":-3, "fromPort":"B", "toPort":"T", "points":[172.00000000000006,-444.7333213806151,172.00000000000006,-434.7333213806151,172.00000000000006,-434.7333213806151,172.00000000000006,-435.26667861938455,172.00000000000009,-435.26667861938455,172.00000000000009,-425.26667861938455]},{"from":-10, "to":-9, "fromPort":"B", "toPort":"T", "points":[171,-23.31998565673828,171,-13.319985656738279,171,-11.793332138061507,171.00000000000006,-11.793332138061507,171.00000000000006,-10.266678619384734,171.00000000000006,-0.26667861938473436]},{"from":-12, "to":-13, "fromPort":"B", "toPort":"T", "points":[303,-227.73332138061525,303,-217.73332138061525,303,-217.5,303,-217.5,303,-217.26667861938478,303,-207.26667861938478]},{"from":-13, "to":-14, "fromPort":"B", "toPort":"T", "points":[303,-172.73332138061525,303,-162.73332138061525,303,-162.73332138061525,303,-163.26667861938478,303,-163.26667861938478,303,-153.26667861938478]},{"from":-14, "to":-15, "fromPort":"B", "toPort":"T", "points":[303,-118.73332138061525,303,-108.73332138061525,303,-107.5,303,-107.5,303,-106.26667861938476,303,-96.26667861938476]},{"from":-5, "to":-12, "fromPort":"R", "toPort":"T", "visible":true, "points":[199.70051574707026,-297.99999999999994,209.70051574707026,-297.99999999999994,303,-297.99999999999994,303,-285.13333930969236,303,-272.2666786193848,303,-262.2666786193848], "text":"Si"},{"from":-18, "to":-17, "fromPort":"B", "toPort":"T", "points":[171,-147.73332138061525,171,-137.73332138061525,171,-137.5,171,-137.5,171,-137.26667861938478,171,-127.26667861938478]},{"from":-19, "to":-20, "fromPort":"B", "toPort":"T", "points":[-25,-147.73332138061525,-25,-137.73332138061525,-25,-137.5,-25,-137.5,-25,-137.26667861938478,-25,-127.26667861938478]},{"from":-16, "to":-18, "fromPort":"R", "toPort":"T", "visible":true, "points":[98.2033081054687,-237.99999999999997,108.2033081054687,-237.99999999999997,171,-237.99999999999997,171,-215.13333930969236,171,-192.26667861938478,171,-182.26667861938478], "text":"Si"},{"from":-16, "to":-19, "fromPort":"L", "toPort":"T", "visible":true, "points":[25.796691894531193,-237.99999999999997,15.796691894531193,-237.99999999999997,-25,-237.99999999999997,-25,-215.13333930969236,-25,-192.26667861938478,-25,-182.26667861938478], "text":"No"},{"from":-5, "to":-16, "fromPort":"L", "toPort":"T", "visible":true, "points":[144.29948425292963,-297.99999999999994,134.29948425292963,-297.99999999999994,61.99999999999994,-297.99999999999994,61.99999999999994,-283.0166786193847,61.99999999999994,-268.0333572387695,61.99999999999994,-258.0333572387695], "text":"No"},{"from":-3, "to":-11, "fromPort":"B", "toPort":"T", "points":[172.00000000000009,-396.733321380615,172.00000000000009,-386.733321380615,172.00000000000009,-385.4999999999999,172,-385.4999999999999,172,-384.2666786193848,172,-374.2666786193848]},{"from":-11, "to":-5, "fromPort":"B", "toPort":"T", "points":[172,-339.7333213806152,172,-329.7333213806152,172,-328.88333930969236,171.99999999999994,-328.88333930969236,171.99999999999994,-328.0333572387695,171.99999999999994,-318.0333572387695]},{"from":-17, "to":-10, "fromPort":"B", "toPort":"T", "points":[171,-92.73332138061525,171,-82.73332138061525,171,-73.1722262064616,171,-73.1722262064616,171,-63.611131032307945,171,-53.611131032307945]},{"from":-15, "to":-10, "fromPort":"B", "toPort":"R", "points":[303,-61.73332138061522,303,-51.73332138061522,303,-36,258.43970489501953,-36,213.87940979003906,-36,203.87940979003906,-36]},{"from":-20, "to":-10, "fromPort":"B", "toPort":"L", "points":[-25,-92.73332138061525,-25,-82.73332138061525,-25,-36,51.56029510498047,-36,128.12059020996094,-36,138.12059020996094,-36]} ]}})

Al igual que en el ejemplo anterior, se valida que los tres datos sean efectivamente números (con isFinite):

var cuadratica = (a, b, c) => {
const d = b**2-4*a*c;
let r, i, r1, r2;
if (d<0) {
r = -b/(2*a);
i = Math.sqrt(-d)/(2*a);
r1 = Complex(r, i);
r2 = Complex(r, -i);
} else
if (d===0) {
r1 = -b/(2*a);
r2 = r1;
} else {
r1 = (-b+Math.sqrt(d))/(2*a);
r2 = (-b-Math.sqrt(d))/(2*a);
}
return [r1, r2];
};

Como se puede ver, para devolver los resultados complejos, se ha empleado la clase Complex, la cual crea un número complejo a partir de las partes real e imaginaria del número.

Llamando a la función con los coeficientes de algunas ecuaciones cuadráticas, se obtiene:

cuadratica(1, -10, 21)
cuadratica(1, -14, 49)
cuadratica(1, 2, 3)