การเรียนรู้ Erlang vs learning node.js [ปิด]


41

ฉันเห็นอึออนไลน์มากมายเกี่ยวกับวิธีที่ Erlang เตะ node.js ในทุกหมวดหมู่ที่เป็นไปได้ ดังนั้นฉันต้องการเรียนรู้ Erlang และให้ภาพ แต่นี่เป็นปัญหา ฉันพบว่าฉันมีเวลายากขึ้นในการรับ Erlang มากกว่าที่ฉันเคยเก็บโหนด ด้วย node.js ฉันสามารถเลือกโครงการที่ค่อนข้างซับซ้อนและในวันที่ฉันมีบางสิ่งบางอย่างทำงาน ด้วย Erlang ฉันวิ่งเข้าไปหาสิ่งกีดขวางและไม่เข้าใกล้อย่างรวดเร็ว

ดังนั้น .. สำหรับผู้ที่มีประสบการณ์มากกว่าเออร์แลงมีความซับซ้อนในการเรียนรู้หรือฉันเพิ่งจะพลาดบางสิ่งบางอย่าง? Node.js อาจไม่สมบูรณ์แบบ แต่ฉันดูเหมือนจะสามารถทำสิ่งต่างๆให้สำเร็จได้


9
บางทีฉันอาจขาดอะไรบางอย่าง แต่ไม่ใช่ node.js ไลบรารี JavaScript และ Erlang เป็นภาษาที่แตกต่างอย่างสิ้นเชิง? พวกเขาเปรียบได้ยังไงกัน?
FrustratedWithFormsDesigner

3
@FrustratedWithFormsDesigner, node.js เป็นส่วนหนึ่งของแฟชั่น / hype ล่าสุดของการได้รับ javascript บนฝั่งเซิร์ฟเวอร์ด้วยวิธีการแบบมัลติเธรดดังนั้นพวกเขาจึงเปรียบ
lurscher

5
@lurscher: คุณไม่สามารถเปรียบเทียบ Erlang (ภาษา) กับ Node.js (JavaScript ฝั่งเซิร์ฟเวอร์) นั่นจะเหมือนกับการเปรียบเทียบ Java (ภาษา) กับ Django (เซิร์ฟเวอร์ python) ไม่พูดถึง Erlang และ JS แตกต่างกันมากเช่นกัน
Josh K

10
ในฐานะที่เป็นคนที่ใช้ทั้ง erlang และ node พวกเขาเปรียบได้กับปัญหาที่พวกเขาแก้ไข
Dale Harvey

3
@Noli มีความแตกต่างระหว่าง node.js และ erlang คุณหมายถึงการเปรียบเทียบระหว่างเว็บเซิร์ฟเวอร์ node.js และ erlang Erlang มีผู้ใช้หลายคนนอกเว็บเซิร์ฟเวอร์
Raynos

คำตอบ:


46

ก่อนอื่นฉันเห็นด้วยกับเพียงคำตอบของ OPINION ที่ถูกต้องเกี่ยวกับการเรียนรู้ Erlang เป็นภาษาที่ใช้งานได้เป็นส่วนใหญ่ (แม้ว่าการทำงานพร้อมกันจะมีบทบาทใหญ่) และคุณสมบัติทั้งหมดของมันได้รับการเพิ่มเพื่อให้สามารถทนต่อความผิดพลาดและความทนทานซึ่งไม่ใช่เป้าหมายการออกแบบเดียวกับ Javascript ในตอนแรก

ประการที่สองการทิ้ง Node.js เพื่อเข้าสู่ Erlang เป็นการวางผิดที่เล็กน้อย Node.js เป็นเซิร์ฟเวอร์ / เฟรมเวิร์กเดียวที่ดำเนินการทุกอย่างในรูปแบบที่ขับเคลื่อนด้วยเหตุการณ์ด้วยความช่วยเหลือของการเรียกกลับ Erlang มีกรอบงานของตัวเอง (OTP) แต่มันไม่ได้อยู่ในระดับเดียวกันเลย

หากคุณวางแผนที่จะเรียนรู้ Erlang ฉันขอแนะนำรายการบล็อกของฉันจดหมายเปิดผนึกถึงผู้เริ่มต้น Erlang (หรือผู้สังเกตการณ์)เป็นการอ่านบทแนะนำก่อนที่จะดำดิ่งสู่บทเรียน


สิ่งเดียวที่คุณสามารถเปรียบเทียบ Erlang และ Node.js ได้ในแง่ของรูปแบบและการใช้งานคือการขับเคลื่อนเหตุการณ์ อย่างไรก็ตามมีความแตกต่างใหญ่สองประการที่นี่ โมเดลของ Node.js ขึ้นอยู่กับการโทรกลับที่ผูกไว้กับกิจกรรม Erlang ขึ้นอยู่กับคิวข้อความและการรับแบบเลือก ความหมายในนั้นมีอะไรบ้าง

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

เซมาฟอร์ Mutex มีสองสถานะ: ถูกล็อกและว่าง เมื่อใดก็ตามที่หน่วยการคำนวณที่กำหนด (ผู้ปฏิบัติงานกระบวนการฟังก์ชั่นหรือเธรด) ต้องการเข้าถึง mutex มันจะต้องเริ่มต้นเหตุการณ์ที่บอกว่า 'ฉันสนใจ' ตอนนี้คุณต้องสนใจกิจกรรมประเภทต่อไปนี้:

  • mutex นั้นฟรีและคุณขอล็อค
  • Mutex ถูกล็อคโดยบุคคลอื่นและคุณต้องการได้รับการล็อค
  • Mutex ถูกล็อคด้วยตัวเองและคุณต้องการปลดปล่อย Mutex

จากนั้นคุณมีกิจกรรมเพิ่มเติมที่ต้องพิจารณาเช่นหมดเวลาเพื่อหลีกเลี่ยงการหยุดชะงัก:

  • mutex ถูกล็อคและคุณรอนานเกินไปตัวจับเวลาในการหยุดยิง
  • mutex ถูกล็อคและคุณรอนานเกินไปรับการล็อคจากนั้นหมดเวลาใช้งาน

จากนั้นคุณมีกิจกรรมนอกขอบเขต:

  • คุณเพิ่งล็อค mutex ในขณะที่คนงานบางคนคาดว่ามันจะเป็นอิสระ ตอนนี้แบบสอบถามของคนงานจะต้องถูกจัดคิวเพื่อที่เมื่อว่างก็จะจัดการกลับ
  • คุณต้องทำให้การทำงานแบบอะซิงโครนัสทั้งหมด

เมทริกซ์ของเหตุการณ์ซับซ้อนขึ้นอย่างรวดเร็ว FSM ของเราที่นี่มี 2 สถานะเท่านั้น ในกรณีของ Erlang (หรือภาษาใด ๆ ที่มีการรับและเลือกแบบอะซิงโครนัสกับเหตุการณ์ที่อาจเกิดขึ้นแบบซิงโครนัส) คุณต้องใส่ใจกับกรณีบางกรณี:

  • mutex นั้นฟรีและคุณขอล็อค
  • Mutex ถูกล็อคโดยบุคคลอื่นและคุณต้องการได้รับการล็อค
  • Mutex ถูกล็อคด้วยตัวเองและคุณต้องการปลดปล่อย Mutex

และนั่นคือมัน ตัวจับเวลาจะได้รับการจัดการในกรณีเดียวกับที่ทำเสร็จและสำหรับทุกสิ่งที่เกี่ยวข้องกับ 'รอจนกว่าจะว่าง' ข้อความจะถูกจัดคิวโดยอัตโนมัติ: ผู้ปฏิบัติงานเพียง แต่ต้องรอการตอบกลับ แบบจำลองนั้นง่ายกว่ามากในกรณีเหล่านี้

ซึ่งหมายความว่าในกรณีทั่วไป CPS และโมเดลที่ใช้การติดต่อกลับเช่นที่อยู่ใน node.js ขอให้คุณฉลาดมากในการจัดการเหตุการณ์หรือขอให้คุณดูแลเมทริกซ์เหตุการณ์ที่ซับซ้อนทั้งหมดเนื่องจาก คุณจะต้องถูกเรียกกลับในแต่ละกรณีที่ไม่สำคัญซึ่งเป็นผลมาจากปัญหาเรื่องเวลาแปลก ๆ และการเปลี่ยนแปลงสถานะ

การเลือกรับมักจะอนุญาตให้คุณมุ่งเน้นเฉพาะในกลุ่มย่อยของเหตุการณ์ที่อาจเกิดขึ้นทั้งหมดและช่วยให้คุณสามารถให้เหตุผลกับเหตุการณ์ในกรณีนั้นได้ง่ายขึ้น โปรดทราบว่า Erlang มีพฤติกรรม (รูปแบบการออกแบบ / การดำเนินงานกรอบ) gen_eventของสิ่งที่เรียกว่า การใช้งาน gen_event ช่วยให้คุณมีกลไกคล้ายกับสิ่งที่ถูกใช้ใน node.js หากนั่นคือสิ่งที่คุณต้องการ


จะมีประเด็นอื่น ๆ ที่ทำให้เห็นความแตกต่าง; Erlang มีการกำหนดตารางเวลาไว้ล่วงหน้าในขณะที่ node.js ร่วมมือกันทำให้ Erlang เหมาะสำหรับแอปพลิเคชันขนาดใหญ่มาก ๆ (การแจกจ่ายและทั้งหมด) แต่ Node.js และชุมชนมักจะเป็นเว็บที่ฉลาดและมีความรู้เกี่ยวกับแนวโน้มล่าสุดของเว็บ เป็นคำถามของการเลือกเครื่องมือที่ดีที่สุดและสิ่งนี้จะขึ้นอยู่กับพื้นหลังประเภทของปัญหาและความต้องการของคุณ ในกรณีของฉันแบบจำลองของ Erlang เหมาะกับวิธีคิดที่ดีของฉันมาก นี่ไม่ใช่กรณีสำหรับทุกคน

หวังว่านี่จะช่วยได้


เพิ่มเติมเกี่ยวกับการเขียนโปรแกรมแบบโต้ตอบและทำมันใน JS: blog.flowdock.com/2013/01/22/…
Bart

"เพราะคุณจะต้องถูกเรียกกลับมาในแต่ละกรณีที่ไม่เหมาะสมซึ่งเป็นผลมาจากปัญหาเรื่องเวลาแปลก ๆ และการเปลี่ยนแปลงสถานะ" - ใน Erlang คุณยังคงต้องจัดการกับตัวจับเวลาและความจริงที่ว่าคุณทำ "ในกรณีเดียวกับที่ได้รับเสร็จ" ไม่ได้เปลี่ยนความซับซ้อน (เลย) จากมุมมองของฉัน (ในฐานะสถาปนิกของระบบที่ประมวลผลคำขอพันล้านต่อวัน) ความแตกต่างที่เหมือนจริงเพียงอย่างเดียวระหว่าง selective receive และ node.js-style คือ (a) คำถาม "สิ่งที่เราต้องการทำโดยค่าเริ่มต้น" (กับ node.js กำลังประมวลผลกิจกรรมตามค่าเริ่มต้นและ Erlang เลื่อนกิจกรรมเว้นแต่จะมีการจับคู่เกิดขึ้น) ...
ไม่มีข้อบกพร่องกระต่าย

... และ (b) ความสามารถในการอ่านรวมถึงจำนวน boilerplate (ซึ่งค่อนข้างแย่ในโหนด node.js แต่ดีขึ้นมาก - และ IMNSHO ดีกว่า Erlang - ด้วยโอเปอเรเตอร์ที่เพิ่งเปิดตัวใหม่) ... และในทุกกรณี ความแตกต่างเหล่านี้เป็นเครื่องสำอางที่สวยงามมาก (ทั้งๆที่มีความคลั่งไคล้ทั้งสองด้านในการเทศนาอย่างอื่น)
ไม่มีข้อบกพร่องกระต่าย

38

Erlang ไม่ซับซ้อนในการเรียนรู้มันเป็นแค่มนุษย์ต่างดาวที่คิดว่า Chambers Constant (99.44%) ของ coders ได้เรียนรู้ในขณะที่วิธีการเขียนโปรแกรมทำงาน ปัญหาที่คุณเผชิญอยู่นั้นมีแนวโน้มที่จะทำให้เกิดความสับสนเล็กน้อยมากกว่าความซับซ้อนที่เกิดขึ้นจริง

นี่คือฟีเจอร์เอเลี่ยนบางส่วนของ Erlang ที่กำลังจะกัดโปรแกรมเมอร์ทั่วไป:

  • Erlang เป็นภาษาการเขียนโปรแกรม (ส่วนใหญ่ -) การทำงาน ภาษาโปรแกรมทั่วไปส่วนใหญ่มีความจำเป็นทางทหารเกือบทั้งหมด
  • รูปแบบการทำงานพร้อมกันของ Erlang คือรูปแบบนักแสดง ภาษาโปรแกรมทั่วไปส่วนใหญ่จะใช้เธรดที่มีการล็อคหรือรูปแบบของ "เครื่องปฏิกรณ์" ซึ่งเป็นวิธีการทำงานพร้อมกัน (ฉันคิดว่า Node.js เป็นตัวหลัง แต่อย่าโทรหาฉัน - ฉันมีความสนใจใน JavaScript ในด้านใด ๆ ของความสัมพันธ์ลูกค้า / เซิร์ฟเวอร์)
  • Erlang มีวิธีการ "ปล่อยให้มันพัง" เพื่อเข้ารหัสด้วยคุณสมบัติรันไทม์ที่มีประสิทธิภาพซึ่งมีให้เพื่อตรวจจับข้อขัดข้องเหล่านี้วิเคราะห์และแก้ไขแพตช์ในขณะที่ระบบกำลังทำงาน ภาษาโปรแกรมทั่วไปส่วนใหญ่รับรองรูปแบบการเขียนโปรแกรมป้องกันอย่างมาก
  • Erlang เกือบจะ แต่ไม่ค่อนข้างจับคู่อย่างแยกไม่ออกกับห้องสมุดที่บิดเบี้ยวขนาดใหญ่และอ่อนโยนต่อสมองของสถาปัตยกรรมที่ใช้กันทั่วไปสำหรับเซิร์ฟเวอร์ที่เชื่อถือได้และเสถียร (OTP) (มีเหตุผลว่าทำไม Erlang ถึงถูกเรียกว่า Erlang / OTP.) นอกจากนี้ไลบรารี่นี้ถูกสร้างขึ้นจากคุณสมบัติของเอเลี่ยนที่กล่าวถึงก่อนหน้านี้และจึงทึบแสงสำหรับผู้มาใหม่ ภาษาการเขียนโปรแกรมส่วนใหญ่มีไลบรารีที่ครอบคลุมทั้งหมดน้อยกว่า (Java EE อย่างไรก็ตาม) ที่จะทำงานกับและกล่าวว่าไลบรารีนั้นเป็นไปตามธรรมชาติซึ่งสร้างขึ้นจากแนวคิดที่คุ้นเคยกับโปรแกรมเมอร์ส่วนใหญ่

ดังนั้นการเรียนรู้ Erlang จะเป็นเรื่องท้าทายสำหรับโปรแกรมเมอร์ส่วนใหญ่มากกว่าการเรียนรู้ Node.js - โดยเฉพาะอย่างยิ่งหากโปรแกรมเมอร์คุ้นเคยกับ JavaScript อยู่แล้ว อย่างไรก็ตามในที่สุดเมื่อคุณผ่านอุปสรรคทางความคิดฉันส่งว่าการเข้ารหัส Erlang จะมีความซับซ้อนน้อยกว่าการเข้ารหัส Node.js นี่คือสาเหตุหลายประการ:

  • รูปแบบการทำงานพร้อมกันของ Erlang ทำให้การไหลของลอจิกมีความชัดเจนยิ่งกว่า "เครื่องปฏิกรณ์" - แบบเห็นพ้องพร้อมกัน แทบจะไม่มีปัญหาเลยสำหรับโปรแกรมเมอร์ Erlang ที่จะทิ้งโพรเซสหลายพันโพรเซสในโปรแกรมทั่วไปในขณะที่ปล่อยเธรดหลายพันตัวเข้าด้วยกัน, Java กล่าวว่าเป็นฝันร้ายของความขัดแย้ง (ไม่ต้องพูดถึงหน่วยความจำ เทียบเท่ากับการรักษาหลายพันรัฐที่แยกจากกันในการตั้งค่าตามเครื่องปฏิกรณ์จะเป็นฝันร้ายที่จะอ่าน
  • เป็นภาษาทำงาน (ส่วนใหญ่ -), Erlang เป็นอย่างมาก "สิ่งที่คุณเห็นคือสิ่งที่คุณได้รับ" การติดตั้ง ตัวแปรเมื่อตั้งค่าแล้วจะไม่เปลี่ยนแปลง เคย ไม่มี OOP "การกระทำที่น่ากลัวในระยะไกล" เพื่อสร้างความสับสนให้คุณ: สิ่งใดก็ตามที่คุณทำงานด้วยจะถูกวางไว้ด้านหน้าคุณอย่างชัดเจน ไม่มีตัวแปรที่สืบทอดจาก X และไม่มีตัวแปรคลาสจาก Y และไม่มีตัวแปรส่วนกลางจาก Z ที่เกี่ยวข้องกับตัวคุณ (ประเด็นหลังนี้ไม่ได้เป็นความจริง 100% แต่เป็นเรื่องจริงในหลาย ๆ กรณีที่ดีพอสำหรับขั้นตอนการเรียนรู้ของคุณ)
  • สิ่งอำนวยความสะดวกที่ทรงพลัง Erlang มีไว้สำหรับจัดการข้อผิดพลาดหมายความว่าคุณทำให้รหัสของคุณยุ่งเหยิงด้วยการตั้งโปรแกรมการป้องกันที่น้อยกว่า
  • ไลบรารี OTP เมื่อคุณค้นหามันเป็นกองซ้อนรหัสทั่วไปที่ทรงพลังอย่างไม่น่าเชื่อซึ่งทำให้แอปพลิเคชั่นทั้งหมดของคุณเป็นปกติและครอบคลุมปัญหามากมายและใช้กรณีของเซิร์ฟเวอร์ที่มีอายุการใช้งานยาวนานซึ่งคุณอาจไม่คิดจนกว่าจะสายเกินไป ห้องสมุด OTP เองก็คือ IM (ns) HO เป็นเหตุผลที่ดีพอที่จะเรียนรู้ Erlang

ดำเนินการต่อไปที่ Erlang หากคุณทำได้และถ้ายังไม่ได้ทำให้ไปที่Learn You Erlang for Great Goodสำหรับการแนะนำที่นุ่มนวลและอ่อนโยน (ส่วนใหญ่) แนะนำแนวคิดของ Erlang


ขอบคุณสำหรับโพสต์นี้ ฉันกำลังอ่านผ่าน Learn You บ้าง Erlang ตอนนี้และฉันผ่านหนังสือมาครึ่งหนึ่งแล้ว แต่ฉันรู้สึกว่าฉันจะต้องรู้ทั้งหมดก่อนที่ฉันจะสามารถเริ่มทำบางสิ่งบางอย่างได้พอสมควร อย่างมีนัยสำคัญและไม่เพียง แต่นำมันทีละชิ้น
Noli

1
ที่จริงแล้วเมื่อคุณเข้าไปในส่วนที่เกิดขึ้นพร้อมกันของหนังสือคุณจะเริ่มสามารถทำสิ่งที่สำคัญพอสมควรได้อย่างง่ายดาย
เพียงแค่ความคิดเห็นที่ถูกต้องของฉัน

"รูปแบบการทำงานพร้อมกันของ Erlang ทำให้การไหลของลอจิกมีความชัดเจนมากกว่า" เครื่องปฏิกรณ์ "- การทำงานพร้อมกันในรูปแบบ" - ฉันขอเถียงว่าในขณะที่การประมวลผลแบบ async ของเครื่องปฏิกรณ์เป็นระเบียบมานานหลายสิบปี กรณีอีกต่อไป ด้วยการรอคอยคุณสามารถมี coroutines น้ำหนักเบาเป็นพิเศษของคุณแสดง "ราวกับว่า" พวกเขากำลังบอก-หัวข้อ (และผมไม่แน่ใจว่าเกี่ยวกับ JS แต่ใน C ++ co_await เป็นสถาปัตยกรรมการปรับขนาดไม่ได้ไปหลายพันเพียง แต่พันล้านของที่โดดเด่น coroutines)
ไม่มีข้อบกพร่องกระต่าย

"เป็นเพียงความคิดที่แปลกใหม่ที่ Chambers Constant (99.44%)" - และสำหรับโครงการอุตสาหกรรมใด ๆ สิ่งนี้ถือว่าเป็นปัญหาใหญ่ ปัญหาไขมันใหญ่นี้จะคงอยู่แม้ว่าจะไม่มีเหตุผลที่ชัดเจนสำหรับภาษาที่ใช้งานไม่ได้ (ซึ่งฉันไม่ได้รังเกียจ แต่นี่เป็นเรื่องที่แตกต่างและยาวมาก)
ไม่มีข้อบกพร่องกระต่าย

10

มีความแตกต่างที่สำคัญบางอย่างระหว่าง Erlang และ Node

อย่างแรกคือโหนดคือ Javascript ซึ่งหมายความว่าเป็นภาษาที่ใช้กันทั่วไปซึ่งมีลักษณะหลายอย่างร่วมกับภาษาที่ผู้คนคุ้นเคยมากกว่าดังนั้นจึงมักจะง่ายต่อการเริ่มต้นใช้งาน Erlang มีไวยากรณ์ที่แปลกและไม่คุ้นเคยเป็นส่วนใหญ่และถึงแม้ว่าภาษาจะง่ายกว่า javascript มาก แต่ก็ต้องใช้ความคุ้นเคยเล็กน้อยเนื่องจากความเป็นเอกลักษณ์

ประการที่สองคือ Erlang มีรูปแบบการทำงานพร้อมกันที่ไม่มีอะไรเกิดขึ้นร่วมกันโดยเฉพาะคุณต้องคิดในวิธีที่ต่างกันเพื่อแก้ปัญหาซึ่งเป็นสิ่งที่ดี

สิ่งสำคัญสุดท้ายคือ Erlang ได้รับการพัฒนาโดย บริษัท การค้าและเปิดแหล่งที่มาหลังจากความจริงมันเป็นเพียง 2 ปีที่ผ่านมาหรือเพื่อให้ผู้คนสามารถเห็นความมุ่งมั่นของแต่ละบุคคลในการควบคุมแหล่งที่มาและแม้ตอนนี้ฉันไม่คิดว่านักพัฒนา Erlang ทุกคน เพื่อสาธารณะ repit Github สำหรับการพัฒนาของพวกเขา node.js ถูกสร้างขึ้นภายในชุมชนตั้งแต่เริ่มต้นซึ่งหมายความว่าการสนับสนุนชุมชนดีกว่ามากมีไลบรารีสำหรับโหนดเพิ่มขึ้นเอกสารชุมชนมากขึ้นตัวอย่างสดมากขึ้นตัวจัดการแพคเกจที่แพร่หลายเป็นต้น Erlang กำลังติดตาม ในเรื่องนี้ แต่มันก็ยังเป็นทางลาดขนาดใหญ่กว่าที่จะลุกขึ้น

โหนดจะช่วยให้คุณเขียนโปรแกรมสนุก ๆ ได้อย่างรวดเร็วและไร้ความเจ็บปวด แต่ก็ยังมีความเจ็บปวดเพิ่มมากขึ้นเกี่ยวกับแอพพลิเคชั่นขนาดใหญ่ที่ erlang แก้ได้เป็นเวลานาน Erlang จะเปลี่ยนวิธีการเขียนโปรแกรมของคุณและ (imo) ทำให้คุณเป็นโปรแกรมเมอร์ที่ดีขึ้น แต่จะไม่ทำให้ชีวิตคุณง่ายขึ้นในการเริ่มต้น ทั้งสองวิธีต่างกันอย่างสนุกสนาน


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