Comparison Operators กับ List และ Iterable อื่น ๆ บน Python

เรื่องนึงที่เราว่ามันเป็น Hidden หรือไม่ก็ Unseen สำหรับเรา เวลาเราใช้งาน Python เลย ตอนรู้จักครั้งแรกคือ ห่ะ แบบนี้ก็ได้เหรอ คือ การใช้พวก Comparison Operators อย่าง เท่ากับ มากกว่า น้อยกว่า อะไรพวกนั้น กับ Iterable ต่าง ๆ อย่างพวก List และ Set อะไรพวกนั้นได้ด้วย วันนี้เราลองมาดูกัน

Set

x = {1, 2, 3}
y = {2, 3, 4, 5}

z = x.issubset(y) 

เราขอเริ่มจากตัวที่เข้าใจง่ายที่สุดก่อน อย่าง Set ถ้าใครที่เคยใช้ Set ใน Python น่าจะคุ้นเคยกันดี ซึ่งหนึ่งในสิ่งที่เราจะเช็ค เราอาจจะเช็คพวก Subset และ Superset อะไรพวกนั้นกับ Set อีกตัว ซึ่งใน Python เอง เราก็สามารถที่จะเช็คได้ผ่านคำสั่ง issubset() ได้เลยอะไรแบบนั้น แต่เราจะเห็นว่า เออ มันก็ดูรก ๆ หน่อย มันน่าจะดีมาก ๆ ถ้ามี Shorthand ให้เราสามารถใช้ได้ พูดมาขนาดนี้ ใช่แล้วละ เราสามารถใช้ Comparison Operators ได้

>>> {1} == {1}
True

>>> {1} < {1,2}
True

>>> {1,2} < {1}
False

>>> {1,3} < {1,2}
False

ดูผ่าน ๆ อาจจะ งง นิด ๆ ทริกง่าย ๆ คือ เท่ากันหมายความว่า Set ทั้ง 2 มี Member เหมือนกันทุกตัว 100% ทำให้มันเท่ากัน ส่วนเครื่องหมาย มากกว่า น้อยกว่า มองง่าย ๆ ว่า ด้านที่น้อยกว่าเป็น Subset ของอีกด้านหรือไม่ ทำให้ในตัวอย่างที่ 2 เป็นจริง เพราะ 1 ของด้านซ้าย อยู่ใน Set ด้านขวาที่เอามาเปรียบเทียบ แต่ในตัวอย่างที่ 3 มองกลับกัน 2 ของด้านซ้าย ไม่ได้อยู่ใน Set ด้านขวา ทำให้ Set ด้านขวา ไม่ได้เป็น Superset ของ Set ด้านซ้ายทำให้ได้ False ไป และตัวอย่างสุดท้าย คือตัวอย่างที่ 4 อันนี้ไม่เป็นจริงใน 2 Member คือ 3 ในฝั่งซ้ายไม่ได้อยู่ในฝั่งขวา และ 2 ในฝั่งขวา ไม่ได้อยู่ในด้านซ้าย ทำให้ไม่เป็นจริงได้ False ออกมา

และถ้าเราเพิ่มเท่ากับ เป็น มากกว่าเท่ากับ และ น้อยกว่าเท่ากับ เราก็จะได้เป็นเงื่อนไขที่เป็น หรือ เช่น มากกว่าเท่ากับ ก็จะเป็น Superset หรือ เป็น Set เดียวกันอะไรแบบนั้นเหมือนเราใช้เครื่องหมายพวกนี้ตามปกติทุกประการเลยแค่นั้น

List และ Iterator อื่น ๆ

สาเหตุที่เราไม่เอา List ที่ดูจะเป็น Data Structure ที่ง่ายที่สุดขึ้นก่อน เพราะ มันเป็นตัวที่ปวดหัวมาก ๆ เพราะสิ่งที่มันจะเช็คคือ ตัวไหนมาก่อน หรือก็คือเป็น Lexicographic order บางคนก็เรียก Dictionary Order ก็ว่ากันไป เอาง่าย ๆ อย่าง String กันก่อน

>>> "brand" < "black"
False

เราเทียบง่าย ๆ เลย คือคำว่า Brand กับ Black ถ้าเราเอาน้อยกว่ามาใส่ มันจะเป็นจริงได้ก็ต่อเมื่อ Brand มาก่อน Black ใน Dictionary เทียบง่าย ๆ คือ ตัวแรก มันเท่ากัน เพราะคือตัว B เหมือนกัน แต่ตัวที่ 2 นี่แหละ ตัดเชือกละ สาเหตุที่มันเป็น False เพราะ L ที่อยู่ใน Black มันมาก่อน ตัว R ใน Brand ทำให้ Brand มันเลยมากกว่านั่นเอง แค่นี้เลย

>>> ["b", "r"] < ["b", "l"]
False

>>> [1,2] < [2,2]
True

ถ้าเราจำกันได้ จริง ๆ แล้ว String มันก็เป็นเหมือน List ที่มีแค่ Character และถ้าเราเห็นพฤติกรรมจากตัวอย่างก่อนหน้าแล้ว เราก็จะเดาของ List ได้ไม่ยากเลย เราเอาตัวอย่างเดิมมาเลย แต่เราใช้แค่ 2 ตัวแรก ก็รู้เรื่องแล้ว เราก็จะได้ผลลัพธ์เหมือนกันเป๊ะ ๆ เลย

งั้นเอาใหม่ เราลองมาเป็นอะไรที่เข้าใจง่ายกว่านั้นหน่อย เราเอา คู่ของ 1,2 และ 2,2 มา โดยเทียบกันผ่านเครื่องหมายน้อยกว่า มันก็จะเป็นจริง เพราะ 2 ในตัวแรกของฝั่งขวา มันมาทีหลัง 1 ที่เป็นตัวแรกของฝั่งซ้ายนั่นเอง

>>> [1] < [2,2]
True

ในตัวอย่างด้านบน เราทำให้ดูในกรณีที่จำนวนสมาชิกไม่เท่ากัน โดยเราจะเห็นว่า จริง ๆ แล้วมันไม่ได้แคร์จำนวนของสมาชิก หรือความยาวของ String ใด ๆ ทั้งสิ้น เพราะจริง ๆ แล้วมันจะค่อย ๆ เทียบทีละตัวไปเรื่อย ๆ จนได้ผลลัพธ์ออกมาว่า เป็นมากกว่าหรือน้อยกว่า หรือฝั่งใดฝั่งหนึ่งหมดก่อนมันจะออกมาเป็นเท่ากับ อย่างในกรณีนี้ เราจะเห็นว่า 1 ที่อยู่ใน List ด้านซ้ายมันมาก่อน 2 ที่เป็นตัวแรกของฝั่งขวา เลยทำให้ มันเป็นจริงนั่นเอง และแน่นอนว่า การเปรียบเทียบแบบนี้ เราสามารถใช้ได้กับ Tuple ก็ได้ด้วยเช่นกัน

>>> [1, 'a'] < [1, 'b']
True

อย่างที่เราบอกว่า มันจะเปรียบเทียบทีละตัวไปเรื่อย ๆ เราลองมาดูเคสที่ซับซ้อนมากกว่านี้กัน เราบอกว่า ใน List เรามี Member ที่เป็นคนละ DataType กัน ตัวนึงเป็น Integer และอีกตัวเป็น String โดยมันจะเริ่มเทียบจากตัวแรก ก็คือ 1 ของทั้ง 2 ด้าน มันเท่ากัน เลยผ่านไป ที่ตัวต่อไป คือ a และ b ซึ่งแน่นอนว่า b มาทีหลัง ทำให้ใน List นี้ฝั่งขวาเลยมากกว่าฝั่งซ้าย ทำให้มันเป็นจริงนั่นเอง

>>> [1, 3] < [1, 'b']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'

ไหน ๆ เราก็พูดถึงการเปรียบเทียบกับ List ที่มีหลาย ๆ DataType หนึ่งในอีกเคสที่เราเจอได้คือ เมื่อ DataType มันไม่เหมือนกัน อย่างที่บอกว่า 1 ของทั้ง 2 ฝั่ง ไม่ใช่ปัญหาเพราะมันเท่า แต่ปัญหามันจะไปอยู่ที่ 3 ของฝั่งซ้าย และ b ของฝั่งขวา เมื่อมันเปรียบเทียบกัน ตัว Python มันเทียบไม่ได้ ทำให้เจอ Error อย่างที่เห็นด้านบน

ดังนั้น ถ้าเราต้องการที่จะเปรียบเทียบ List หรือ Tuple ที่สมาชิกสามารถมีได้มากกว่าหนึ่ง DataType เราอาจจะต้องมีการระวังในเรื่องนี้ด้วย ไม่งั้น เราจะมีโอกาสเจอ Error แบบด้านบนได้ แนะนำว่าให้แปลงให้เป็นสัก DataType นึงที่เหมาะกับข้อมูลของเรามากที่สุดจะได้ไม่แตก

สรุป

การใช้ Comparison Operator กับพวก Iterable ต่าง ๆ ก็ถือว่าเป็น Shorthand ที่ทำให้เวลาเราเขียน Script ออกมา มันสั้นลงได้ ก็ตามหน้าที่ของ Shorthard แหละ นอกจากนั้นมันยังลดความซับซ้อนในการเขียน โดยเฉพาะใน Python เองที่เราสามารถทำ Comparison Operation Chaining หรือก็คือ การเทียบไปเรื่อย ๆ เช่น 1 > 2 > 3 ได้ อย่างใน Set เองถ้าเราเจอพวก Chaining แล้วหาพวก subset เราก็ต้องพ่วง issubset() ไปเรื่อย ๆ มันก็ไม่น่าอ่านเท่าไหร่ อ่าน ๆ ไป งง อ้าว อันไหนเริ่มก่อนหลังอะไรยังไง ดูไม่น่ารักเลย พวกนี้การใช้ Comparison Operator มันช่วยมาก ๆ แต่สำหรับในกรณีทั่ว ๆ ไป เรามองว่า ใช้เป็นพวก issubset() อะไรพวกนั้นเหมือนเดิม เข้าใจง่ายกว่าเยอะ แต่สำหรับ List และ Tuple เอง การใช้พวกนี้ เราว่ามันทำให้เราทำงานง่ายขึ้นมากจริง ๆ ก็ลองเอาไปใช้กันได้ ถือว่าเป็นทริกเล็ก ๆ น้อยใน Python