Intercambio incondicional loco
Recientemente, me encontré con una tarea de manera inmutable para intercambiar dos elementos en una matriz por sus índices. La tarea es bastante sencilla. Por tanto, resolviéndolo de forma razonable:
const swap = (arr, ind1, ind2) =>
arr.map((e, i) => {
if (i === ind1) return arr[ind2]
if (i === ind2) return arr[ind1]
return e
})
Quería resolverlo de una manera loca. Pensé que sería interesante resolver este problema:
- Sin operadores de comparación y operadores lógicos (
&&
,||
, ...) - Sin bucles ni ifs ni operadores ternarios
- Sin utilizar estructuras de datos adicionales
- Sin casting
Reducir el problema a uno más pequeño
De hecho, esta tarea puede reducirse a una menor. Para demostrar esto, reescribamos el código de arriba de esta manera:
const swap = (arr, ind1, ind2) => {
return arr.map((elem, i) => {
const index = i === ind1 ? ind2 : i === ind2 ? ind1 : i
return arr[index]
})
}
index
, . — ind1
, ind2
. ind2
ind1
. , — — index
.
index
getSwapIndex(i, ind1, ind2)
.
const getSwapIndex(i, ind1, ind2) {
return i === ind1
? ind2
: i === ind2
? ind1
: i
}
const swap = (arr, ind1, ind2) => arr.map((_, i) => arr[getSwapIndex(i, ind1, ind2)])
swap
. ,
— . getSwapIndex
, . , 1 0. 1 , 0 .
:
type NumberBoolean = 1 | 0
""
const or = (condition1, condition2) => condition1 + condition2 - condition1 * condition2
, 1 0. "" .
or(0, 0) => 0 + 0 - 0 * 0 => 0
or(0, 1) => 0 + 1 - 0 * 1 => 1
or(1, 0) => 1 + 0 - 1 * 0 => 1
or(1, 1) => 1 + 1 - 1 * 1 => 1
, :
const or = (c1, c2) => Math.sign(c1 + c2)
La función Math.sign
devuelve el "signo" de su primer parámetro:
Math.sign(-23) = -1
Math.sign(0) = 0
Math.sign(42) = 1
, , .
const R = ? R1 : R2
// - , R1, R2 - .
// ,
const R = P * R1 + (1 - P) * R2
// - . === true, P 1, === false, P 0.
P === 0
, R = 0 * R1 + (1 - 0) * R2 = R2
.
P === 1
, R = 1 * R1 + (1 - 1) * R2 = R1
.
— .
ternary(c, r1, r2)
:
function ternary(p: NumberBoolean, r1: number, r2: number): number {
return p * r1 + (1 - p) * r2
}
. :
isEqual(a: number, b: number): NumberBoolean
:
const isEqual = (a, b) => {
return 1 - Math.sign(Math.abs(a - b))
}
Math.abs
:
Math.abs(-23) = 23
Math.abs(0) = 0
Math.abs(42) = 42
, , . a
b
— , :
isEqual(a, b)
=> 1 - Math.sign(Math.abs(a - b))
=> 1 - Math.sign(Math.abs(0))
=> 1 - Math.sign(0)
=> 1 - 0
=> 1
, :
isEqual(a, b)
=> 1 - Math.sign(Math.abs(a - b))
=> 1 - Math.sign(Math.abs( ))
=> 1 - Math.sign( ))
=> 1 - 1
=> 0
.
--
getSwapIndex
:
const getSwapIndex = (i, ind1, ind2) =>
ternary(isEqual(i, ind1), ind2, ternary(isEqual(i, ind2), ind1, i))
:
const isEqual = (a, b) => 1 - Math.sign(Math.abs(a - b))
const ternary = (p, r1, r2) => p * r1 + (1 - p) * r2
const getSwapIndex = (i, ind1, ind2) =>
ternary(isEqual(i, ind1), ind2, ternary(isEqual(i, ind2), ind1, i))
const swap = (arr, ind1, ind2) => arr.map((_, i) => arr[getSwapIndex(i, ind1, ind2)])
, , .
, , :
const getSwapIndex = (i, ind1, ind2) => {
const shouldSwap = or(isEqual(i, ind1), isEqual(i, ind2))
return ternary(shouldSwap, ind1 + ind2 - i, i)
}
:
, !