φυβλαςのβλογ
phyblas的博客



javascript เบื้องต้น บทที่ ๓๙: เจเนอเรเตอร์
เขียนเมื่อ 2020/02/07 09:19
แก้ไขล่าสุด 2021/09/28 16:42


ในบทที่แล้วได้แนะนำเรื่องอิเทอร์ไปแล้ว ส่วนในบทนี้จะแนะนำเกี่ยวกับสิ่งที่เรียกว่าเจเนอเรเตอร์ (generator) ซึ่งเป็นวิธีการสร้างอิเทอเรเตอร์อย่างง่ายแบบหนึ่ง




การสร้างเจเนอเรเตอร์

เจเนอเรเตอร์ (generator) เป็นอิเทอเรเตอร์ชนิดหนึ่ง สามารถสร้างขึ้นได้อย่างง่ายโดยใช้คำสั่ง function

วิธีการสร้างเจเนอเรเตอร์นั้นทำคล้ายๆกับการสร้างฟังก์ชันธรรมดา แต่จะต่างกันตรงที่หน้าวงเล็บจะเติม * ลงไป และภายใน {} จะมีคำสั่ง yield

ออบเจ็กต์ที่ได้จากฟังก์ชันเจเนอเรเตอร์นี้จะเป็นอิเทอเรเตอร์ที่เมื่อใช้เมธอด .next หรือวนซ้ำด้วย for๛of แล้วก็จะได้ค่าออกมาเรื่อยๆ

โดยค่าที่ได้คือค่าที่อยู่ข้างหลังคำสั่ง yield คล้ายๆกับการใช้ retuen ในฟังก์ชันทั่วไป แต่คำสั่ง yield อาจมีหลายครั้ง ซึ่งแทนการวนซ้ำแต่ละครั้ง การวนซ้ำจะสิ้นสุดลงเมื่อสิ้นสุดฟังก์ชันไปโดยไม่เจอ yield อีกแล้ว

ตัวอย่างการสร้างและใช้เจเนอเรเตอร์
function *rezerogen(){
  yield "เอมิเลีย";
  yield "เรม";
  yield "รัม";
}

let iter = rezerogen();
alert(iter.next().value); // ได้ เอมิเลีย
alert(iter.next().value); // ได้ เรม
alert(iter.next().value); // ได้ รัม
alert(iter.next().value); // ได้ undefined

let iter2 = rezerogen()
for (let x of iter2){
  alert(x); // วน ๓​ รอบ ได้ เอมิเลีย, เรม, รัม
}

ในที่นี้ฟังก์ชัน rezerogen() เป็นเจเนอเรเตอร์ซึ่งจะสร้างอิเทอเรเตอร์ที่ไล่คืนค่าออกมา ๓ ตัว จะนำมาใช้ .next() เพื่อไล่เอาค่าทีละตัว หรือนำมาวนซ้ำด้วย for๛of ก็ได้

สามารถใช้การวนซ้ำเพื่อให้ฟังก์ชันผ่าน yield หลายครั้งเพื่อจะได้เขียน yield แค่ครั้งเดียวแต่วนซ้ำได้หลายรอบ
function* gen(n) {
  let i = 1;
  while (i <= n) {
    yield i * i
    i++;
  }
}

let iter = gen(4);
alert(iter.next().value); // ได้ 1
alert(iter.next().value); // ได้ 4
alert(iter.next().value); // ได้ 9
alert(iter.next().value); // ได้ 16
alert(iter.next().value); // ได้ undefined

นอกจากนี้ สามารถใช้ * ตามหลัง yield เพื่อรับค่าเป็นแถวลำดับได้ แบบนี้แต่ละรอบก็จะเอาค่าจากในนั้นไปทีละตัว
function* konosubagen() {
  yield "คาซึมะ"
  yield* ["อควา","เมงุมิน","ดักเนส"]
}

let iter = konosubagen();
alert(iter.next().value); // ได้ คาซึมะ
alert(iter.next().value); // ได้ อควา
alert(iter.next().value); // ได้ เมงุมิน
alert(iter.next().value); // ได้ ดักเนส
alert(iter.next().value); // ได้ undefined




สร้างคลาสของอิเทอเรเตอร์โดยใช้เจเนอเรเตอร์

สามารถนำเจเนอเรเตอร์มาสร้างเป็นคลาสของอิเทอเรเตอร์ขึ้นมาได้ โดยอาจสร้าง this[Symbol.iterator] ไว้ใน constructor

ตัวอย่างเช่น ลองสร้างคลาสที่เอาค่าในในแถวลำดับ ๒ อันมาต่อกันแล้วไล่ออกมาทีละตัว
class Itor {
  constructor(ar1, ar2) {
    this.ar1 = ar1;
    this.ar2 = ar2;
    this[Symbol.iterator] = function* () {
      let i = 0, n = this.ar1.length;
      while (i < n) {
        yield "~" + this.ar1[i] + " " + this.ar2[i] + "~";
        i++;
      }
    }
  }
}

let myouji = ["โคโนะฮาตะ", "มานากะ", "โมริโนะ", "อิโนเสะ", "ซากุราอิ"];
let namae = ["มิระ", "อาโอะ", "มาริ", "ไม", "มิกาเงะ"];
let koias = new Itor(myouji, namae);

let s = "";
for (let k of koias) {
  s += k;
}
alert(s); // ได้ ~โคโนะฮาตะ มิระ~~มานากะ อาโอะ~~โมริโนะ มาริ~~อิโนเสะ ไม~~ซากุราอิ มิกาเงะ~




ลองสร้างฟังก์ชัน range

เพื่อเป็นตัวอย่างการใช้งานจริง ลองใช้เจเนอเรเตอร์เพื่อสร้างเจเนอเรเตอร์เหมือนอย่าง range ในภาษาไพธอนขึ้นมาดู

range คือฟังก์ชันที่สร้างค่าที่วนซ้ำโดยไล่จากค่าต่ำสุด a ไปจนก่อนถึงค่าสูงสุด b โดยเว้นห่างทีละ c

แต่กรณีที่ใส่แค่ a กับ b ให้ถือว่าค่า c เป็น 1 คือข้ามทีละ 1

หรือถ้าใส่แค่ตัวเดียว จะถือว่าไล่จากค่า a=0 ไปยังก่อนถึงค่านั้น

ตัวอย่างการสร้าง
function* range(a, b, c = 1) {
  if (!b) [a, b] = [0, a];
  while (a < b) {
    yield a;
    a += c;
  }
}

for (let x of range(4)) { alert(x); } // ได้ 0, 1, 2, 3
for (let x of range(5, 10)) { alert(x); } // ได้ 5, 6, 7, 8, 9
for (let x of range(2, 7, 2)) { alert(x); } // ได้ 2, 4, 6




ลองสร้างจำนวนฟีโบนัชชี

อีกตัวอย่างที่นิยมเขียนเพื่อฝึกคือจำนวนฟีโบนัชชี อาจลองเขียนโค้ดได้ดังนี้
function* fibo() {
  let a = 0, b = 1, c
  while (1) {
    c = a + b;
    a = b;
    b = c;
    yield a;
  }
}

let s = "";
let i = 0;
for (let x of fibo()) {
  if(x > 2000) break;
  s += x + "; ";
  i++;
}
alert(s) // ได้ 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377; 610; 987; 1597;




ลองสร้างฟังก์ชันไล่จำนวนเฉพาะ

อีกตัวอย่างหนึ่งที่อาจลองใช้เจเนอเรเตอร์สร้างได้คือฟังก์ชันที่ไล่จำนวนเฉพาะออกมา โดยใช้วิธีการแบบตะแกรงของเอราโตสเทเนส (Ερατοσθένης) อาจสร้างได้ดังนี้
function* chamnuanchapho() {
  let a = 1
  while (1) {
    let pen = true;
    a++;
    let i = 2;
    while (i <= Math.floor(Math.sqrt(a))) {
      if (a % i == 0) {
        pen = false
        break;
      }
      i++;
    }
    if (pen) yield a;
  }
}

let s = "";
let i = 0;
for (let x of chamnuanchapho()) {
  if (x > 100) break
  s += x + "' ";
  i++;
}
alert(s) // ได้ 2' 3' 5' 7' 11' 13' 17' 19' 23' 29' 31' 37' 41' 43' 47' 53' 59' 61' 67' 71' 73' 79' 83' 89' 97' 






-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> javascript

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

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ