เมื่อใดควรใช้แลมบ์ดาเมื่อใดควรใช้ Proc.new?


336

ใน Ruby 1.8 มีความแตกต่างเล็กน้อยระหว่าง proc / lambda ในมือข้างหนึ่งและProc.newอีกด้านหนึ่ง

  • ความแตกต่างเหล่านั้นคืออะไร?
  • คุณสามารถให้แนวทางเกี่ยวกับวิธีการตัดสินใจเลือกได้หรือไม่?
  • ใน Ruby 1.9 proc และ lambda นั้นแตกต่างกัน ตกลงคืออะไร?

3
ดูเพิ่มเติม: หนังสือภาษาโปรแกรมภาษาทับทิมโดย Matz และ Flanagan ซึ่งครอบคลุมหัวข้อนี้อย่างละเอียด proc ทำตัวเหมือนซีแมนทิกส์บล็อกผลตอบแทนซึ่งแลมบ์ดาทำตัวเหมือนความหมายของเมธอดโทร - เมธอด ส่งคืนทำลายและอื่น ๆ ทุกพฤติกรรมต่างกันใน procs n lambdas
Gishu


คุณยอมรับคำตอบที่บอกว่าอะไรคือความแตกต่างระหว่าง proc และแลมบ์ดาเท่านั้นในขณะที่ชื่อคำถามของคุณคือเมื่อไรที่จะใช้สิ่งเหล่านั้น
Shri

คำตอบ:


378

ข้อแตกต่างที่สำคัญ แต่แตกต่างกันเล็กน้อยระหว่าง procs ที่สร้างด้วยlambdaและ procs ที่สร้างด้วยProc.newคือวิธีจัดการกับreturnคำสั่ง:

  • ในlambda-created proc returnคำสั่งจะส่งกลับจาก proc เท่านั้น
  • ในProc.new-created proc returnคำสั่งนั้นน่าแปลกใจมากกว่านี้เล็กน้อย: มันกลับมาควบคุมไม่เพียงแค่จาก proc เท่านั้น แต่ยังมาจากวิธีการที่ล้อมรอบ proc ด้วย!

นี่คือการกระทำlambdaของ proc ที่สร้างreturnขึ้น มันทำงานในลักษณะที่คุณอาจคาดหวัง:

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

ทีนี้นี่คือProc.newproc ที่สร้างขึ้นreturnทำสิ่งเดียวกัน คุณกำลังจะเห็นหนึ่งในกรณีเหล่านั้นที่รูบี้ทำลายหลักการเซอร์ไพรส์อย่างน้อยที่โอ้อวดมาก:

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

ขอบคุณกับพฤติกรรมที่น่าแปลกใจนี้ (เช่นเดียวกับพิมพ์น้อยลง) ผมมักจะชอบใช้lambdaมากกว่าProc.newเมื่อมีการ procs


12
จากนั้นยังมีprocวิธีการ มันเป็นแค่การจดชวเลขProc.newหรือเปล่า?
panzi


4
@mattdipasquale ในการทดสอบของฉันprocทำตัวเหมือนlambdaและไม่ชอบProc.newเกี่ยวกับการส่งคืนข้อความ นั่นหมายความว่าทับทิมเอกสารไม่ถูกต้อง
เคลวิน

31
@mattdipasquale ขออภัยฉันแค่ครึ่งเดียวเท่านั้น procทำหน้าที่เหมือนlambdaใน 1.8 แต่ทำหน้าที่เหมือนProc.newใน 1.9 ดูคำตอบของ Peter Wagenet
เคลวิน

55
เหตุใดจึงมีพฤติกรรม "น่าประหลาดใจ" นี้ A lambdaเป็นวิธีที่ไม่ระบุชื่อ เนื่องจากเป็นเมธอดมันจะส่งคืนค่าและเมธอดที่เรียกว่าสามารถทำกับมันได้ทุกอย่างที่ต้องการรวมถึงการเพิกเฉยและคืนค่าที่แตกต่าง A Procเป็นเหมือนการวางโค้ดขนาดสั้น มันไม่ได้ทำหน้าที่เหมือนวิธีการ ดังนั้นเมื่อมีการส่งคืนเกิดขึ้นภายในProcนั่นเป็นเพียงส่วนหนึ่งของรหัสของวิธีการที่เรียกว่า
Arcolye

96

เพื่อให้คำชี้แจงเพิ่มเติม:

Joey กล่าวว่าพฤติกรรมการกลับมาของProc.newน่าแปลกใจ อย่างไรก็ตามเมื่อคุณพิจารณาว่า Proc.new ทำงานเหมือนบล็อกสิ่งนี้ไม่น่าแปลกใจเพราะนั่นเป็นวิธีการทำงานของบล็อก lambas ในทางกลับกันทำตัวเหมือนวิธีการมากกว่า

สิ่งนี้อธิบายได้ว่าทำไม Procs จึงมีความยืดหยุ่นเมื่อพูดถึง arity (จำนวนอาร์กิวเมนต์) ในขณะที่ lambdas ไม่ใช่ บล็อกไม่จำเป็นต้องระบุอาร์กิวเมนต์ทั้งหมด แต่จะใช้วิธีการนั้น ในขณะที่การระบุค่าเริ่มต้นของอาร์กิวเมนต์ lambda ไม่ใช่ตัวเลือกใน Ruby 1.8 ตอนนี้ได้รับการสนับสนุนใน Ruby 1.9 ด้วยไวยากรณ์ lambda ทางเลือก (ดังที่บันทึกไว้โดย webmat):

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

และ Michiel de Mare (OP) ไม่ถูกต้องเกี่ยวกับ Procs และแลมบ์ดาทำตัวเหมือนกันกับ arity ใน Ruby 1.9 ฉันได้ตรวจสอบแล้วว่าพวกเขายังคงรักษาพฤติกรรมจาก 1.8 ตามที่ระบุไว้ข้างต้น

breakคำแถลงไม่สมเหตุสมผลจริงๆทั้งใน Procs หรือ lambdas ใน Procs การแบ่งจะส่งคืนให้คุณจาก Proc.new ที่เสร็จสมบูรณ์แล้ว และมันก็ไม่สมเหตุสมผลเลยที่จะแยกออกจากแลมบ์ดาเพราะมันเป็นวิธีการหนึ่งและคุณจะไม่มีวันแตกจากระดับสูงสุดของวิธีการ

next, redoและraiseประพฤติตนเหมือนกันทั้งใน Procs และ lambdas ในขณะที่retryไม่ได้รับอนุญาตในทั้งสองและจะยกข้อยกเว้น

และสุดท้ายprocไม่ควรใช้วิธีนี้เนื่องจากไม่สอดคล้องกันและมีพฤติกรรมที่ไม่คาดคิด ใน Ruby 1.8 มันจะส่งคืนแลมบ์ดาจริง! ใน Ruby 1.9 สิ่งนี้ได้รับการแก้ไขแล้วและส่งคืน Proc หากคุณต้องการสร้าง Proc ให้ทำProc.newตาม

สำหรับข้อมูลเพิ่มเติมฉันขอแนะนำของThe Ruby Programming Languageของ O'Reilly ซึ่งเป็นแหล่งข้อมูลส่วนใหญ่ของฉันสำหรับข้อมูลนี้


1
"" "อย่างไรก็ตามเมื่อคุณพิจารณาว่า Proc.new ทำงานเหมือนบล็อกสิ่งนี้ไม่น่าแปลกใจเลยว่านั่นเป็นวิธีการทำงานของบล็อก" "" <- บล็อกเป็นส่วนหนึ่งของวัตถุในขณะที่ Proc.new สร้างวัตถุ ทั้งแลมบ์ดาและ Proc.new สร้างวัตถุที่มีคลาสเป็น Proc เหตุใดจึงต่างกัน
อ่อนตัว

1
ในฐานะของทับทิม 2.5 breakจากยก Procs LocalJumpErrorขณะที่breakจาก lambdas พฤติกรรมเช่นเดียวreturn( เช่น , return nil)
Masa Sakano

43

ฉันพบหน้านี้ซึ่งแสดงความแตกต่างระหว่างProc.newและlambdaคือ ตามหน้าเว็บที่แตกต่างเพียงว่าแลมบ์ดาเป็นที่เข้มงวดเกี่ยวกับจำนวนของข้อโต้แย้งยอมรับในขณะที่แปลงข้อโต้แย้งไปProc.new nilนี่คือตัวอย่างเซสชัน IRB ที่แสดงให้เห็นถึงความแตกต่าง:

irb (main): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (irb): 1>
irb (main): 002: 0> p = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (irb): 2>
irb (หลัก): 003: 0> l.call "hello", "world"
=> "helloworld"
irb (หลัก): 004: 0> p.call "hello", "world"
=> "helloworld"
irb (หลัก): 005: 0> l.call "hello"
ArgumentError: จำนวนอาร์กิวเมนต์ไม่ถูกต้อง (1 สำหรับ 2)
    จาก (irb): 1
    จาก (irb): 5: ใน `call '
    จาก (irb): 5
    จาก: 0
irb (หลัก): 006: 0> p.call "hello"
TypeError: ไม่สามารถแปลงศูนย์เป็นสตริงได้
    จาก (irb): 2: ใน `+ '
    จาก (irb): 2
    จาก (irb): 6: ใน `call '
    จาก (irb): 6
    จาก: 0

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

สำหรับ Ruby 1.9 ขอโทษฉันยังไม่ได้ดูที่ 1.9 แต่ฉันไม่คิดว่าพวกเขาจะเปลี่ยนมันทั้งหมดมาก (อย่าใช้คำพูดของฉันมันดูเหมือนว่าคุณเคยได้ยินการเปลี่ยนแปลงบางอย่างดังนั้น ฉันอาจจะผิดที่นั่น)


2
procs กลับต่างจาก lambdas
บแคม

"" "Proc.new แปลงอาร์กิวเมนต์ที่หายไปเป็นศูนย์" "" Proc.new ยังละเว้นอาร์กิวเมนต์เพิ่มเติม (แน่นอนแลมบ์ดาบ่นเรื่องนี้ด้วยข้อผิดพลาด)
อ่อนตัว

16

Proc มีอายุมากกว่า แต่ความหมายของการกลับมาเป็นสิ่งที่ต่อต้านได้ง่ายสำหรับฉัน (อย่างน้อยตอนที่ฉันเรียนรู้ภาษา) เพราะ:

  1. หากคุณกำลังใช้ proc คุณมักจะใช้กระบวนทัศน์การทำงานบางประเภท
  2. Proc สามารถกลับออกจากขอบเขตการปิดล้อม (ดูคำตอบก่อนหน้า) ซึ่งเป็น goto โดยทั่วไปและไม่สามารถใช้งานได้ในธรรมชาติ

แลมบ์ดาปลอดภัยกว่าและใช้งานง่ายกว่า - ฉันมักจะใช้มันแทน proc


11

ฉันไม่สามารถพูดอะไรได้มากมายเกี่ยวกับความแตกต่างที่ลึกซึ้ง อย่างไรก็ตามฉันสามารถชี้ให้เห็นว่า Ruby 1.9 ตอนนี้อนุญาตพารามิเตอร์ทางเลือกสำหรับ lambdas และบล็อก

นี่คือไวยากรณ์ใหม่สำหรับ lambdas ของ stabby ต่ำกว่า 1.9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Ruby 1.8 ไม่มีไวยากรณ์นั้น วิธีการทั่วไปในการประกาศบล็อก / lambdas ไม่รองรับ args เสริม:

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

อย่างไรก็ตาม Ruby 1.9 ยังสนับสนุนอาร์กิวเมนต์ที่เป็นทางเลือกแม้จะมีไวยากรณ์เก่า:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

หากคุณต้องการสร้าง Ruby1.9 สำหรับ Leopard หรือ Linux ตรวจสอบบทความนี้ (การส่งเสริมตนเองที่ไร้ยางอาย)


ตัวเลือก params ภายในแลมบ์ดานั้นมีความจำเป็นมากฉันดีใจที่พวกเขาเพิ่มมันใน 1.9 ฉันถือว่าบล็อกสามารถมีพารามิเตอร์ทางเลือกได้เช่นกัน (ใน 1.9)
mpd

คุณไม่ได้แสดงให้เห็นถึงพารามิเตอร์เริ่มต้นในบล็อกเฉพาะ lambdas
iconoclast

11

คำตอบสั้น ๆ : สิ่งที่สำคัญคือทำอะไรreturn: แลมบ์ดากลับมาจากตัวของมันเองและพรานกลับมาจากตัวมันเองและฟังก์ชั่นที่เรียกมันว่า

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

ในทางกลับกัน Proc มีประโยชน์อย่างยิ่งสำหรับการใช้งานภาษาเอง ตัวอย่างเช่นคุณสามารถใช้คำสั่ง "ถ้า" หรือ "สำหรับ" ลูปกับพวกเขา การส่งคืนใด ๆ ที่พบใน proc จะส่งคืนจากวิธีการที่เรียกว่าไม่ใช่แค่คำสั่ง "if" นี่คือวิธีการทำงานของภาษาการใช้คำว่า "ถ้า" ดังนั้นการเดาของฉันคือ Ruby ใช้สิ่งนี้ภายใต้หน้าปกและพวกเขาก็เปิดเผยเพราะมันดูทรงพลัง

คุณจะต้องใช้สิ่งนี้จริงๆถ้าคุณกำลังสร้างโครงสร้างภาษาใหม่เช่นลูป, โครงสร้างอื่น ๆ , เป็นต้น


1
แลมบ์ดากลับมาจากตัวของมันเองและพรานกลับมาจากตัวของมันเองและฟังก์ชั่นที่เรียกมันว่า "ผิดธรรมดาและเป็นความเข้าใจผิดที่พบบ่อยมาก proc คือการปิดและส่งคืนจากวิธีการที่สร้างขึ้น ดูคำตอบแบบเต็มของฉันที่อื่นบนหน้า
ComDubh

10

วิธีที่ดีในการดูคือ lambdas จะถูกดำเนินการในขอบเขตของตัวเอง (ราวกับว่าเป็นการเรียกใช้เมธอด) ในขณะที่ Procs อาจถูกมองว่าดำเนินการแบบอินไลน์ด้วยวิธีการโทรอย่างน้อยก็เป็นวิธีที่ดีในการตัดสินใจเลือก ในแต่ละกรณี.


8

ฉันไม่ได้สังเกตเห็นความคิดเห็นใด ๆ เกี่ยวกับวิธีที่สามในเควส "proc" ซึ่งเลิกใช้แล้ว แต่จัดการแตกต่างกันใน 1.8 และ 1.9

นี่เป็นตัวอย่างที่ค่อนข้างชัดเจนซึ่งทำให้ง่ายต่อการเห็นความแตกต่างระหว่างการเรียกที่คล้ายกันทั้งสาม:

def meth1
  puts "method start"

  pr = lambda { return }
  pr.call

  puts "method end"  
end

def meth2
  puts "method start"

  pr = Proc.new { return }
  pr.call

  puts "method end"  
end

def meth3
  puts "method start"

  pr = proc { return }
  pr.call

  puts "method end"  
end

puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3

1
Matz ได้กล่าวว่าเขาวางแผนที่จะเลิกใช้เพราะมันสับสนที่จะมี proc และ Proc.new กลับผลลัพธ์ที่แตกต่าง ใน 1.9 พวกเขาประพฤติตนเหมือนกัน (proc เป็นนามแฝงของ Proc.new) eigenclass.org/hiki/Changes+in+Ruby+1.9#l47
Dave Rapin

@banister: procคืนแลมบ์ดาเป็น 1.8 ตอนนี้ได้รับการแก้ไขแล้วให้ส่งคืน proc ใน 1.9 - อย่างไรก็ตามนี่เป็นการเปลี่ยนแปลงที่ผิดพลาด ดังนั้นจึงไม่แนะนำให้ใช้อีกต่อไป
Gishu

ฉันคิดว่าพลั่วพูดในเชิงอรรถที่ไหนสักแห่งที่ proc มีการลดทอนอย่างมีประสิทธิภาพหรือบางสิ่งบางอย่าง ฉันไม่มีหมายเลขหน้าแน่นอน
Dertoni

7

Closures in Rubyเป็นภาพรวมที่ดีเกี่ยวกับวิธีการทำงานของบล็อกแลมบ์ดาและ proc ใน Ruby กับ Ruby


ฉันหยุดอ่านสิ่งนี้หลังจากที่ฉันอ่าน "ฟังก์ชั่นไม่สามารถรับหลายบล็อคได้ - เป็นการละเมิดหลักการที่การปิดสามารถถูกส่งผ่านได้อย่างอิสระเป็นค่า" บล็อกไม่ใช่การปิด Procs คือและฟังก์ชั่นสามารถรับหลาย procs
ComDubh

5

แลมบ์ดาทำงานได้ตามที่คาดหวังเช่นในภาษาอื่น ๆ

สายProc.newมีความประหลาดใจและสับสน

returnคำสั่งใน proc สร้างขึ้นโดยProc.newไม่เพียง แต่จะกลับมาควบคุมที่มาจากตัวเอง แต่ยังมาจากวิธีการที่ล้อมรอบมัน

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

คุณสามารถยืนยันว่าการProc.newแทรกรหัสลงในวิธีการปิดล้อมเช่นเดียวกับบล็อก แต่Proc.newสร้างวัตถุในขณะที่บล็อกเป็นส่วนหนึ่งของวัตถุ

และมีความแตกต่างระหว่างแลมบ์ดาและProc.newซึ่งก็คือการจัดการกับข้อโต้แย้ง (ผิด) แลมบ์ดาบ่นเกี่ยวกับมันในขณะที่Proc.newไม่สนใจข้อโต้แย้งเพิ่มเติมหรือพิจารณาว่าไม่มีข้อโต้แย้งใด ๆ

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

BTW procใน Ruby 1.8 จะสร้างแลมบ์ดาในขณะที่ Ruby 1.9+ จะมีพฤติกรรมเช่นProc.newนี้ซึ่งทำให้เกิดความสับสน


3

เพื่ออธิบายรายละเอียดเกี่ยวกับการตอบสนองของ Accordion Guy:

ขอให้สังเกตว่าProc.newสร้าง proc ออกโดยการผ่านบล็อก ฉันเชื่อว่าlambda {...}มีการแยกวิเคราะห์เป็นตัวอักษรมากกว่าการเรียกเมธอดที่ผ่านบล็อก returnไอเอ็นจีจากภายในบล็อกที่แนบมากับการเรียกวิธีการจะกลับมาจากวิธีการไม่บล็อกและProc.newกรณีที่เป็นตัวอย่างของเรื่องนี้ที่เล่น

(นี่คือ 1.8. ฉันไม่รู้ว่าสิ่งนี้แปลเป็น 1.9.)


3

ฉันสายไปนิดหน่อย แต่มีสิ่งหนึ่งที่ดี แต่ไม่ค่อยมีใครรู้จักเกี่ยวกับการProc.newไม่พูดถึงในความคิดเห็นเลย ตามเอกสาร :

Proc::newอาจถูกเรียกโดยไม่มีบล็อกเฉพาะภายในวิธีการที่มีบล็อกที่แนบมาซึ่งในกรณีนั้นบล็อกนั้นจะถูกแปลงเป็นProcวัตถุ

ที่กล่าวว่าProc.newช่วยให้วิธีการห่วงโซ่ผลผลิต:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!

ที่น่าสนใจมันทำเช่นเดียวกับการประกาศ&blockข้อโต้แย้งในdefแต่ไม่ต้องทำเช่นนั้นในรายการหาเรื่อง
jrochkind

2

มันคุ้มค่าที่เน้นว่าreturnในผลตอบแทน proc จากวิธี lexically ล้อมรอบคือวิธีการที่ proc ที่ถูกสร้างขึ้น , ไม่ได้วิธีการที่เรียกว่า proc นี่คือผลที่ตามมาของคุณสมบัติการปิดของ procs ดังนั้นรหัสต่อไปนี้ไม่มีอะไรแสดงผล:

def foo
  proc = Proc.new{return}
  foobar(proc)
  puts 'foo'
end

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

แม้ว่า proc รันในfoobarมันถูกสร้างขึ้นในfooและเพื่อให้returnออกไม่เพียงfoo foobarดังที่ Charles Caldwell เขียนไว้ด้านบนมันให้ความรู้สึกแบบ GOTO ในความคิดของฉันreturnมันเป็นเรื่องปกติในบล็อกที่ดำเนินการในบริบทของคำศัพท์ แต่มีความเป็นธรรมชาติน้อยกว่าเมื่อใช้ใน proc ที่ดำเนินการในบริบทที่แตกต่างกัน


1

ความแตกต่างของพฤติกรรมด้วยreturnIMHO คือความแตกต่างที่สำคัญที่สุดระหว่าง 2 ฉันชอบแลมบ์ดามากกว่าเพราะมันพิมพ์น้อยกว่า Proc.new :-)


2
การอัปเดต: procs proc {}ขณะนี้สามารถสร้างขึ้นโดยใช้ ฉันไม่แน่ใจว่าเมื่อสิ่งนี้มีผล แต่มัน (เล็กน้อย) ง่ายกว่าการพิมพ์ Proc.new
aceofbassgreg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.