วิธีใดเป็นวิธีที่ดีที่สุดในการ จำกัด จำนวนให้กับกลุ่ม?


127

สมมติว่าx, aและbตัวเลข ฉันจำเป็นต้องหมวกขอบเขตของส่วนx[a, b]

ฉันเขียนMath.max(a, Math.min(x, b))ได้ แต่ไม่คิดว่าจะอ่านง่ายขนาดนี้ ใครมีวิธีที่ชาญฉลาดในการเขียนสิ่งนี้ด้วยวิธีที่อ่านง่ายกว่านี้?

คำตอบ:


197

วิธีที่คุณทำนั้นค่อนข้างได้มาตรฐาน คุณสามารถกำหนด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);
};

(แม้ว่าโดยทั่วไปแล้วการขยายภาษาในตัวจะถูกขมวด)


OOps you copy / paste my typo (swapped max and min)
Samuel Rossille

5
ไม่ฉันได้มาจากเว็บไซต์ การสั่งซื้อการทำรัง / การดำเนินการMath.minและMath.maxไม่เกี่ยวข้องตราบเท่าที่พารามิเตอร์ของMath.minเป็นmaxและพารามิเตอร์ของมีMath.max min
Otto Allmendinger

23
การขยายต้นแบบดั้งเดิม ( Number.prototypeในกรณีนี้) ไม่ได้รับคำแนะนำจากหลายสาเหตุ ดู "แนวทางปฏิบัติที่ไม่ดี: ส่วนขยายของต้นแบบดั้งเดิม" ที่developer.mozilla.org/en-US/docs/Web/JavaScript/…
broofa

67

วิธีที่เน้น "คณิตศาสตร์" น้อยลง แต่ก็ควรได้ผลเช่นกันด้วยวิธีนี้การทดสอบ </> จะถูกเปิดเผย (อาจเข้าใจได้มากกว่าการย่อขนาด) แต่จริงๆแล้วขึ้นอยู่กับความหมายของคุณโดย "อ่านได้"

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

4
ดีกว่าที่จะแทนที่ <and> ด้วย <= และ> = ในสำนวนนี้เพื่อให้มีการเปรียบเทียบน้อยลง ด้วยการแก้ไขนั้นนี่เป็นคำตอบที่ฉันต้องการอย่างแน่นอน: จำนวนนาทีที่สับสนและเกิดข้อผิดพลาดได้ง่าย
Don Hatch

นี่คือคำตอบที่ดีที่สุด เร็วกว่ามากง่ายกว่ามากอ่านง่ายกว่ามาก
tristan

5
มันไม่เร็วขึ้น Math.min / max solution เร็วที่สุด jsperf.com/math-clamp/9
Manuel Di Iorio

1
@ManuelDiIorio ฮะ? ภายใต้รุ่นปัจจุบันของ Chrome ที่ ternary ง่ายรวดเร็วเป็นสองเท่า Math.min / MAX - และถ้าคุณลบค่าใช้จ่ายจากไกลมีราคาแพงกว่าMath.random()การโทรวิธีการง่ายๆที่นี้คือ10 x เร็วขึ้น
mindplay.dk

4
การถอดค่าใช้จ่ายจาก Math.random () ที่โทร Math.min / แก้ปัญหาสูงสุดคือเร็วที่สุด โซลูชันสามส่วนนี้ช้าลง 80%
idbrii

48

อัปเดตสำหรับ ECMAScript 2017:

Math.clamp(x, lower, upper)

แต่ทราบว่า ณ วันนี้ก็เป็นขั้นตอนที่ 1 ข้อเสนอ จนกว่าจะได้รับการสนับสนุนอย่างกว้างขวางคุณสามารถใช้polyfill


4
หากคุณต้องการติดตามข้อเสนอนี้และอื่น ๆ โปรดดูที่github.com/tc39/proposals
gfullam

5
ฉันไม่พบข้อเสนอนี้ใน tc39 / ข้อเสนอ repo
Colin D

สำหรับผู้ที่กำลังมองหาข้อเสนอจะแสดงอยู่ในข้อเสนอขั้นที่ 1เป็น "ส่วนขยายคณิตศาสตร์" ข้อเสนอที่แท้จริงอยู่ที่นี่: github.com/rwaldron/proposal-math-extensions
WickyNilliams

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

2
วิธีนี้เร็วขึ้นจริงแม้ว่าฉันจะพิจารณาขยายprototypeความสง่างามมากเมื่อต้องใช้ฟังก์ชันนี้จริงๆ
MaxArt

11

สิ่งนี้ไม่ได้ต้องการเป็นคำตอบ "just-use-a-library" แต่ในกรณีที่คุณใช้ 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

6

หากคุณสามารถใช้ฟังก์ชันลูกศร 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

การสร้างฟังก์ชันทุกครั้งที่คุณต้องการจับตัวเลขดูไม่มีประสิทธิภาพมาก
จอร์จ

1
อาจขึ้นอยู่กับกรณีการใช้งานของคุณ นี่เป็นเพียงฟังก์ชั่นของโรงงานในการสร้างแคลมป์ด้วยช่วงที่กำหนด คุณสามารถสร้างครั้งเดียวและใช้ซ้ำได้ตลอดทุกแอปของคุณ ไม่ต้องเรียกหลายครั้ง นอกจากนี้อาจไม่ใช่เครื่องมือสำหรับทุกกรณีการใช้งานที่คุณไม่จำเป็นต้องล็อกในช่วงที่กำหนด
bingles

@ George ถ้าคุณไม่จำเป็นต้องเก็บช่วงเพียงแค่ลบฟังก์ชันลูกศรด้านในและย้ายพารามิเตอร์ค่าไปยังฟังก์ชันลูกศรด้านนอก
Alisson Nunes


0

ด้วยจิตวิญญาณแห่งความเซ็กซี่ของลูกศรคุณสามารถสร้างที่หนีบขนาดเล็ก / ข้อ จำกัด / ประตู / & c ฟังก์ชันโดยใช้พารามิเตอร์ส่วนที่เหลือ

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

จากนั้นส่งผ่านสามค่า

clamp(100,-3,someVar);

นั่นคืออีกครั้งถ้าโดยเซ็กซี่คุณหมายถึง 'สั้น'


คุณไม่ควรใช้อาร์เรย์และการเรียงลำดับสำหรับ a clampperf จะได้รับความนิยมอย่างมากหากคุณมีตรรกะที่ซับซ้อน (แม้ว่ามันจะ "เซ็กซี่"
ก็ตาม

0

สิ่งนี้จะขยายอ็อพชัน ternary เป็น if / else ซึ่ง minified นั้นเทียบเท่ากับอ็อพชัน ternary แต่ไม่ลดทอนความสามารถในการอ่าน

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;

นอกจากนี้ขึ้นอยู่กับว่าคุณใช้เครื่องมือหรือเบราว์เซอร์ที่สมบูรณ์แบบใดคุณจะได้ผลลัพธ์ที่หลากหลายว่าการใช้งานตามคณิตศาสตร์หรือการใช้งานตามหลักสามนั้นเร็วกว่า ในกรณีที่มีประสิทธิภาพใกล้เคียงกันฉันจะเลือกให้อ่านง่าย


-5

ของโปรด:

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

12
การลงคะแนนเนื่องจากไม่ได้ผล [1,5,19].sort()[1]ผลตอบแทน 19. อาจแก้ไขได้เช่นนี้[min,x,max].sort(function(a, b) { return a - b; })[1]แต่มันไม่เซ็กซี่อีกต่อไป นอกจากนี้เมื่อใช้งานหนักอาจเป็นปัญหาด้านประสิทธิภาพในการสร้างอาร์เรย์ใหม่เพื่อเปรียบเทียบตัวเลขสามตัว
kayahr

5
ให้ +1 นี้ต่อไปเพราะความเซ็กซี่คือสิ่งที่มีค่า
Don Hatch

3
IMHO นี่อ่านได้ง่ายกว่าตัวเลือกอื่น ๆ โดยเฉพาะอย่างยิ่งกับฟังก์ชั่นลูกศร:[min, x, max].sort((a,b) => a-b)[1]
zaius

4
สาเหตุที่ไม่ได้ผลเสมอไปเนื่องจากวิธีการจัดเรียงไม่ได้เปรียบเทียบค่าตัวเลขอย่างถูกต้อง (ถือว่าพวกเขาเหมือนสตริง)
John Leuenhagen

ฉันคิดว่าไม่มีอะไรเซ็กซี่ไปกว่าฟังก์ชันที่มีชื่อเหมาะเจาะซึ่งทำงานได้อย่างมีประสิทธิภาพและในลักษณะที่ถูกต้องอย่างเห็นได้ชัด แต่นั่นอาจเป็นเรื่องของรสนิยม
BallpointBen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.