ลูปเป็นหนึ่งในกลไกที่สำคัญที่สุดในทุกภาษาการเขียนโปรแกรมและลูปไม่ได้เปิดในเกือบทุกโปรแกรมคอมพิวเตอร์ที่มีความสำคัญในทางปฏิบัติ (การเรียงลำดับการสืบค้น ฯลฯ ) การวนรอบยังเป็นส่วนหนึ่งของการเพิ่มประสิทธิภาพโปรแกรม เรามักจะต้องเพิ่มประสิทธิภาพความซับซ้อนของโปรแกรมอย่างต่อเนื่อง แต่เรามีความสัมพันธ์กันอย่างต่อเนื่องในการเลือกระหว่างความซับซ้อนของเวลาและความซับซ้อนของอวกาศเนื่องจากการวนรอบ
ใน JavaScript มี 3 ลูปดั้งเดิมสำหรับ () {} ในขณะที่ () {} และทำ {} ในขณะที่ () และสิ่งที่ใช้กันมากที่สุดคือ () {}
อย่างไรก็ตามสำหรับการวนซ้ำที่น่าจะเป็นไปได้มากที่สุดที่วิศวกร JavaScript เพิกเฉยเมื่อเพิ่มประสิทธิภาพโปรแกรม
มาทบทวนความรู้พื้นฐานก่อน
สำหรับไวยากรณ์ของจาวาสคริปต์นั้นสืบทอดมาจากภาษา C และมีสองวิธีในการใช้ไวยากรณ์พื้นฐานของลูป
1. การวนรอบอาร์เรย์
ไวยากรณ์พื้นฐานของสำหรับการวนรอบ
การคัดลอกรหัสมีดังนี้:
สำหรับ ( / * การเริ่มต้น * /2 / * เงื่อนไขการตัดสิน * /2 / * การประมวลผลลูป * /) {
// ... รหัสตรรกะ
-
เราจะอธิบายรายละเอียดด้วยรหัสอินสแตนซ์
การคัดลอกรหัสมีดังนี้:
var array = [1, 2, 3, 4, 5];
var sum = 0;
สำหรับ (var i = 0, len = array.length; i <len; ++ i) {
sum += array [i];
-
console.log ('ผลรวมของรายการอาร์เรย์/' คือ %d. ', ผลรวม);
// => ผลรวมของรายการอาร์เรย์คือ 15
ในรหัสนี้ก่อนอื่นเราจะกำหนดและเริ่มต้นอาร์เรย์ที่เก็บรายการที่จะสะสมและตัวแปรการสร้างผลรวม ต่อไปเราเริ่มวนรอบ ในรหัสการเริ่มต้นของสิ่งนี้สำหรับลูปเรายังกำหนดและเริ่มต้นตัวแปรสองตัว: I (ตัวนับ) และ LEN (นามแฝงของความยาวอาร์เรย์ลูป) เมื่อฉันน้อยกว่า LEN เงื่อนไขการวนซ้ำจะถูกสร้างขึ้นและรหัสเชิงตรรกะจะถูกดำเนินการ; หลังจากเรียกใช้งานโค้ดลอจิคัลแต่ละตัวฉันจะเพิ่มขึ้น 1
ในรหัสตรรกะของลูปเราเพิ่มข้อกำหนดอาร์เรย์ของลูปปัจจุบันลงในตัวแปรผลรวม
วงจรนี้แสดงโดยผังงานดังนี้:
จากแผนภูมิการไหลนี้มันไม่ยากที่จะพบว่าร่างกายลูปจริงในโปรแกรมไม่เพียง แต่มีรหัสตรรกะของเราเท่านั้น แต่ยังรวมถึงการตัดสินการดำเนินการและการประมวลผลลูปที่ใช้ลูปเอง
ด้วยวิธีนี้แนวคิดการเพิ่มประสิทธิภาพของเราจะชัดเจนและเราสามารถเพิ่มประสิทธิภาพจากสี่ด้าน
1. รหัสการเริ่มต้นก่อนที่ร่างกายลูป
2. การดำเนินการตามเงื่อนไขการตัดสินในร่างกายลูป
3. รหัสตรรกะ
4. รหัสการประมวลผลหลังจากรหัสตรรกะ
PS: มีความสัมพันธ์ที่สำคัญระหว่างจุดแรกและจุดที่สอง
1.1 การเพิ่มประสิทธิภาพรหัสการเริ่มต้นและเงื่อนไขการตัดสินการดำเนินการ
ก่อนอื่นมาดูรหัสชิ้นหนึ่งที่ทุกคนคุ้นเคย
การคัดลอกรหัสมีดังนี้:
// ผิด!
สำหรับ (var i = 02 i <list.length2 ++ i) {
// ... รหัสตรรกะ
-
ฉันเชื่อว่าวิศวกรส่วนใหญ่ที่เขียน JavaScript ยังคงใช้วิธีการวนซ้ำที่ดูเหมือนปกติ แต่ทำไมฉันถึงบอกว่ามันผิดที่นี่?
มาแยกกันทุกอย่างในวงนี้และดู:
1. เริ่มต้นรหัส - ลูปนี้จะกำหนดและเริ่มต้นตัวแปรตัวนับเท่านั้น
2. เงื่อนไขการตัดสินการดำเนินการ - มันเป็นความจริงเมื่อตัวนับน้อยกว่าความยาวของรายการ
3. รหัสการประมวลผล - ตัวนับเพิ่มขึ้น 1
มาตรวจสอบผังงานด้านบนและดูว่ามีอะไรผิดปกติหรือไม่?
ร่างกายลูปจริงไม่เพียง แต่มีรหัสเชิงตรรกะของเราเท่านั้น แต่ยังรวมถึงการตัดสินการดำเนินการและรหัสการประมวลผลที่ใช้ลูปเอง กล่าวอีกนัยหนึ่งเงื่อนไขการตัดสิน i <list.length ต้องดำเนินการก่อนแต่ละลูป ใน JavaScript จำเป็นต้องมีการสืบค้นเมื่ออ่านคุณสมบัติหรือวิธีการของวัตถุ
คุณดูเหมือนจะเข้าใจอะไรบางอย่างใช่มั้ย มีการดำเนินการสองครั้งในเงื่อนไขการตัดสินนี้: 1. สอบถามแอตทริบิวต์ความยาวจากอาร์เรย์รายการ 2. เปรียบเทียบขนาดของ i และ list.length
สมมติว่าอาร์เรย์รายการมีองค์ประกอบ n โปรแกรมจำเป็นต้องดำเนินการ 2N ในการตัดสินการดำเนินการของลูปนี้
หากเราเปลี่ยนรหัสเป็นสิ่งนี้:
การคัดลอกรหัสมีดังนี้:
// ดี
สำหรับ (var i = 0, len = list.length; i <len; ++ i) {
-
-
ในรหัสที่ได้รับการปรับปรุงนี้เราได้เพิ่มคำจำกัดความและเริ่มต้นตัวแปร LEN เพื่อจัดเก็บค่าของรายการความยาวในรหัสการเริ่มต้นก่อนการดำเนินการของลูปร่างกาย (เนื้อหาที่เกี่ยวข้องเกี่ยวกับตัวแปรนิพจน์พอยน์เตอร์และค่าจะถูกกล่าวถึงในบทความที่สอง) ด้วยวิธีนี้เราไม่จำเป็นต้องสอบถามอาร์เรย์รายการอีกครั้งในการตัดสินการประหารชีวิตในร่างกายลูปและตัวถูกดำเนินการเป็นครึ่งหนึ่งของต้นฉบับ
ในขั้นตอนข้างต้นเราได้ปรับปรุงความซับซ้อนของเวลาของอัลกอริทึมและเราจะทำอย่างไรถ้าเราต้องการเพิ่มประสิทธิภาพความซับซ้อนของพื้นที่ต่อไป? หากรหัสตรรกะของคุณไม่ จำกัด ตามลำดับลูปคุณสามารถลองใช้วิธีการเพิ่มประสิทธิภาพต่อไปนี้
การคัดลอกรหัสมีดังนี้:
สำหรับ (var i = list.length -1; i> = 0; --i) {
-
-
รหัสนี้จะวนไปข้างหน้าโดยกลับคำสั่งลูปโดยเริ่มจากตัวห้อยองค์ประกอบสุดท้าย (list.length - 1) เพื่อลดจำนวนตัวแปรที่จำเป็นสำหรับลูปเป็น 1 และในการตัดสินการดำเนินการจำนวนการสืบค้นตัวแปรจะลดลงและเวลาที่ใช้ก่อนที่จะดำเนินการคำสั่ง CPU จะลดลง
1.2 การเพิ่มประสิทธิภาพรหัสตรรกะ
ในลูปเราได้รับองค์ประกอบอาร์เรย์ปัจจุบันของลูปตามธรรมชาติเพื่อดำเนินการบางอย่างกับมันหรือใช้มันซึ่งอย่างหลีกเลี่ยงไม่ได้นำไปสู่การโทรหลายครั้งไปยังองค์ประกอบ
การคัดลอกรหัสมีดังนี้:
var array = [
{ชื่อ: 'Will Wen Gunn', พิมพ์: 'Hentai'},
{ชื่อ: 'Vill Lin', ประเภท: 'moegril'}
-
สำหรับ (var i = array.length -1; i> = 0; --i) {
console.log ('ชื่อ: %s', อาร์เรย์ [i] .name);
console.log ('เขา/เธอเป็น (n) %s', อาร์เรย์ [i] .type);
console.log ('/r/n');
-
-
ชื่อ: Vill Lin
เขา/เธอเป็น moegril (n)
ชื่อ: Will Wen Gunn
เขา/เธอเป็นโพสต์ (n)
-
ในรหัสนี้โปรแกรมจำเป็นต้องสืบค้นชื่อและพิมพ์แอตทริบิวต์ของแต่ละองค์ประกอบอาร์เรย์ หากอาร์เรย์มีองค์ประกอบ N โปรแกรมจะดำเนินการค้นหาวัตถุ 4N
การคัดลอกรหัสมีดังนี้:
1. อาร์เรย์ [i]
2. อาร์เรย์ [i] .name
3. อาร์เรย์ [i]
4. อาร์เรย์ [i]. type
ฉันเชื่อว่าคุณต้องคิดวิธีแก้ปัญหาในเวลานี้นั่นคือกำหนดค่าขององค์ประกอบอาร์เรย์ปัจจุบันให้กับตัวแปรแล้วใช้ในรหัสตรรกะ
การคัดลอกรหัสมีดังนี้:
var array = [
{ชื่อ: 'Will Wen Gunn', พิมพ์: 'Hentai'},
{ชื่อ: 'Vill Lin', ประเภท: 'moegril'}
-
var person = null;
สำหรับ (var i = array.length -1; i> = 0 && (person = array [i]); --i) {
console.log ('ชื่อ: %s', person.name);
console.log ('เขา/เธอเป็น (n) %s', person.type);
console.log ('/r/n');
-
บุคคล = null;
มันดูสวยงามกว่านี้มาก
การคัดลอกรหัสมีดังนี้:
1. อาร์เรย์ [i] => var person
2. คนชื่อ
3. คนประเภท
มันเป็นเหมือน foreach ใน Emcascript5 แต่ความแตกต่างระหว่างทั้งสองนั้นใหญ่มากดังนั้นฉันจะไม่อธิบายที่นี่
PS: ขอบคุณสำหรับการแก้ไข หลังจากการทดลองฉันพบว่าหากองค์ประกอบในอาร์เรย์ถูกกำหนดโดยค่าผ่านโดยตรงค่าที่ได้รับในลูปจะต้องเป็นค่าไม่ใช่ตัวชี้ ดังนั้นไม่ว่าคุณจะกำหนดนิพจน์หรือตัวแปรจะมีคำขอพื้นที่หน่วยความจำเพิ่มเติม
1.3 เพิ่มประสิทธิภาพรหัสการประมวลผล
ในความเป็นจริงมีไม่มากที่จะเพิ่มประสิทธิภาพรหัสการประมวลผลในร่างกายลูปและตัวนับ I ก็เพียงพอที่จะเพิ่ม 1 ด้วยตัวเอง
PS: หากคุณมีข้อเสนอแนะหรือวิธีการที่ดีโปรดให้พวกเขา -
2. วัตถุวงกลม (วัตถุ)
ใน JavaScript สำหรับสามารถสำรวจคุณสมบัติและวิธีการของวัตถุ ควรสังเกตว่าการวนรอบไม่สามารถผ่านประเภทการห่อหุ้มที่วัตถุเป็นของหรือคุณสมบัติต้นแบบและวิธีการในคอนสตรัคเตอร์
ไวยากรณ์ง่ายกว่าการวนรอบอาร์เรย์
การคัดลอกรหัสมีดังนี้:
สำหรับ (/* เริ่มต้นคีย์*/ var ในวัตถุ) {
// ... รหัสตรรกะ
-
เรามักจะใช้วิธีนี้เพื่อใช้งานกับวัตถุ
การคัดลอกรหัสมีดังนี้:
var person = {
'ชื่อ': 'Will Wen Gunn'
'Type': 'Hentai',
'ทักษะ': ['การเขียนโปรแกรม', 'การถ่ายภาพ', 'พูด', 'etc']
-
สำหรับ (คีย์ var ด้วยตนเอง) {
ค่า = บุคคล [คีย์];
// ถ้าค่าเป็นอาร์เรย์ให้แปลงเป็นสตริง
if (ค่าอินสแตนซ์ของอาร์เรย์) {
value = value.join (',');
-
console.log (' %s: %s', คีย์, ค่า);
-
-
ชื่อ: Will Wen Gunn
ประเภท: Hentai
ทักษะ: การเขียนโปรแกรมการถ่ายภาพการพูด ฯลฯ
-
หากคุณใช้ MongoDB คุณจะคุ้นเคยกับกลไกการสืบค้นอย่างแน่นอน เนื่องจากกลไกการสืบค้นของ MongoDB เป็นเหมือนจิตวิญญาณของ API วิธีการดำเนินงานของเต้าหู้ที่ยืดหยุ่นได้รับความนิยมและโมเมนตัมการพัฒนามากมาย
ในการใช้ NANODB Mongo API การใช้งานแบบสอบถามใช้วัตถุลูปในขนาดใหญ่
การคัดลอกรหัสมีดังนี้:
var mydb = nano.db ('mydb');
var mycoll = mydb.collection ('mycoll');
var _cursor = mycoll.find ({
ประเภท: 'repo',
ภาษา: 'JavaScript'
-
_เคอร์เซอร์
.เรียงลำดับ({
ดาว: 1
-
.toArray (ฟังก์ชั่น (err, แถว) {
ถ้า (err)
return console.error (err);
console.log (แถว);
-
สิ่งที่เราต้องปรับให้เหมาะสมไม่ใช่การวนรอบตัวเอง แต่การเพิ่มประสิทธิภาพของวัตถุที่คุณต้องผ่าน
ตัวอย่างเช่นคลาส nanocollection ใน nanodb ดูเหมือนอาร์เรย์ซึ่งมีองค์ประกอบหรือวัตถุทั้งหมดและใช้ ID ขององค์ประกอบเป็นคีย์แล้วเก็บองค์ประกอบ
แต่นี่ไม่ใช่กรณี นักเรียนที่ใช้ขีดเส้นใต้ควรรู้วิธี _.invert นี่เป็นวิธีที่น่าสนใจในการย้อนกลับกุญแจและค่าของวัตถุที่ถูกส่งผ่าน
การคัดลอกรหัสมีดังนี้:
var person = {
'ชื่อ': 'Will Wen Gunn'
'Type': 'Hentai'
-
var _inverted = _.invert (บุคคล);
console.log (_inverted);
-
-
'Will Wen Gunn': 'ชื่อ',
'Hentai': 'type'
-
-
หากคุณต้องการใช้วัตถุลูปเพื่อสอบถามค่าของคุณสมบัติบางอย่างของวัตถุคุณสามารถลองใช้วิธีการต่อไปนี้
การคัดลอกรหัสมีดังนี้:
var person = {
'ชื่อ': 'Will Wen Gunn'
'Type': 'Hentai'
-
ชื่อ var = 'Will Wen Gunn';
var _inverted = _.invert (บุคคล);
if (_inverted [ชื่อ] === 'ชื่อ') {
console.log ('จับ!');
-
// => จับได้!
อย่างไรก็ตามมีการเพิ่มประสิทธิภาพไม่มากนักที่จะใช้สำหรับการสืบค้นวัตถุและทุกอย่างจะต้องขึ้นอยู่กับความต้องการที่แท้จริง : P
ต่อไปเราจะดูอีกสองลูปในขณะที่ () {} และทำ {} ในขณะที่ () ฉันเชื่อว่าเพื่อนที่ได้รับหลักสูตรวิทยาศาสตร์คอมพิวเตอร์จะคุ้นเคยกับสองรอบนี้ ความแตกต่างเพียงอย่างเดียวระหว่างพวกเขาคือลำดับตรรกะของการดำเนินการของร่างกายลูป
ลำดับการดำเนินการของขณะที่ () {} คล้ายกับของสำหรับ () {} การตัดสินการดำเนินการจะดำเนินการก่อนรหัสเชิงตรรกะ แต่การเริ่มต้นและรหัสการประมวลผลจะถูกละเว้น
เมื่อกำหนดเงื่อนไขรหัสเชิงตรรกะจะถูกดำเนินการจนกว่าเงื่อนไขจะไม่ถืออีกต่อไป
การคัดลอกรหัสมีดังนี้:
var sum = 0;
ในขณะที่ (ผลรวม <10) {
ผลรวม + = ผลรวม + 1;
-
console.log (ผลรวม);
// => 15
ทำ {} ในขณะที่ () ทำให้การตัดสินการดำเนินการหลังจากรหัสตรรกะซึ่งหมายถึง "ตายก่อนแล้วเล่น"
การคัดลอกรหัสมีดังนี้:
var sum = 0;
ทำ {
ผลรวม + = ผลรวม + 1;
} ในขณะที่ (ผลรวม <10);
console.log (ผลรวม);
// => 15
ในขณะที่ () {} และทำ {} ในขณะที่ () ยังไม่จำเป็นต้องมีตัวนับ แต่ใช้เงื่อนไขบางอย่างเพื่อพิจารณาว่าจะดำเนินการหรือดำเนินการต่อเพื่อดำเนินการรหัสเชิงตรรกะ
3. ในขณะที่ () {} และทำ {} ในขณะที่ ()
ในขณะที่ () {} และทำ {} ในขณะที่ () ส่วนใหญ่จะใช้ในตรรกะทางธุรกิจและชุดการดำเนินงานจะดำเนินการอย่างต่อเนื่องเพื่อให้บรรลุวัตถุประสงค์บางอย่างเช่นคิวงาน
แต่ลูปทั้งสองนี้เป็นอันตรายเพราะถูกควบคุมโดยเงื่อนไขการดำเนินการตามค่าเริ่มต้นเท่านั้น หากไม่มีผลกระทบต่อการตัดสินการดำเนินการในรหัสตรรกะลูปที่ตายแล้วจะเกิดขึ้น
การคัดลอกรหัสมีดังนี้:
var sum = 02
// คำเตือน!
ในขณะที่ (ผลรวม <10) {
ผลรวม = 1 + 12
-
รหัสดังกล่าวไม่แตกต่างจากในขณะที่ (จริง) {} ดังนั้นก่อนการใช้งานจำเป็นต้องชี้แจงเงื่อนไขการดำเนินการและวิธีการส่งผลกระทบต่อเงื่อนไขการดำเนินการ
4. ใช้ประโยชน์จากคำสั่งควบคุมลูปให้ดี
ฉันเชื่อว่าวิศวกร JavaScript ทั้งหมดใช้คำสั่ง Break แต่ข้อความต่อไปนั้นค่อนข้างไม่ค่อยได้ใช้ ในความเป็นจริงมีโครงการโอเพนซอร์ส JavaScript ที่ยอดเยี่ยมมากมายที่สามารถพบได้
เพื่อที่จะแก้ปัญหาฟังก์ชั่นของคำสั่งต่อไปลองดูที่รหัสตัวอย่างก่อน
การคัดลอกรหัสมีดังนี้:
// node.js เซิร์ฟเวอร์ออกอากาศ
var net = ต้องการ ('net');
var util = ต้องการ ('util');
var broadcastServer = net.createServer ();
// ลูกค้าร้านค้า
BroadcastServer.Clients = [];
// วิธีการออกอากาศลูกค้า
net.socket.prototype.broadcast = function (msg) {
var clients = broadcastServer.Clients;
// รับตัวห้อยของไคลเอนต์ออกอากาศในส่วนกลาง
var index = client.indexof (นี้);
สำหรับ (var i = clients.length -1; i> = 0; --i) {
ถ้า (i === ดัชนี) {
// ถ้าเป็นไคลเอนต์ออกอากาศร่างกายลูปปัจจุบันจะถูกยกเลิก
ดำเนินการต่อ;
-
currclient = ลูกค้า [i];
if (! currclient.destroyed) {
currclient.write (
util.format (
'/r [echo client %s: %d] %s/ninput:',
Currclient.remoteaddress, Currclient.remoteport, MSG)
-
-
-
-
// ไคลเอนต์ใหม่เชื่อมต่อ
BroadcastServer.on ('การเชื่อมต่อ', ฟังก์ชั่น (ไคลเอนต์) {
BroadcastServer.Clients.push (ไคลเอนต์);
// ยินดีต้อนรับ
client.write ('[เซิร์ฟเวอร์ออกอากาศ] ยินดีต้อนรับ!/ninput:');
client.broadcast (ไคลเอนต์ 'เข้าร่วม!');
// ที่จับข้อความ
client.on ('data', function (msg) {
client.broadcast (msg);
client.write ('/rinput:');
-
// ถอดด้ามจับ
client.on ('end', function () {
client.broadcast ('ซ้าย!');
-
-
// ผูก
BroadcastServer.Listen (8080, function () {
console.log ('เซิร์ฟเวอร์ออกอากาศถูกผูกไว้');
-
รหัสนี้ใช้เซิร์ฟเวอร์ออกอากาศตามโมดูล NODE.JS NET ในวิธีการออกอากาศเราใช้คำสั่งต่อไปเพื่อใช้ไคลเอนต์ที่เชื่อมต่อทั้งหมดที่ได้สร้างการเชื่อมต่อยกเว้นไคลเอนต์ออกอากาศ
เนื้อหารหัสค่อนข้างง่าย เมื่อลูกค้าต้องการออกอากาศไปยังลูกค้ารายอื่นวิธีการออกอากาศของวัตถุไคลเอนต์ที่เกี่ยวข้องกับลูกค้าจะถูกเรียก ในวิธีการออกอากาศโปรแกรมจะได้รับตัวห้อยตำแหน่งของไคลเอนต์ปัจจุบันในคอลเลกชันซ็อกเก็ตไคลเอนต์แคชแล้ววนซ้ำผ่านซ็อกเก็ตไคลเอนต์ทั้งหมด เมื่อตัวนับลูปมาถึงตัวห้อยตำแหน่งที่ได้รับก่อนหน้านี้โค้ดตรรกะในร่างกายลูปปัจจุบันจะถูกข้ามและลูปถัดไปจะดำเนินต่อไป
ฉันเชื่อว่าวิศวกรที่ได้เรียนรู้ภาษา C/C ++ จะได้รับคำแนะนำนี้จากสถานที่ต่าง ๆ : "อย่าใช้คำสั่ง goto"
คำสั่ง "ฉาวโฉ่" นี้จริง ๆ แล้วเป็นตัวควบคุมการไหลของรหัสและรายละเอียดของคำสั่ง GOTO จะไม่ได้รับการอธิบายอย่างละเอียดที่นี่ อย่างไรก็ตามไม่มีคำสั่ง goto ที่ชัดเจนใน JavaScript แต่จากคำสั่ง Break และคำสั่งต่อไปมันไม่ยากที่จะหาเงาของ Goto ใน JavaScript
นี่เป็นเพราะคำสั่ง BREAK และคำสั่งต่อไปอนุญาตให้ยอมรับชื่อป้ายกำกับที่กำหนดไว้สำหรับการเปลี่ยนเส้นทางรหัส
ลองดูที่รหัสตัวอย่างที่จัดทำโดย MDN
การคัดลอกรหัสมีดังนี้:
var i, j;
Loop1:
สำหรับ (i = 0; i <3; i ++) {// คำสั่งแรกสำหรับคำสั่งมีป้ายกำกับ "loop1"
Loop2:
สำหรับ (j = 0; j <3; j ++) {// ที่สองสำหรับคำสั่งมีป้ายกำกับ "loop2"
ถ้า (i == 1 && j == 1) {
วนวนต่อไป 1;
} อื่น {
console.log ("i =" + i + ", j =" + j);
-
-
-
// เอาต์พุตคือ:
// "i = 0, j = 0"
// "i = 0, j = 1"
// "i = 0, j = 2"
// "i = 1, j = 0"
// "i = 2, j = 0"
// "i = 2, j = 1"
// "i = 2, j = 2"
// สังเกตว่ามันข้ามทั้ง "i = 1, j = 1" และ "i = 1, j = 2"
ในรหัสตัวอย่างนี้มีการใช้ลูปสองชั้นและมีการกำหนดฉลากนอกแต่ละลูปซึ่งใช้สำหรับการเรียกคำสั่งต่อไป
เลเยอร์แรกของลูปอยู่ในฉลากของ Loop1 นั่นคือในโปรแกรมที่ตามมาหากเลือกฉลาก Loop1 ในคำสั่งต่อหรือคำสั่ง Break, Outermest Loop จะแตก
ลูปเลเยอร์ที่สองอยู่ในฉลากของ Loop2 ในลูประดับบนสุด หากเลือกฉลาก loop2 ในคำสั่งต่อหรือคำสั่ง break มันจะกลับไปที่วงวนของลูประดับบนสุด
ด้วยการใช้คำสั่งควบคุมลูปเราสามารถแทรกแซงการตัดสินการดำเนินการลูปดั้งเดิมเพื่อให้สามารถสร้างระบบลอจิกที่ซับซ้อนมาก เพื่อให้มันตรงไปตรงมามีคำสั่ง goto จำนวนมากในเคอร์เนล Linux สำหรับสาเหตุที่คุณยังคงได้ยินคำพูดเช่นคำพูดของ Goto เพียงแค่ Google พวกเขาด้วยตัวคุณเอง
5. ลูปขั้นสูง
5.1 ขยายวง
ก่อนอื่นลองดูรหัสสองชิ้นและเดาว่าอันไหนมีประสิทธิภาพที่ดีกว่า
การคัดลอกรหัสมีดังนี้:
// การตั้งค่า
var array = [
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "],
["data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data", "data"]
-
กระบวนการฟังก์ชั่น (รายการ) {
// ทำอะไรกับรายการ
-
// กรณีที่ 1
สำหรับ (var i = array.length-1; i> = 0; i--) {
สำหรับ (var j = array [i] .length-1; j> = 0; i--) {
กระบวนการ (อาร์เรย์ [i] [j]);
-
-
// กรณีที่ 2
สำหรับ (var i = array.length - 1; i> = 0; i = i - 4) {
สำหรับ (var j = array [i] .length - 1; j> = 0; j = j - 6) {
กระบวนการ (อาร์เรย์ [i] [j]);
กระบวนการ (อาร์เรย์ [i] [J - 1]);
กระบวนการ (อาร์เรย์ [i] [J - 2]);
กระบวนการ (อาร์เรย์ [i] [J - 3]);
กระบวนการ (อาร์เรย์ [i] [J - 4]);
กระบวนการ (อาร์เรย์ [i] [J - 5]);
-
สำหรับ (var j = array [i - 1] .length - 1; j> = 0; j = j - 6) {
กระบวนการ (อาร์เรย์ [i] [j]);
กระบวนการ (อาร์เรย์ [i] [J - 1]);
กระบวนการ (อาร์เรย์ [i] [J - 2]);
กระบวนการ (อาร์เรย์ [i] [J - 3]);
กระบวนการ (อาร์เรย์ [i] [J - 4]);
กระบวนการ (อาร์เรย์ [i] [J - 5]);
-
สำหรับ (var j = array [i - 2] .length - 1; j> = 0; j = j - 6) {
กระบวนการ (อาร์เรย์ [i] [j]);
กระบวนการ (อาร์เรย์ [i] [J - 1]);
กระบวนการ (อาร์เรย์ [i] [J - 2]);
กระบวนการ (อาร์เรย์ [i] [J - 3]);
กระบวนการ (อาร์เรย์ [i] [J - 4]);
กระบวนการ (อาร์เรย์ [i] [J - 5]);
-
สำหรับ (var j = array [i - 3] .length - 1; j> = 0; j = j - 6) {
กระบวนการ (อาร์เรย์ [i] [j]);
กระบวนการ (อาร์เรย์ [i] [J - 1]);
กระบวนการ (อาร์เรย์ [i] [J - 2]);
กระบวนการ (อาร์เรย์ [i] [J - 3]);
กระบวนการ (อาร์เรย์ [i] [J - 4]);
กระบวนการ (อาร์เรย์ [i] [J - 5]);
-
-
ฉันต้องผ่านองค์ประกอบทั้งหมดของ subarray ในอาร์เรย์ มีสองวิธีแก้ปัญหาหนึ่งคือวิธีที่เรามักจะใช้และอีกวิธีหนึ่งคือการขยายงานลูป คำตอบคือกรณีที่ 2 ทำงานได้ดีขึ้นเพราะการตัดสินการดำเนินการทั้งหมดระหว่างทุกองค์ประกอบทั้งหมดจะถูกลบซึ่งเร็วกว่าปกติตามธรรมชาติ
ที่นี่เรามาดูวิธีแก้ปัญหาที่ทรงพลังกว่า หากลิงค์ธุรกิจจำเป็นต้องประมวลผลซ้ำ ๆ ในชุดข้อมูลขนาดใหญ่และปริมาณข้อมูลจะไม่เปลี่ยนแปลงจากจุดเริ่มต้นของการทำซ้ำคุณสามารถพิจารณาใช้เทคโนโลยีที่เรียกว่าอุปกรณ์ Duff เทคโนโลยีนี้ได้รับการตั้งชื่อตามทอมดัฟฟ์ผู้สร้างซึ่งถูกนำไปใช้เป็นครั้งแรกในภาษา C ต่อมาเจฟฟ์กรีนเบิร์กส่งไปยังจาวาสคริปต์และแก้ไขผ่าน Andrew b ราชาและเสนอเวอร์ชันที่มีประสิทธิภาพมากขึ้น
การคัดลอกรหัสมีดังนี้:
// เครดิต: เพิ่มความเร็วในเว็บไซต์ของคุณ (นักปั่นใหม่, 2003)
var iterations = math.floor (value.length / 8);
var เหลือ = ค่าความยาว % 8;
var i = 0;
ถ้า (เหลือ> 0) {
ทำ {
กระบวนการ (ค่า [i ++]);
} ในขณะที่ (-Leftover> 0);
-
ทำ {
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
กระบวนการ (ค่า [i ++]);
} ในขณะที่ (-ค่าใช้จ่าย> 0);
หลักการทำงานของเทคนิคนี้คือการคำนวณความยาวของค่าหารด้วย 8 เพื่อให้ได้จำนวนการวนซ้ำที่ต้องทำซ้ำและใช้ฟังก์ชันคณิตศาสตร์ floor () เพื่อให้แน่ใจว่าผลลัพธ์เป็นจำนวนเต็มและคำนวณจำนวนที่ไม่สามารถหารได้ 8
ฉันบรรจุอุปกรณ์นี้และได้รับ API ที่มีรสชาติแบบอะซิงโครนัส
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น duff (อาร์เรย์, mapper) {
var n = math.floor (array.length / 8);
var l = array.length % 8;
var i = 0;
ถ้า (l> 0) {
ทำ {
MAPPER (อาร์เรย์ [i ++]);
} ในขณะที่ (-i> 0);
-
ทำ {
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
MAPPER (อาร์เรย์ [i ++]);
} ในขณะที่ (-n> 0);
-
Duff ([... ], ฟังก์ชั่น (รายการ) {
-
-
นี่คือชุดของการทดสอบประสิทธิภาพและผลลัพธ์สำหรับโซลูชันซ้ำสามแบบข้างต้น http://jsperf.com/spreaded-loop
5.2 วงที่ไม่ใช่เจ้าของภาษา
ในภาษาการเขียนโปรแกรมใด ๆ ลูปสามารถนำไปใช้ไม่เพียง แต่ทางอ้อมในรูปแบบอื่น ๆ แต่ยังมีวิธีอื่นด้วย
ลองทบทวนเนื้อหาของคณิตศาสตร์โรงเรียนมัธยมก่อน - สูตรทั่วไปของลำดับ
การคัดลอกรหัสมีดังนี้:
ขั้นพื้นฐาน
A [1] = 1
a [n] = 2 * a [n - 1] + 1
ดังนั้น
a [n] + 1 = 2 * a [n - 1] + 2
= 2 * (a [n - 1] + 1)
(a [n] + 1) / (a [n - 1] + 1) = 2
แล้ว
a [n] + 1 = (a [n] + 1) / (a [n - 1] + 1) * (a [n - 1] + 1) / (a [n - 2] + 1) * ... * (a [2] + 1) / (a [1] + 1) * (a [i] + 1)
A [n] + 1 = 2 * 2 * ... * 2 * 2
a [n] + 1 = 2^n
a [n] = 2^n - 1
สุดท้าย
a [n] = 2^n - 1
หลังจากอ่านการคำนวณอย่างง่ายข้างต้นคุณอาจเดาได้ว่าเราจะพูดคุยอะไร ใช่เรายังสามารถใช้ลูปโดยใช้การเรียกซ้ำ
การเรียกซ้ำเป็นวิธีการใช้งานที่สำคัญมากในวิชาคณิตศาสตร์และวิทยาการคอมพิวเตอร์ซึ่งหมายถึงฟังก์ชั่นการเรียกตัวเองเมื่อมีการใช้งาน
ในชุมชน Node.js การเรียกซ้ำใช้เพื่อใช้เทคโนโลยีที่สำคัญมาก: เทคโนโลยีมิดเดิลแวร์ นี่เป็นรหัสการใช้งานมิดเดิลแวร์เวอร์ชันใหม่ใน WebJs ที่ยังไม่ได้เผยแพร่
การคัดลอกรหัสมีดังนี้:
-
* วิธีการเรียกใช้ Middlewares
* @param {string} url คำขอปัจจุบัน url url
* @param {Object} req วัตถุคำขอ
* @param {Object} res วัตถุตอบสนอง
* @param {function} การโทรกลับที่สมบูรณ์
* @return {function} เซิร์ฟเวอร์
-
server.runmiddleWares = function (url, req, res, out) {
ดัชนี var = -1;
var middlewares = this._usingmiddlewares;
// เรียกใช้มิดเดิลแวร์ถัดไปหากมีอยู่
ฟังก์ชั่นถัดไป (เอ่อ) {
ดัชนี ++;
// มิดเดิลแวร์ปัจจุบัน
var curr = middlewares [ดัชนี];
ถ้า (Curr) {
ตรวจสอบ var = ใหม่ regexp (curr.route);
// ตรวจสอบเส้นทาง
if (check.test (url)) {
พยายาม {
ฟังก์ชั่นในภายหลัง () {
DEBUG ('มิดเดิลแวร์บอกว่าจำเป็นต้องมีในภายหลังใน %s', url);
// การพึ่งพาไม่ได้ในขณะนี้
if (middlewares.indexof (curr)! == Middlewares.length - 1) {
_later (Curr);
ดัชนี--;
ต่อไป();
} อื่น {
การดีบัก ('การพึ่งพามิดเดิลแวร์ผิด');
// มิดเดิลแวร์นี้ไม่สามารถทำงานได้
ออก();
-
-
// เรียกใช้มิดเดิลแวร์
if (utils.isfunc (curr.handler)) {
// ฟังก์ชั่นมิดเดิลแวร์ปกติ
curr.handler (req, res, ถัดไป, ภายหลัง);
} อื่นถ้า (utils.isobject (curr.handler) && utils.isfunc (curr.handler.emit)) {
// วัตถุเซิร์ฟเวอร์
curr.handler.emit ('คำขอ', req, res, ถัดไป, ภายหลัง);
} อื่น {
// มีบางอย่างผิดปกติเกี่ยวกับมิดเดิลแวร์
ต่อไป();
-
} catch (err) {
ต่อไป();
-
} อื่น {
ต่อไป();
-
} อื่น {
// ไปยังขั้นตอนต่อไปของท่อ
ออก();
-
-
// ถ้ามิดเดิลแวร์ขึ้นอยู่กับคนกลางอื่น ๆ
// มันสามารถปล่อยให้มันทำงานในภายหลัง
ฟังก์ชั่น _later (Curr) {
var i = middlewares.indexof (curr);
var _tmp1 = middlewares.slice (0, i);
_tmp1.push (Middlewares [i + 1], Curr);
var _tmp2 = middlewares.slice (i + 2);
[] .push.apply (_tmp1, _tmp2);
Middlewares = _tmp1;
-
// มิดเดิลแวร์คนแรก
ต่อไป();
คืนสิ่งนี้;
-
แม้ว่ารหัสนี้จะดูรุนแรงและซับซ้อน แต่ก็จะชัดเจนยิ่งขึ้นถ้าเราทำให้มันง่ายขึ้น
การคัดลอกรหัสมีดังนี้:
server.runmiddleWares = function (url, req, res, out) {
ดัชนี var = -1;
var middlewares = this._usingmiddlewares;
// เรียกใช้มิดเดิลแวร์ถัดไปหากมีอยู่
ฟังก์ชั่นถัดไป (เอ่อ) {
ดัชนี ++;
// มิดเดิลแวร์ปัจจุบัน
var curr = middlewares [ดัชนี];
ถ้า (Curr) {
ตรวจสอบ var = ใหม่ regexp (curr.route);
// ตรวจสอบเส้นทาง
if (check.test (url)) {
// เรียกใช้มิดเดิลแวร์ปัจจุบัน
curr.handler (req, res, ถัดไป);
} อื่น {
ต่อไป();
-
} อื่น {
// ไปยังขั้นตอนต่อไปของท่อ
ออก();
-
-
// มิดเดิลแวร์คนแรก
ต่อไป();
คืนสิ่งนี้;
-
เหตุผลที่การเรียกซ้ำสามารถใช้ในการใช้งานระบบมิดเดิลแวร์คือการเรียกซ้ำเป็นวิธีที่เหมาะสมที่สุดในการตอบสนองการไหลของโปรแกรมใน Node.Js.
ในรหัสการใช้งานมิดเดิลแวร์นี้ this._usingmiddlewares เป็นอาร์เรย์ลูปฟังก์ชั่นถัดไป () เป็นวงวนที่ตรวจสอบการทดสอบ (URL) คือเงื่อนไขการตัดสินการดำเนินการและรหัสการประมวลผลลูปเป็นตัวนับดัชนีแรกในวงวนเพื่อเพิ่ม 1 และฟังก์ชั่นถัดไป