สถาบันข้อมูลขนาดใหญ่ (องค์การมหาชน)

คำสั่งแบบ Switch-Case Statements จะสามารถใช้ร่วมกับ Python ได้แล้ว!

Jul 31, 2022
Python คำสั่ง

Python คำสั่ง

เวอร์ชั่นที่ออกมาใหม่ล่าสุดเผยให้เห็น New Logic (ตรรกะแบบใหม่)

Python 3.10 เริ่มจะเต็มไปด้วยฟีเจอร์ใหม่ ๆ น่าทึ่งหลายอย่าง มีฟีเจอร์หนึ่งที่น่าสนใจมาก — Structural Pattern Matching (การเทียบเคียงรูปแบบแบบมีโครงสร้าง) — หรือที่พวกเรารู้จักกันคือ switch/case statement ซึ่ง Switch-statement ห่างหายไปจาก Python มานานแม้ว่าจะเป็นฟีเจอร์ทั่วไปของเกือบทุกภาษา

ย้อนกลับไปปี 2006 ที่มีการสร้าง PEP 3103 ขึ้น โดยแนะนำให้ดำเนินการตามคำสั่ง Switch-case Statement แต่หลังจากทำแบบสำรวจที่ PyCon 2007 หรืองานสัมมนาเกี่ยวกับภาษาที่ใช้เขียนโปรแกรม พบว่าฟีเจอร์ดังกล่าวไม่ได้รับการสนับสนุนผู้พัฒนา Python จึงล้มเลิกการพัฒนาฟีเจอร์นี้ไป ในปี 2020 และ Guido van Rossum (กวีโด แวน รอสซัม) ผู้สร้าง Python นำเสนอเอกสารกำกับโปรแกรมฉบับแรกภายใต้หัวข้อ Switch-statement แบบใหม่ ที่ถูกตั้งชื่อว่า Structural Pattern Matching ตามที่พบใน PEP 634

โดยสิ่งที่เรามีในตอนนี้เป็นมากกว่าแค่ Switch-case Statement แบบเรียบง่าย  ซึ่งเดี๋ยวเราจะได้เห็นกันในบทความนี้

มาดูกันว่าตรรกะแบบใหม่นี้ทำงานอย่างไร

> Structural Pattern Matching
  - Intro
  - Another Example
  - Real Use-Case With JSON

Structural Pattern Matching

Pattern Matching ใช้ค่าตัวเลขหลังจาก match และช่วยให้เราเขียนเคสที่อาจมีออกมาหลายกรณี โดยแต่ละกรณีจำกัดความว่าเป็น case ที่เท่าไหร่ และถ้าเราต้องจับคู่ระหว่าง Match และ Case หรือ match-case เราจะต้องใช้งานโค้ดตามลำดับ

ตัวอย่างเช่น:

http_code = "418"

match http_code:
    case "200":
        print("OK")
        do_something_good()
    case "404":
        print("Not Found")
        do_something_bad()
    case "418":
        print("I'm a teapot")
        make_coffee()
    case _:
        print("Code not found")

ในส่วนนี้เราต้องตรวจดูเงื่อนไขหลายอย่าง รวมถึงการดำเนินการปฏิบัติการที่แตกต่างกัน โดยขึ้นอยู่กับค่าตัวเลขที่เราพบภายใน http_code

จากแผนผังในด้านบน จะเห็นได้ชัดในทันทีว่าเราสามารถสร้างตรรกะเดียวกันโดยใช้กลุ่มของ If-elif-else Statement:

http_code = "418"

if http_code == "418":
    print("OK")
    do_something_good()
elif http_code == "404":
    print("Not Found")
    do_something_bad()
elif http_code == "418"
    print("I'm a teapot")
    make_coffee()
else:
    print("Code not found")

อย่างไรก็ตาม เราสามารถใช้ Match-case Statement ลบการทำซ้ำของ http_code == ออก ซึ่งเมื่อทดสอบกับหลากหลายเงื่อนไขจะให้ผลลัพธ์ที่ดูง่ายกว่า

อีกหนึ่งตัวอย่างของ Match-case Statement

เราจะศึกษาตัวอย่างที่ดีของการใช้ Match-case Statement เพื่อปรับปรุงความสามารถในการอ่านโค้ดใน PEP 635 หนึ่งในนั้นคือตัวอย่างนี้ ซึ่งแสดงให้เราเห็นวิธีใช้ Match-case เพื่อตรวจดูประเภทและโครงสร้างของตัวทดสอบของเรา:

match x:
    case host, port:
        mode = "http"
    case host, port, mode:
        pass

ในส่วนนี้เรามักจะคาดหวังที่จะได้รายละเอียดการเชื่อมโยงในรูปแบบ Tuple (ทูเพิล) และมอบหมายค่าตัวเลขที่ให้มากับตัวแปรที่ถูกต้อง ซึ่งถ้า mode การเชื่อมโยงยังไม่ถูกกำหนดภายใน Tuple (ตัวอย่างเช่น มีเพียงค่าตัวเลขสองค่าที่ให้มาคือ host และ port) เราสันนิษฐานได้ว่าโหมดการเชื่อมโยงคือ “http”.

อย่างไรก็ตามในกรณีอื่น ๆ เราอาจคาดการณ์ได้ว่าโหมดถูกกำหนดไว้แล้วอย่างชัดเจน เราจึงอาจจะได้รับ Tuple แบบ (<host>, <port>, "ftp") — ซึ่งในกรณีนี้เราไม่ได้อยากตั้ง mode เป็น “http”

และถ้าเราอยากจะเขียนตรรกะเดียวกันโดยใช้ If-else Statement เราจะได้สิ่งนี้:

if isinstance(x, tuple) and len(x) == 2:
    host, port = x
    mode = "http"
elif isinstance(x, tuple) and len(x) == 3:
    host, port, mode = x

การเลือกใช้ของแต่ละคนอาจจะแตกต่างกันตามความ แต่สำหรับผมแล้วการใช้งาน Match-case ดูได้สบายตากว่ามาก

กรณีศึกษาของ JSON

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

ถ้าเรามาลองดูโครงสร้างของ SQuAD จะเห็นชัดว่ามีหลากหลายระดับ ซึ่งเราจะต้องพิจารณาเมื่อทำการแยกวิเคราะห์:

รูปแบบ JSON ของชุดเทรนนิ่ง SQuAD 2

แต่ปัญหาคือ ไม่ใช่ว่าทุกตัวอย่างจะสามารถใช้รูปแบบพจนานุกรมแบบเดียวกันได้ ถ้าเราดูสองสามตัวอย่างสุดท้าย เราจะเห็นรายการ qas มีทั้ง answers และ plausible_answers — ขณะที่ตัวอย่างมีเพียงแค่ answers:

การกรอกข้อมูลสุดท้ายในชุดเทรนนิ่ง SQuAD 2 JSON แสดงให้เห็นรูปแบบพจนานุกรมที่แตกต่างกัน (ซึ่งการกรอกข้อมูลอื่นๆ มากมายทั่วไฟล์ใช้แบบเดียวกัน)

ทีนี้ลองมาใช้ Match-case Statement แบบใหม่เพื่อสร้างทางเลือกที่สบายตากว่าสำหรับตรรกะหนัก ๆ แบบ if-else ที่จำเป็นต้องใช้กับสิ่งนี้  อย่างแรก เราต้องโหลดข้อมูลมาก่อน:

เราโหลดข้อมูลโดยใช้ json.load

SQuAD JSON มีหลายระดับชั้น  หลังจากเข้าถึง squad['data']  เราจำเป็นต้องทำซ้ำในคำถามแต่ละ group: จากนั้นในแต่ละ paragraph แล้วในแต่ละ qas (คำถาม-คำตอบ) — ซึ่งดูเหมือน:

for group in squad['data']:
    for paragraph in group['paragraphs']:
        for qa in paragraph['qas']:
            # insert code here

และนี่คือจุดที่เริ่มสนุกแล้วล่ะ การใช้ตรรกะแบบ if-else เราจะได้อะไรแบบนี้:

แม้ว่ามันจะดูไม่ค่อยสวย แต่มันก็สามารถใช้งานได้ ลองมาเขียนใหม่โดยใช้ตรรกะแบบ match-case ดูบ้าง:

จะเห็นได้ว่า การใช้ ตรรกะนี้จะช่วยให้ดูซับซ้อนน้อยลงและถือเป็นอีกหนึ่งทางเลือกที่ดีเมื่อเทียบกับเวอร์ชั่นดั้งเดิมที่ใช้ตรรกะการแยกวิเคราะห์

ทั้งหมดมีแค่นี้เป็นเรื่องของ Structural Pattern Matching แบบใหม่ที่นำเสนอใน Python 3.10! แบบสั้น ๆ เท่านั้น เวอร์ชั่นเต็มคาดว่าจะออกมาในต้นเดือนตุลาคม 2021

ส่วนตัวแล้ว ผมคิดว่า Syntax แบบใหม่นี้ดูดีมาก ๆ — แม้ว่าตอนนี้จะมั่นใจได้แค่ 50/50 แต่เมื่อมีผู้ใช้ match-case ทำโค้ดมากขึ้น ผมมั่นใจว่าพวกเขาจะเลือกวิธีการที่ดีที่สุดในการเขียน

บทความโดย James Briggs
เนื้อหาจากบทความของ Medium
แปลและเรียบเรียงโดย ไอสวรรค์ ไชยชะนะ
ตรวจทานและปรับปรุงโดยนววิทย์ พงศ์อนันต์