Diciamo x, ae bsono numeri. Devo limitare xi 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, ae bsono numeri. Devo limitare xi 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 clampfunzione 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.mine Math.maxè irrilevante purché il parametro di Math.minis maxe il parametro di Math.maxis min.
Number.prototypein 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]