Diciamo x
, a
e b
sono numeri. Devo limitare x
i limiti del segmento [a, b]
.
Posso scrivere Math.max(a, Math.min(x, b))
, ma non credo sia molto facile da leggere. Qualcuno ha un modo intelligente di scrivere questo in un modo più leggibile?
Diciamo x
, a
e b
sono numeri. Devo limitare x
i limiti del segmento [a, b]
.
Posso scrivere Math.max(a, Math.min(x, b))
, ma non credo sia molto facile da leggere. Qualcuno ha un modo intelligente di scrivere questo in un modo più leggibile?
Risposte:
Il modo in cui lo fai è piuttosto standard. È possibile definire una clamp
funzione di utilità :
/**
* Returns a number whose value is limited to the given range.
*
* Example: limit the output of this computation to between 0 and 255
* (x * 255).clamp(0, 255)
*
* @param {Number} min The lower boundary of the output range
* @param {Number} max The upper boundary of the output range
* @returns A number in the range [min, max]
* @type Number
*/
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
(Sebbene l'estensione del linguaggio integrato sia generalmente disapprovata)
Math.min
e Math.max
è irrilevante purché il parametro di Math.min
is max
e il parametro di Math.max
is min
.
Number.prototype
in questo caso) è sconsigliata per una serie di motivi. Vedi "Cattiva pratica: estensione di prototipi nativi" su developer.mozilla.org/en-US/docs/Web/JavaScript/…
un approccio meno orientato alla "matematica", ma dovrebbe funzionare anche in questo modo, il test </> è esposto (forse più comprensibile del minimoxing) ma dipende davvero da cosa intendi per "leggibile"
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}
Math.random()
chiamata molto più costosa , questo semplice approccio è 10 volte più veloce .
Aggiornamento per ECMAScript 2017:
Math.clamp(x, lower, upper)
Ma nota che da oggi è una proposta Stage 1 . Fino a quando non viene ampiamente supportato, è possibile utilizzare un polyfill .
Questa non vuole essere una risposta "usa solo una biblioteca" ma nel caso in cui tu stia usando Lodash puoi usare .clamp
:
_.clamp(yourInput, lowerBound, upperBound);
Così che:
_.clamp(22, -10, 10); // => 10
Ecco la sua implementazione, presa dalla fonte Lodash :
/**
* The base implementation of `_.clamp` which doesn't coerce arguments.
*
* @private
* @param {number} number The number to clamp.
* @param {number} [lower] The lower bound.
* @param {number} upper The upper bound.
* @returns {number} Returns the clamped number.
*/
function baseClamp(number, lower, upper) {
if (number === number) {
if (upper !== undefined) {
number = number <= upper ? number : upper;
}
if (lower !== undefined) {
number = number >= lower ? number : lower;
}
}
return number;
}
Inoltre, vale la pena notare che Lodash rende disponibili singoli metodi come moduli autonomi, quindi nel caso in cui sia necessario solo questo metodo, è possibile installarlo senza il resto della libreria:
npm i --save lodash.clamp
Se sei in grado di utilizzare le funzioni della freccia es6, puoi anche utilizzare un approccio applicativo parziale:
const clamp = (min, max) => (value) =>
value < min ? min : value > max ? max : value;
clamp(2, 9)(8); // 8
clamp(2, 9)(1); // 2
clamp(2, 9)(10); // 9
or
const clamp2to9 = clamp(2, 9);
clamp2to9(8); // 8
clamp2to9(1); // 2
clamp2to9(10); // 9
Nello spirito della sensualità delle frecce, potresti creare un micro clamp / vincolo / gate / ecc. funzione utilizzando i parametri di riposo
var clamp = (...v) => v.sort((a,b) => a-b)[1];
Quindi passa tre valori
clamp(100,-3,someVar);
Cioè, di nuovo, se per sexy intendi "breve"
clamp
, perf avrà un colpo serio se hai una logica complessa (anche se è "sexy")
Questo espande l'opzione ternaria in if / else che è minimizzata è equivalente all'opzione ternaria ma non sacrifica la leggibilità.
const clamp = (value, min, max) => {
if (value < min) return min;
if (value > max) return max;
return value;
}
Riduce a 35b (o 43b se si utilizza function
):
const clamp=(c,a,l)=>c<a?a:c>l?l:c;
Inoltre, a seconda dello strumento perf o del browser in uso, si ottengono vari risultati se l'implementazione basata sulla matematica o l'implementazione basata sul ternario è più veloce. Nel caso della stessa prestazione, opterei per la leggibilità.
Il mio preferito:
[min,x,max].sort()[1]
[1,5,19].sort()[1]
restituisce 19. Potrebbe essere risolto in questo modo: [min,x,max].sort(function(a, b) { return a - b; })[1]
ma questo non è più sexy. Oltre a quando usato pesantemente potrebbe essere un problema di prestazioni per creare un nuovo array solo per confrontare tre numeri.
[min, x, max].sort((a,b) => a-b)[1]