Cách thanh lịch nhất để giới hạn một số cho một phân khúc là gì?


127

Hãy nói rằng x, ablà những con số. Tôi cần phải giới hạn xđến giới hạn của phân khúc [a, b].

Tôi có thể viết Math.max(a, Math.min(x, b)), nhưng tôi không nghĩ nó rất dễ đọc. Có ai có một cách thông minh để viết này theo cách dễ đọc hơn không?

Câu trả lời:


197

Cách bạn làm là khá chuẩn. Bạn có thể xác định một clampchức năng tiện ích :

/**
 * 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);
};

(Mặc dù mở rộng ngôn ngữ tích hợp thường được tán thành)


OOps bạn sao chép / dán lỗi đánh máy của tôi (hoán đổi tối đa và tối thiểu)
Samuel Rossille

5
Không, tôi đã nhận nó từ trang web. Thứ tự lồng / thực hiện của Math.minMath.maxkhông liên quan miễn là tham số của Math.minmaxvà tham số của Math.maxmin.
Otto Allmendinger

23
Mở rộng các nguyên mẫu bản địa ( Number.prototypetrong trường hợp này) không được khuyến khích vì nhiều lý do. Xem "Thực hành xấu: Mở rộng các nguyên mẫu bản địa" tại developer.mozilla.org/en-US/docs/Web/JavaScript/iêu
broofa

68

một cách tiếp cận theo hướng "Toán học" ít hơn, nhưng cũng nên hoạt động, theo cách này, bài kiểm tra </> được đưa ra (có thể dễ hiểu hơn là minimaxing) nhưng nó thực sự phụ thuộc vào ý của bạn là "có thể đọc được"

function clamp(num, min, max) {
  return num <= min ? min : num >= max ? max : num;
}

4
Tốt hơn là thay thế <và> bằng <= và> = trong thành ngữ này, để có khả năng thực hiện một so sánh ít hơn. Với sự điều chỉnh đó, đây chắc chắn là câu trả lời ưa thích của tôi: tối đa phút là khó hiểu và dễ bị lỗi.
Don nở

Đây là câu trả lời tốt nhất. Nhanh hơn nhiều, đơn giản hơn nhiều, dễ đọc hơn nhiều.
tristan

5
Nó không nhanh hơn. Giải pháp Math.min
Manuel Di Iorio

1
@ManuelDiIorio hả? trong phiên bản Chrome hiện tại, ternary đơn giản nhanh gấp đôi Math.min / max - và nếu bạn loại bỏ chi phí từ Math.random()cuộc gọi đắt hơn nhiều , cách tiếp cận đơn giản này nhanh hơn 10 lần .
mindplay.dk


48

Cập nhật cho ECMAScript 2017:

Math.clamp(x, lower, upper)

Nhưng lưu ý rằng tính đến hôm nay, đó là đề xuất Giai đoạn 1 . Cho đến khi nó được hỗ trợ rộng rãi, bạn có thể sử dụng một polyfill .


4
Nếu bạn muốn theo dõi đề xuất này và những người khác, hãy xem: github.com/tc39/proposeals
gfullam 20/11/18

5
Tôi không tìm thấy đề xuất này trong repo tc39 / đề xuất
Colin D

Đối với những người tìm kiếm, đề xuất được liệt kê trong các đề xuất Giai đoạn 1 là "Phần mở rộng toán học". Đề xuất thực tế có tại đây: github.com/rwaldron/proposed-math-extensions
WickyNilliams

14
Math.clip = function(number, min, max) {
  return Math.max(min, Math.min(number, max));
}

2
Giải pháp này thực sự là cách nhanh hơn, mặc dù tôi xem xét việc mở rộng prototyperất thanh lịch khi thực sự sử dụng chức năng.
MaxArt

11

Đây không muốn là câu trả lời "chỉ sử dụng một thư viện" nhưng chỉ trong trường hợp bạn đang sử dụng Lodash, bạn có thể sử dụng .clamp:

_.clamp(yourInput, lowerBound, upperBound);

Vậy nên:

_.clamp(22, -10, 10); // => 10

Đây là cách thực hiện của nó, được lấy từ nguồn 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;
}

Ngoài ra, điều đáng chú ý là Lodash cung cấp các phương thức đơn lẻ dưới dạng các mô-đun độc lập, vì vậy trong trường hợp bạn chỉ cần phương thức này, bạn chỉ có thể cài đặt nó mà không cần phần còn lại của thư viện:

npm i --save lodash.clamp

6

Nếu bạn có thể sử dụng các chức năng mũi tên es6, bạn cũng có thể sử dụng phương pháp ứng dụng một phần:

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

Tạo một hàm mỗi khi bạn muốn kẹp một số trông rất kém hiệu quả
George

1
Có lẽ phụ thuộc vào trường hợp sử dụng của bạn. Đây chỉ là một chức năng của nhà máy để tạo ra kẹp với một phạm vi thiết lập. Bạn có thể tạo một lần và sử dụng lại trong suốt ứng dụng của mình. không phải gọi nhiều lần. Cũng có thể không phải là công cụ cho tất cả các trường hợp sử dụng mà bạn không cần phải khóa trong phạm vi đã đặt.
trận đấu

@George nếu bạn không cần giữ phạm vi, chỉ cần xóa chức năng mũi tên bên trong và di chuyển tham số giá trị sang chức năng mũi tên bên ngoài
Alisson Nunes

6

Nếu bạn không muốn xác định bất kỳ chức năng nào, viết nó như thế Math.min(Math.max(x, a), b)không phải là xấu.


0

Theo tinh thần của sự gợi cảm của mũi tên, bạn có thể tạo ra một kẹp / kẹp / cổng / & c. chức năng sử dụng các tham số nghỉ ngơi

var clamp = (...v) => v.sort((a,b) => a-b)[1];

Sau đó, chỉ cần vượt qua trong ba giá trị

clamp(100,-3,someVar);

Đó là, một lần nữa, nếu gợi cảm, bạn có nghĩa là 'ngắn'


bạn không nên sử dụng mảng và sắp xếp cho một clamp, hoàn hảo sẽ gây ảnh hưởng nghiêm trọng nếu bạn có logic phức tạp (ngay cả khi nó "gợi cảm")
Andrew McOlash

0

Điều này mở rộng tùy chọn ternary thành if / other mà rút gọn tương đương với tùy chọn ternary nhưng không hy sinh khả năng đọc.

const clamp = (value, min, max) => {
  if (value < min) return min;
  if (value > max) return max;
  return value;
}

Giảm tối đa xuống 35b (hoặc 43b nếu sử dụng function):

const clamp=(c,a,l)=>c<a?a:c>l?l:c;

Ngoài ra, tùy thuộc vào công cụ hoàn hảo hoặc trình duyệt bạn sử dụng, bạn sẽ nhận được các kết quả khác nhau về việc triển khai dựa trên Toán học hoặc triển khai dựa trên phương pháp nhanh hơn. Trong trường hợp có hiệu suất gần như nhau, tôi sẽ chọn khả năng đọc.


-5

Yêu thích của tôi:

[min,x,max].sort()[1]

12
Downvote vì nó không hoạt động. [1,5,19].sort()[1]trả về 19. Nó có thể được sửa như thế này: [min,x,max].sort(function(a, b) { return a - b; })[1]nhưng điều này không còn gợi cảm nữa. Bên cạnh khi được sử dụng nhiều, nó có thể là một vấn đề hiệu năng để tạo ra một mảng mới chỉ để so sánh ba số.
kayahr

5
Dù sao đi nữa, hãy cho +1 này, vì gợi cảm mới là điều quan trọng.
Don nở

3
IMHO điều này dễ đọc hơn nhiều so với bất kỳ tùy chọn nào khác, đặc biệt là với các chức năng mũi tên:[min, x, max].sort((a,b) => a-b)[1]
zaius

4
Lý do tại sao điều này không luôn luôn hoạt động là vì phương pháp sắp xếp không so sánh đúng các giá trị số. (Nó đối xử với họ như các chuỗi)
John Leuenhagen

Tôi nghĩ rằng không có gì hấp dẫn hơn một chức năng được đặt tên khéo léo, thực hiện công việc một cách hiệu quả và theo cách rõ ràng chính xác. Nhưng đó có thể là một vấn đề của hương vị.
BallpointBen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.