คิดแบบภาษาที่ใช้ Array
(github.com/razetime)- การเขียนโปรแกรม K เน้นย้ายโค้ดที่ทดลองใน REPL ไปเป็นสคริปต์ พร้อมค่อย ๆ ย่อแพตเทิร์นเชิงคำสั่งขนาดใหญ่ให้เป็น แพตเทิร์นแบบประกาศเชิงอาร์เรย์ ที่เล็กลง
- สคริปต์
ngn/kทำงานทีละบรรทัดเหมือนอินพุตใน REPL และสามารถ โหลดเข้าสู่ REPL ข้อมูลและฟังก์ชันที่บันทึกไว้ได้ด้วย\\l file.k - หากย้ายโค้ดคูณเมทริกซ์แบบลูป 3 ชั้นจาก Wikipedia มาตรง ๆ จะมีตัวแปร global, ลูปซ้อน, และการแก้ไขค่าจำนวนมาก ซึ่งขัดกับจุดเด่นของ K
- กระบวนการปรับปรุงค่อย ๆ ผ่าน
+/fold,'each,/:eachright,\:eachleft, การตัด transpose ออก, และการแปลงเป็น tacit จนย่อจากmatmul: {x{+/x*y}\:y}เหลือmatmul: (+/*)\: - ตัวอย่างการคูณเมทริกซ์แสดงให้เห็นว่าความชำนาญใน K อยู่ที่การทำ กระบวนการย่อโค้ด ซ้ำ ๆ เพื่อเปลี่ยนขั้นตอนที่ซับซ้อนให้เป็นนิพจน์แบบอาร์เรย์ที่อ่านง่ายขึ้น
เวิร์กโฟลว์การพัฒนา K ที่มี REPL เป็นศูนย์กลาง
- ซอร์สโค้ดทั้งหมดดูได้ที่
matmul.kบน GitHub - การเขียนโปรแกรม K ส่วนใหญ่เกิดขึ้นใน REPL ซึ่งเหมาะกับการทดลองและปรับปรุงอย่างรวดเร็วบนโค้ดเดิม
- การใช้
ngn/kร่วมกับrlfeรองรับประวัติด้วยปุ่มลูกศรขึ้น/ลง จึงเพียงพอสำหรับการพัฒนาโปรแกรม K ที่ใหญ่ขึ้น - แนวทางที่เป็นธรรมชาติคือทดสอบฟังก์ชันใน REPL ก่อน แล้วค่อยย้ายไปเป็นโค้ดจริง
- prettyprinting ของ
ngn/kจะคืนข้อมูล K ที่ถูกต้องเสมอ จึงสามารถคำนวณค่าบางส่วนล่วงหน้าเพื่อเพิ่มความเร็วให้โปรแกรมได้
โมเดลการรันสคริปต์ K
- สคริปต์ K จะทำงานเหมือนกับการพิมพ์คำสั่งใน REPL
- แต่ละบรรทัดทำงานตามลำดับ
- ถ้าบรรทัดไม่ลงท้ายด้วยเซมิโคลอน ระบบจะพิมพ์ค่าที่คืนกลับ
- สคริปต์รองรับ นิยามหลายบรรทัด เพื่อให้อ่านง่ายขึ้น
- หากต้องการใช้ข้อมูลและฟังก์ชันที่บันทึกไว้จากใน REPL ให้รัน
\\l file.k- ไฟล์จะถูกประมวลผล
- ข้อมูลในไฟล์จะถูกโหลด
- หากโหลดไฟล์เดิมหลายครั้ง ข้อมูลก่อนหน้าจะถูกเขียนทับ
- สามารถดูคำสั่งเพิ่มเติมได้จากคู่มือช่วยเหลือใน REPL ที่เข้าถึงด้วย
\\
วิธีลดรูปแพตเทิร์นในภาษาอาร์เรย์
- K และการเขียนโปรแกรมแบบอาร์เรย์คือกระบวนการของการ ทำให้แพตเทิร์นเรียบง่ายลง อย่างต่อเนื่อง
- แม้แพตเทิร์นจะใหญ่และจัดการยาก ก็มักมีอย่างน้อยหนึ่งวิธีที่จะย่อให้เล็กลง เป็นแบบประกาศมากขึ้น และอ่านง่ายขึ้น
- ดูการอภิปรายที่เกี่ยวข้องได้ใน Patterns and Anti-patterns in APL: Escaping the Beginner's Plateau - Aaron Hsu - Dyalog '17
- จุดเริ่มต้นที่พบบ่อยคือการพยายามแปลอัลกอริทึมที่คุ้นเคยจาก GeeksforGeeks หรือ Wikipedia มาเป็น K
- ตัวอย่างนี้ใช้ การคูณเมทริกซ์
เมื่อย้ายการคูณเมทริกซ์แบบ imperative มาตรง ๆ
- Matrix multiplication algorithm ของ Wikipedia เติมค่าให้เมทริกซ์
Cด้วยลูป 3 ชั้นi,j,kและการสะสมค่าsum - หากแปลมาตรง ๆ เป็น K จะต้องกำหนดค่าระดับ global จำนวนมาก เช่น
A,B,n,m,p,C,i,j,k,sum - โค้ดลักษณะนี้ใช้ K ราวกับเป็นภาษาเชิงคำสั่ง ซึ่งไม่ค่อยสอดคล้องกับการออกแบบของ K
- ปัญหาสามารถสรุปได้ 3 ข้อ
- มี การกำหนดค่าระดับ global มากเกินไป
- ยังเหลือลูปซ้อนหลายชั้น
- มีการแก้ไขค่าเกิดขึ้นบ่อย
เริ่มย่อจากลูปชั้นในสุด
- ลูปชั้นในสุดเริ่มจากตั้ง
sumเป็น 0 แล้ววนkเพื่อสะสมA[i;k]*B[k;j] - การปรับปรุงแรกคือใช้
/ซึ่งเป็น fold เพื่อเปลี่ยนการบวกสะสมให้เป็น+/- ตัวแปร global
sumหายไป - โค้ดถูกจัดให้อยู่ในรูป
C[i;j]::+/...
- ตัวแปร global
- จากนั้นใช้ข้อเท็จจริงที่ว่า
'each คืนค่าเป็นอาร์เรย์ ทำให้สามารถใช้ค่าที่ลูปซ้อนคืนกลับมาได้โดยตรงโดยไม่ต้องแก้ไขC - หลังขั้นตอนนี้จะเหลือเพียงลูป 3 ชั้นที่ไม่มีการแก้ไขค่า และตัวแปรหลักคือ
i,j,k
กระบวนการลบ k, j, i
- บทบาทของตัวแปรทั้งสามมีดังนี้
iใช้ทำดัชนีแต่ละแถวของAjใช้ทำดัชนีแต่ละคอลัมน์ของBkใช้ทำดัชนีแต่ละคอลัมน์ของAและแต่ละแถวของB
kทำหน้าที่จับคู่แต่ละแถวของAกับแต่ละคอลัมน์ของBเพื่อคูณกัน ดังนั้นจึงสามารถตัดดัชนีกลางออกและจับคู่กันโดยตรงได้- ขั้นตอนนี้ทำให้ลูปหายไปหนึ่งชั้นและไม่ต้องใช้
mอีกต่อไป
- ขั้นตอนนี้ทำให้ลูปหายไปหนึ่งชั้นและไม่ต้องใช้
- หากต้องการลบ
jต้องดึงแต่ละคอลัมน์ของBออกมาแล้วจับคู่กับA[i]- ทำได้ด้วยการ transpose
Bแล้วใช้ eachright/:เพื่อจับคู่แต่ละองค์ประกอบ
- ทำได้ด้วยการ transpose
iก็ลบออกได้ในลักษณะเดียวกัน- ใช้ eachleft
\:เพื่อจับคู่แต่ละแถวของAกับแต่ละคอลัมน์ของB
- ใช้ eachleft
- เมื่อทำถึงจุดนี้จะได้รูปแบบต่อไปนี้โดยไม่มีตัวแปร global
matmul: {x{+/x*y}\:y}
ตัด transpose ออกและไปสู่รูป tacit สุดท้าย
- การ transpose ด้วย
+มีต้นทุนสูง จึงควรตัดออก - วิธีเดิมคือ วิธีตรงไปตรงมา ที่นำแต่ละแถวของ
xไปคูณกับแต่ละคอลัมน์ของy - แต่ถ้านำแต่ละแถวของ
BไปจับกับAทั้งหมดแทน ก็จะทำงานเดียวกันได้โดยปริยาย
matmul: {x{+/x*y}\:y}
- ฟังก์ชันนี้สามารถแปลงเป็นรูป tacit ได้โดยใช้กฎจาก Chapter 3
- ผลลัพธ์สุดท้ายเป็นดังนี้
matmul: (+/*)\:
สร้างสัญชาตญาณของภาษาอาร์เรย์ผ่านการฝึกฝน
matmul: (+/*)\:ถูกสรุปออกมาเป็นฟังก์ชันคูณเมทริกซ์ในแบบฉบับของ K- กระบวนการย่ออาจดูเหมือนมีหลายขั้นตอนในช่วงแรก
- เมื่อฝึก K มากขึ้น การย่อโค้ด จะกลายเป็นงานที่ง่ายและเป็นธรรมชาติมากขึ้น
- การคูณเมทริกซ์เป็นกระบวนการง่าย ๆ ที่เข้ากันได้ดีกับการรองรับอาร์เรย์ของ K
- ในบทถัดไปจะกล่าวถึงอัลกอริทึมที่ไม่ค่อยเข้ากับ K และวิธีจัดการ
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
สิ่งที่แสดงให้เห็นศักยภาพของภาษาแบบอาร์เรย์ได้อย่างน่าโน้มน้าวที่สุดจริง ๆ คือวิดีโอที่ Aaron Hsu อธิบายการพัฒนา Co-dfns ซึ่งเป็น คอมไพเลอร์ APL แบบขนาน: https://www.youtube.com/watch?v=gcUWTa16Jc0&t=860s
เขายังเขียนเรื่อง ความหนาแน่นของความหมาย หลายครั้งบน HN ในนาม arcfide โดยอธิบายว่าโค้ด APL ถูกออกแบบมาให้สามารถมองเห็นวิธีการทำงาน บริบทโดยรอบ และการพึ่งพาต่าง ๆ ได้ภายในหน้าจอเดียวโดยแทบไม่ต้องเลื่อนสายตาไปไหน: https://news.ycombinator.com/item?id=13571159
มุมมองนี้คือ ถ้าทำให้กระชับได้จนชื่ออัลกอริทึมมีความยาวใกล้เคียงกับการเขียนตัวอัลกอริทึมนั้นออกมาตรง ๆ เราก็จะอ่านโค้ดเป็นหน่วยสำนวนคล้ายการอ่านวลีภาษาอังกฤษ และแทนที่จะสร้าง abstraction เพื่อการนำกลับมาใช้ใหม่ การแก้ทุกจุดใช้งานที่มองเห็นอยู่บนหน้าจอโดยตรงอาจเร็วกว่า
ถ้ายังไม่คุ้นกับ array programming ขอแนะนำ The Array Cast เป็นสื่อเริ่มต้น: https://www.arraycast.com/episodes/
RSS คือ https://www.arraycast.com/episodes?format=rss
map/filter/reduce ก็มีแทบทุกที่อยู่แล้ว และเหมือนพวกเขามองข้ามข้อดีที่ว่าเราใช้มันได้โดยไม่ต้องไปเรียนระบบสัญลักษณ์ใหม่ที่คล้ายอักษรภาพ
เคยได้เจอ APL/APL2 ตอนยุค 70s ที่บน paper terminal ยังใช้การพิมพ์ทับกันจริง ๆ แล้วก็ติดใจทันที แต่พอภายหลังได้รู้จักการเขียนโปรแกรมเชิงฟังก์ชันผ่าน ML และ Haskell ก็พบว่าสิ่งที่ชอบจริง ๆ ใน APL ไม่ใช่เรื่องอาร์เรย์ แต่คือความสามารถด้าน การประกอบฟังก์ชัน
Haskell นั้นบริสุทธิ์อย่างสมบูรณ์และมี type ครอบคลุมทั่วทั้งภาษา จึงดีกว่ามากในแง่นี้ และทั้งสนุกและทรงพลังกว่า APL เคยทำโปรเจ็กต์ขนาดเล็กถึงกลางไว้เยอะ รวมถึงทำต้นแบบที่แสดงว่า parser ของ LLVM Flang สามารถเขียนด้วย parser combinator ได้ และทุกปีก็แก้ Advent of Code ทั้งหมดด้วยโค้ดรวมกันเพียงไม่กี่ร้อยบรรทัด ถ้าชอบ APL ก็น่าลอง Haskell เช่นกัน
ตอนนี้มองว่าด้านที่ APL เป็น “สัญกรณ์ในฐานะเครื่องมือช่วยคิด” ดูเหมือนเป็นคำอธิบายเพื่อทำให้ความกระชับเกินพอดูสมเหตุสมผล มันเหมาะกับการแสดงพลังของการประกอบ แต่ก็อาจบั่นทอนความชัดเจนได้
<=<ก็มีอยู่แล้ว และถ้าใช้สิ่งที่เทียบได้กับfmapมันทำงานได้ดีมาก|||,+++,&&&,***ก็ดีเหมือนกัน และยังสร้างโอเปอเรเตอร์ UTF-8 ของตัวเองให้สั้นและสวยขึ้นได้ด้วย เพียงแต่น่าเสียดายที่โค้ด Haskell จริงจังที่ใช้ในงานจริงหรือเผยแพร่สู่สาธารณะมักไม่ค่อยเป็นมิตรกับพื้นที่แนวตั้งของหน้าจอแบบนี้อยากรู้ว่าในภาษาแบบอาร์เรย์ โดยทั่วไปจัดการปัญหาแนว “หาค่าทั้งหมดที่น้อยกว่า N และทำให้เพรดิเคต P เป็นจริง” อย่างไร เช่น การหา prime ที่น้อยกว่า 1000 หรือหา Pythagorean triple ที่มี z น้อยกว่า 1,000,000
ถ้าเป็นภาษาเชิงคำสั่งก็มักจะตรวจเพรดิเคตในลูป ส่วนภาษาเชิงฟังก์ชันก็อาจใช้ recursion หรือลิสต์แบบ lazy กับ map/filter แต่สำหรับภาษาแบบอาร์เรย์ ผมเข้าใจว่าปกติจะสร้างอาร์เรย์
1..Nแล้วนำเพรดิเคตไปใช้เพื่อสร้าง mask array จากนั้นใช้ mask นั้นกรองอาร์เรย์ต้นฉบับถ้า N ใหญ่มากอย่าง 1 พันล้าน และเพรดิเคตแทบไม่เคยเป็นจริงเลย การสร้างทั้ง
1..Nและ mask ซึ่งเป็นอาร์เรย์ชั่วคราวขนาดมหึมาสองชุด ดูเป็นการสิ้นเปลืองหน่วยความจำและทรัพยากรมาก อยากรู้ว่าภาษาแบบอาร์เรย์ช้าลงเพราะต้องสร้างอาร์เรย์ชั่วคราวแบบนี้ตลอดหรือไม่ และการ implement มีการ optimize ด้วยวิธีอย่าง lazy evaluation หรือเปล่าในทางกลับกัน ภาษาแบบสเกลาร์มีค่าเริ่มต้นคือประมวลผลทีละค่าหนึ่งค่า จึงสูญเสียศักยภาพของความขนานที่ภาษาแบบอาร์เรย์ใช้กับอัลกอริทึม SIMD ได้ เรื่องนี้ก็แค่ดูไม่ใช่ปัญหาใหญ่เพราะเราคุ้นกับสภาพปัจจุบันเท่านั้น และทางแก้ก็คือการทำ blocking เช่นกัน
ในทางปฏิบัติ ภาษาแบบอาร์เรย์ดีหรือไม่ก็ขึ้นกับปัญหาเป็นหลัก สำหรับงานใช้งานจริงส่วนใหญ่ ประสิทธิภาพไม่สำคัญเลย และชื่อเสียงของ k ก็ดูจะมาจากการที่ kdb เร็วในฐานะฐานข้อมูล มากกว่าที่ตัวภาษา k จะเป็นภาษาที่รันเร็วในตัวเอง ถึงอย่างนั้น การโฟกัสที่อัลกอริทึมแบบอาร์เรย์ที่สวยงามแทนการจูนระดับรายละเอียดเฉพาะเครื่อง ก็อาจทำให้เร็วขึ้นอย่างน่าทึ่งได้: https://mlochbaum.github.io/BQN/implementation/versusc.html
อีกวิธีที่ตรงไปตรงมาคือทำ loop fusion กับทั้งบอดีเพื่อไม่ให้เกิดอาร์เรย์ชั่วคราวขึ้นมาเลย ทางเลือกที่ง่ายกว่านั้นคือแบ่งอาร์เรย์อินพุต/เอาต์พุตเป็นชังก์ขนาดระดับหลายสิบ KB เพื่อจำกัดการใช้หน่วยความจำชั่วคราวที่ไม่จำเป็น เท่าที่ผมรู้ยังไม่มีภาษาแบบอาร์เรย์ใดทำสิ่งนี้อัตโนมัติ แต่สักวันหนึ่งอยากลองทำใน CBQN ผู้ใช้ทำเองแบบ manual ก็ได้ และถ้าจะรีดประสิทธิภาพสูงสุดก็มักต้องทำบ่อยทีเดียว
!10000000เป็นเพียงช่วงค่า ไม่ได้สร้างอาร์เรย์จำนวนเต็มสิบล้านตัวจริง ๆ ตั้งแต่แรกแน่นอนว่าเมื่อใช้ operator บางตัว สุดท้ายอาร์เรย์แบบนั้นก็อาจถูกสร้างขึ้นมาจริงได้ นอกจากนี้ยังมี optimization แบบเปลี่ยนแพตเทิร์นอย่าง
+|xซึ่งคือกลับลำดับ x แล้วหยิบสมาชิกตัวแรก ให้กลายเป็นเพียงการหยิบสมาชิกตัวสุดท้ายแทนแน่นอนว่าสามารถเขียนเลี่ยงได้ แต่ทางออกแบบนั้นอาจยาวขึ้นและสวยน้อยลง ภาษาถิ่น APL ที่ผมกำลังทำอยู่คือ Kap ซึ่งจะเลื่อนการคำนวณออกไปจนกว่าจะต้องใช้ผลลัพธ์ ทำให้เขียนโค้ดในแบบที่เป็นธรรมชาติได้โดยไม่ต้องคำนวณผลที่ท้ายที่สุดจะถูกทิ้งในหลายกรณี
สิ่งที่ตระหนักได้มากที่สุดจากการใช้ภาษาแบบอาร์เรย์ โดยเฉพาะ k มีดังนี้ กริยาคืออัลกอริทึม และในภาษาเชิงคำสั่ง/เชิงวัตถุ เรามักต้องลงมือ implement อัลกอริทึมร่วมอย่าง find, sort, group ด้วยตัวเอง
การต่อเนื่องกันของกริยาและ副詞เป็นรูปแบบการประกอบที่ตรงไปตรงมาที่สุดเท่าที่ผมเคยใช้มา และการ compose ก็ทำได้ง่ายและเป็นธรรมชาติ โปรแกรมจึงดูเหมือนไม่ใช่ชุดของ statement กับ expression แต่เป็นการประกอบของอัลกอริทึม
การจัดการแนวคิดเรื่องโดเมนและโคโดเมนอย่างสม่ำเสมอในอาร์เรย์ แมป และฟังก์ชัน ทำให้การเลือกออกแบบง่ายขึ้น และเมื่อมีการประเมินจากขวาไปซ้าย เวลาจะอ่านโค้ดสายตาก็ไม่ต้องกระโดดไปมา
แทนที่จะดึงข้อมูลเข้าหาโค้ด ยังสามารถและมักนิยม ส่งโค้ดไปหาข้อมูล ได้ โปรเจกต์ k ขนาดใหญ่ส่วนมากเมื่อตัดคอมเมนต์ออกแล้ว ยังอยู่ภายในขนาด network MTU คือ 1540 ไบต์ได้สบาย โบนัสอีกอย่างของ k คือ view สามารถ implement ความสัมพันธ์เชิงฟังก์ชันได้โดยตรง และการโหลดโค้ดร้อนผ่านอินเทอร์พรีเตอร์ก็ทำให้แอปพลิเคชันที่รัน “ตลอดกาล” เป็นไปได้
จากความประทับใจส่วนตัวที่มีอคติและจำกัดของผม หลังจากลองแก้โจทย์ ภาษา K เพื่อเตรียมสัมภาษณ์งาน ผมรู้สึกว่าภาษานี้จงใจทำให้อ่านยาก มันเป็นภาษาที่เหมาะกับปริศนาและวิธีแก้ที่แยบคาย
แต่ถ้าพูดถึงการสอนเรื่องภาษาแบบอาร์เรย์และการคิดแบบอาร์เรย์ ผมมองว่าประสบการณ์การใช้ NumPy array ใน Python สอนเรื่องนี้ได้
จากประสบการณ์ที่ใช้ J มาราว 50 ชั่วโมง ผมรู้สึกตรง ๆ ว่าพาราไดม์นี้เอนไปข้างหนึ่งมากเกินไป
ผมไม่แน่ใจว่าการพยายามมองทุกปัญหาเป็นการซ้อนกันของอาร์เรย์จะช่วยในฐานะ เครื่องมือสำหรับคิด ได้จริงหรือไม่ ถ้าสามารถสร้างโครงสร้างข้อมูลที่จับปัญหาได้เหมาะเจาะอย่างอิสระ ส่วนอัลกอริทึมก็อาจเรียบง่ายลงมาก
ผมคิดว่าถ้าจะใช้ APL/J/K คุณต้องฉลาดกว่าเดิมมาก เพราะแนวทางที่ในภาษาที่ยืดหยุ่นกว่าสามารถทำได้ทันที มักกลับทำไม่ได้ ต้องแปลงปัญหาก่อน และกระบวนการนั้นอาจต้องใช้ความคิดมากกว่ามาก
ตัวอย่างนี้อิง K แต่ยังมีภาษาแบบอาร์เรย์อีกตัวคือ J: http://jsoftware.com
ใน J คุณเขียน
dot =: +/ . *,P =: 2 3 4,Q =: 1 0 2,P dot Qแล้วจะได้ inner product ของ P และ Q เป็น 10dot←+.×แต่ถ้ารูปแบบที่เขียนขยายออกมาสั้นพอ ๆ กับชื่อที่ตั้งแบบกระชับ ก็ดูไม่มีเหตุผลมากนักที่จะต้องตั้งชื่อให้มัน แถมอาจยังต้องใส่ช่องว่างรอบชื่ออีกด้วยdot = (sum.) . zipWith (*),p = [2, 3, 4],q = [1, 0, 2],p `dot` qก็เขียนได้เหมือนกันในสายตาผม ความต่างมีแค่การใช้ชื่อ
sumกับzipWithและการยกโครงสร้างหรือแปลงโครงสร้างไม่ได้เกิดขึ้นแบบ “เวทมนตร์” เท่านั้นเองdot::{+/x*y}ใช้ในรูปP::[2 3 4],Q::[1 0 2],dot(P;Q)ดูจากตัวอย่างแล้วไม่ค่อยเข้าใจว่ามันมีความหมายหรือประโยชน์อะไร แบบนี้ประสิทธิภาพดีกว่าจริงหรือ?
ไวยากรณ์การคูณเมทริกซ์สั้นกว่าก็จริง แต่ดูเหมือนว่านั่นเป็นเพราะต้องมีบริบทที่ฝังมากับภาษา K จำนวนมากอยู่ในหัวว่ามันทำงานอย่างไร
มันคุ้มที่จะลองเล่นกับภาษาอาร์เรย์จนกว่าจะเข้าใจพาราไดม์นี้ เพราะบ่อยครั้งโค้ดเชิงคำสั่งจะถ่ายทอดออกมาในสไตล์อาร์เรย์ได้ดีกว่า และฟังก์ชันที่ยาวและจุกจิกก็มักถูกทำให้ง่ายลงได้มากด้วยการใช้งานอาร์เรย์ล้วน ๆ หรือใช้ร่วมกับสไตล์อื่น
ลองเทียบ
(+) <$> Just 1 <*> Just 2กับdo x <- Just 1; y <- Just 2; Just (x + y)ใน Haskell; สำหรับความซับซ้อนระดับนี้ ฉันจะเลือกอันแรกเสมออันหลังใช้พื้นที่มากกว่าเลยทำให้รู้สึกเหมือนมีเรื่องที่ซับซ้อนกว่ากำลังเกิดขึ้น
ถ้างานซับซ้อนกว่านี้ ฉันก็ยังอยากแยกมันออกเป็นฟังก์ชันย่อย ๆ เพื่อให้รูปแบบแรกที่ดัดแปลงแล้วยังสมเหตุสมผล มากกว่าจะไปใช้รูปแบบที่สอง นี่คือการแลกเปลี่ยนจากการทำให้ “มือใหม่บางคนอ่านได้เร็ว” ไปเป็นการทำให้ “คนที่เกินระดับมือใหม่อ่านได้”
ถ้าตั้งเป้าเพิ่มประสิทธิภาพให้กับ “มือใหม่บางคนอ่านได้” ฉันคิดว่าจะเจอกับผลตอบแทนที่ลดลงอย่างมาก จึงเลือกตั้งเป้าให้ “คนที่เกินระดับมือใหม่” หรือในบางกรณีก็ “ระดับกลางขึ้นไป” อ่านได้แทน
ไม่ว่าภาษาไหนก็มีทั้งเหตุผลที่จะใช้และเหตุผลที่จะไม่ใช้ แต่ประเด็นสำคัญไม่ใช่แค่การเขียนให้สั้น ความชัดเจนเชิงเปรียบเทียบ หรือความสามารถในการคอมไพล์เป็นโค้ดที่เร็ว สิ่งสำคัญคือโปรแกรมเมอร์ที่มาทีหลังจะสามารถ แก้ไขและบำรุงรักษา โค้ดนั้นเพื่อใช้งานจริงได้หรือไม่
บ่อยเกินไปที่โปรแกรมเมอร์อยากโชว์ฝีมือ leet ของตัวเอง โดยไม่คำนึงถึงคนที่น่าสงสารซึ่งต้องมารับช่วงโค้ดนั้นต่อทีหลัง ในโลกความเป็นจริง โค้ดแบบ leet จำนวนมากสุดท้ายต้องถูกทิ้งหรือเขียนใหม่ทั้งหมด เพื่อให้ได้สิ่งที่พอจะรองรับในระยะยาวได้
ฉันใช้เวลานานกว่าจะเข้าใจเรื่องนี้ และหลังจากนั้นก็พยายามเขียนโค้ดที่สะอาด เรียบง่าย และเข้าใจได้ เพื่อให้คนอื่นดูแลต่อได้ มีหลายครั้งเกินไปที่โค้ดซึ่งเดิมตั้งใจเขียนแล้วทิ้งกลับแข็งตัวกลายเป็นโครงสร้างพื้นฐานหลักขององค์กร และกลายเป็นสิ่งที่คนรุ่นถัดไปไม่อาจทำความเข้าใจได้