9/3(2+1) = ?
เวลามีคนถามว่า
9/3(2+1) = ?
จะตอบโดยไม่ลังเลเลยว่า 1 แถมถ้าใครมาบอกว่าต้องคิดหารทางด้านซ้ายมือ แล้วค่อยมาคูณแบบคอมพิวเตอร์นะ จะตอบกลับไปเลยว่ามัน syntax error โว้ย ภาษาคอมพิวเตอร์ที่ไหนเค้ายอมรับการเขียนเลขติดกันว่าเป็นการคูณเลขบ้าง?
อนึ่ง ถ้ามองว่าการเขียนเลขติดกัน (คั่นด้วยวงเล็บ) เป็นการคูณจริงๆ ตัวที่ติดกับวงเล็บก็ต้องมี precedence สูงกว่า (ทำงานก่อน) เครื่องหมายหารอยู่แล้ว นั่นหมายความว่าต้องคิดส่วน 3(2+1)
ให้เสร็จก่อนเอาไปคำนวณต่ออยู่ดี
เจอบ่อยๆ ก็เริ่มขี้เกียจอธิบาย เลยเขียนโปรแกรมที่มันแปลสมการด้านบนนี้ได้ถูกต้องเลยละกัน -> จิ้มโลด
ตัวอย่างผลลัพท์จากโปรแกรม
>>> 9/3(2+1)
1.0
>>> a, b, c, d = 48, 2, 9, 3
>>> a/b(c+d)
2.0
>>> 10(9(8(7(6(5(4(3(2(1)))))))))
3628800
>>> (1+2j)(3-4j)
(11+2j)
>>> 1 + 1/phi
1.618033988749895
>>> phi(43)
42
อันที่จริงก็คิดว่าจะทำมาได้ซักพักละ แต่ด้วยความที่ขี้เกียจวางกฎ grammar ด้วย flex bison ทั้งที่หลายๆ กฎนั้นภาษาส่วนใหญ่ก็ทำไว้ให้แล้ว เลยดองมาเรื่อยๆ จนทำ Infinite List เสร็จ และไปเจอ Python’s Magic Methods เลยได้ฤกษ์เริ่มจากตรงนั้น
แนวคิดก็ง่ายนิดเดียว แค่เพิ่มเมธอด __call__
เข้าไปให้ class ของตัวเลข (เป็น Monkey Patching เหมือนใน Ruby) โดย define มันกว่าถ้าเรียก a.__call__(b)
แล้วจะมีค่าเท่ากับเอามันมาคูณกันซะ
ปัญหาที่ต้องไล่เก็บก็คือ
- Python ไม่อนุญาติให้แก้ไขพวก
builtins
– ที่จริงปัญหานี้ก็เหมือนกับคราวที่ทำ Infinite List นั่นแหละ ซึ่งแก้ไม่ยาก แค่ inherit class นั้นๆ ออกมาแล้วไล่ define magic method ซะ - ปัญหาจริงๆ คือ Python parser มันจะแปลตัวเลขไปเป็น class ตัวเลขใน
builtins
เท่านั้น ดังนั้นก็ต้องแปลง token เองโดยหาว่าตรงไหนคือเลข แล้วก็เอา class ตัวเลขที่ inherit มาแล้วครอบไว้ - งานนี้ได้โมดูล tokenize มาช่วยแปลตัวเลขให้ ไม่ต้องเขียน regex เอง เพราะมันจะมีท่าประกาศตัวเลขประหลาดๆ ทำให้ tokenize เองพลาดได้
- ที่เหลือก็เอา code ที่แก้ token เรียบร้อยไปทำงาน ตรงนี้ดึงโมดูล
code
ที่ทำทุกอย่างเตรียมไว้แล้วมาใช้ฟังก์ชันเดียวจบ - แต่ตอนที่เอา
tokenize
มาใช้ร่วมกับcode
จะมีปัญหาที่พิมพ์ได้ทีละบรรทัด เนื่องจากtokenize
มันปรับแต่งอะไรไม่ได้เลย ก็ต้องหลอกมันเอานิดหน่อย - ครบถ้วนกระบวนความแล้วก็ clean up โดยจุดที่เอา code ออกไปได้เยอะมากๆ คือส่วน define magic method อันนี้ใช้ meta-programming บอกแค่ว่าจะเพิ่ม method ไหนบ้างก็พอ แล้วปล่อยให้มัน generate ตัวเองไปซะ
- อยู่ๆ ก็เขียน function decorator เป็นเฉยเลย งงตัวเอง 555+
อย่างไรก็ตาม แนวคิดของ callable number นี้ไม่ควรเอาไปใช้เขียนโปรแกรมในโลกจริง เพราะนักคณิตศาสตร์ชอบตังชื่อเดียว แต่ใช้ในคนละบริบท เช่น $\phi$ ที่อาจหมายถึง golden radio หรือ Euler’s totient ก็ได้ ซึ่งแม้ว่าทั้ง 2 รูปของ $\phi$ นี้จะไม่ overlap กัน (เช่น $1+\frac{1}{\phi}$ จะถูกมองว่าเป็นตัวเลข ในขณะที่ $\phi(43)$ คือการใช้ฟังก์ชัน) แต่ด้วยความที่ Python เป็น 1st class function ที่ยอมให้เราส่งผ่านฟังก์ชันเป็นตัวแปรของฟังก์ชันอื่นๆ ได้ ดังนั้นถ้าเราเจออะไรอย่าง $\delta(\phi,\epsilon)$ เราจะไม่สามารถแน่ใจได้เลยว่า $\phi$ ในบริบทนี้คืออะไรครับ
author