Скажем x, aи bявляются числами. Мне нужно xограничить границы сегмента [a, b].
Я умею писать Math.max(a, Math.min(x, b)), но не думаю, что это очень легко читать. Есть ли у кого-нибудь умный способ написать это более читаемым способом?
Скажем x, aи bявляются числами. Мне нужно xограничить границы сегмента [a, b].
Я умею писать Math.max(a, Math.min(x, b)), но не думаю, что это очень легко читать. Есть ли у кого-нибудь умный способ написать это более читаемым способом?
Ответы:
То, как вы это делаете, довольно стандартно. Вы можете определить служебную clampфункцию:
/**
* 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);
};
(Хотя расширение встроенных языков обычно не одобряется)
Math.minи не Math.maxимеет значения, пока параметр Math.minis maxи параметр Math.maxis min.
Number.prototypeв данном случае) не рекомендуется по ряду причин. См. «Плохая практика: расширение собственных прототипов» на developer.mozilla.org/en-US/docs/Web/JavaScript/…
менее «математический» подход, но он также должен работать, таким образом, тест </> выставлен (возможно, более понятен, чем минимаксинг), но это действительно зависит от того, что вы имеете в виду под «читаемым»
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}
Math.random()вызова, этот простой подход будет в 10 раз быстрее .
Обновление для ECMAScript 2017:
Math.clamp(x, lower, upper)
Но обратите внимание , что на сегодняшний день, это этап 1 предложение . Пока он не получит широкой поддержки, вы можете использовать полифил .
Это не должно быть ответом типа «просто используйте библиотеку», но на всякий случай, если вы используете Lodash, вы можете использовать .clamp:
_.clamp(yourInput, lowerBound, upperBound);
Так что:
_.clamp(22, -10, 10); // => 10
Вот его реализация, взятая из источника 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;
}
Также стоит отметить, что Lodash делает отдельные методы доступными в виде отдельных модулей, поэтому, если вам нужен только этот метод, вы можете просто установить его без остальной части библиотеки:
npm i --save lodash.clamp
Если вы можете использовать стрелочные функции es6, вы также можете использовать подход частичного приложения:
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
В духе сексуальности стрелы вы можете создать микрозажим / ограничение / гейт / и т. Д. функция с использованием параметров отдыха
var clamp = (...v) => v.sort((a,b) => a-b)[1];
Затем просто передайте три значения
clamp(100,-3,someVar);
То есть, опять же, если под сексуальным вы имеете в виду «короткий»
clamp, perf серьезно пострадает, если у вас сложная логика (даже если она «сексуальна»)
Это расширяет тернарный вариант до if / else, который минимизирован, эквивалентен тернарному варианту, но не жертвует удобочитаемостью.
const clamp = (value, min, max) => {
if (value < min) return min;
if (value > max) return max;
return value;
}
Уменьшается до 35b (или 43b при использовании function):
const clamp=(c,a,l)=>c<a?a:c>l?l:c;
Кроме того, в зависимости от того, какой инструмент perf или браузер вы используете, вы получите различные результаты, будь то реализация на основе математики или троичная реализация быстрее. В случае примерно такой же производительности я бы выбрал удобочитаемость.
Моя любимая:
[min,x,max].sort()[1]
[1,5,19].sort()[1]возвращает 19. Это можно исправить вот так: [min,x,max].sort(function(a, b) { return a - b; })[1]но это уже не сексуально. Помимо интенсивного использования, создание нового массива для сравнения трех чисел может вызвать проблемы с производительностью.
[min, x, max].sort((a,b) => a-b)[1]