April 11, 2012

Python: จัดการตัวแปรจาก CLI ด้วย argparse

การสร้าง CLI สำหรับ script ที่รับตัวแปรตอนเรียกโปรแกรมนั้น แม้จะใช้ sys.argv เพื่อดึงส่วน argument ได้ แต่เมื่อต้องทำงานที่ซับซ้อนแล้ว module ที่จะช่วยให้งานนี้เป็นระเบียบเรียบร้อยคือ argparse

ตัวอย่างเช่น โปรแกรม sq.py ที่จะทำการยกกำลังสองเลขที่รับเข้ามา

เมื่อเรียกโปรแกรมนี้ผ่านทาง CLI จะได้ผลลัพท์ดังนี้

สังเกตกว่า num มีประเภทตัวแปรเป็น string ก่อนที่จะนำไปใช้คำนวณต้องแปลงค่าให้เป็น int ก่อน ซึ่งเราสามารถบอกประเภทให้ตัว parser แต่แรกเลยก็ได้ โดยแก้ไขบรรทัดที่ 4 เป็น



จากตัวอย่างที่แล้ว ตัวแปร num คือตัวแปรแบบ positional argument ซึ่งหมายความว่า ต้องป้อนตัวแปรเหล่านี้ให้ครบ

ลองดูโปรแกรม acal.py ที่รับตัวเลข 3 ตัว แล้วหาผลรวม

นอกจากกำหนดจำนวน argument ด้วยตัวเลขแล้ว ยังสามารถใช้ wildcard ?, +, * เพื่อบอกจำนวนก็ย่อมได้ เช่นปรับปรุงโปรแกรมข้างต้นให้รับ argument ได้ไม่จำกัดโดยเปลี่ยนบรรทัดที่ 4 เป็น



สำหรับตัวแปรอีกประเภทคือ optional argument เช่น -h โดยมีวิธีเขียนที่แตกต่างออกไปเล็กน้อย

คราวนี้มาปรับปรุง acal.py ให้รับ optional argument เพื่อให้สามารถเลือก max หรือ min แทนการ sum ได้

และยังมี optional argument แบบที่สามารถรับ argument ต่อท้ายอีกได้ด้วย ลองดูตัวอย่างการเปิดไฟล์สำหรับเขียนโดยเพิ่มคำสั่งนี้เข้าไปหลังบรรทัดที่ 8

เนื่องจากการเปิดไฟล์ด้วย open เพื่อเขียนต้องบอก option 'w' ด้วย แต่เพราะ type รับตัวแปรเดียว ตรงนี้ให้ใช้ argparse.FileType('w') แทน (หรือจะใชประยุกต์ใช้ lambda f: open(f, 'w') ก็ย่อมได้)

จุดสังเกตอีกอย่างที่เพิ่มขึ้นมาคือ metavar ซึ่งเป็นการบ่งว่า optional argument นี้รับตัวแปรเพิ่มได้ แต่ถ้าเอาไปใส่ให้กับ positional argument จะกลายเป็นแค่การบอกชื่อย่อเท่านั้น

หลังจากเปิดไฟล์กันได้แล้ว ถึงตอนนี้ก็ต้องจัดการกับการแสดงผลที่บรรทัด 11 ใหม่หมด



คราวนี้ก็ลองเขียน help เต็มรูปแบบให้กับ acal.py กันดูครับ

April 3, 2012

PHP: แทรก/ดึงข้อมูลออกจากอาร์เรย์ที่เรียงลำดับ

ใน PHP ตัวแปรชุดแบบอาร์เรย์นั้นจะถูก implement ด้วย hash table ทั้งหมด นี่อาจเป็นข้อดีในแง่ความสะดวก แต่ถ้าหากเราลองทำอะไรอย่างนี้
จะพบว่าตัวที่ถูก unset ไปนั้น กลายเป็นรูโหว่ๆ อยู่กลางอาร์เรย์ โดยที่ index ของ element ใน array ไม่เลื่อนลงมาถมช่องโหว่ให้เต็ม ทางแก้นอกจากเลี่ยงไปใช้การวนแบบ foreach แทนแล้ว ในสถานการณ์ที่เลข index มีความสำคัญ (ต้องเลื่อนไปถมให้เต็ม) ก็มี array_splice สำหรับงานนี้ครับ


สำหรับ argument ที่ให้กับ array_splice จะมีอยู่ทั้งหมด 4 ตัว โดยบังคับใส่อย่างน้อย 2 ตัวได้แก่อาร์เรย์ที่จะเปลี่ยนแปลง และตำแหน่งเริ่มต้นที่จะลบตัวแปรในอาร์เรย์ทิ้ง

argument ตัวที่ 3 จะเป็นการบอกจำนวนของตัวแปรในอาร์เรย์ที่จะลบทิ้งตั้งแต่จุดเริ่มต้นที่ได้บอกไว้ (ถ้าไม่ใส่ก็คือลบทั้งหมด) ซึ่งถ้าตัวเลขนี้เป็นลบ มันจะเป็นการบอกจำนวนของตัวแปรที่เหลือจากการลบนี้แทน

ส่วน argument ตัวสุดท้ายคืออาร์เรย์ที่จะนำเข้ามาแทรกลงในตำแหน่งที่ลบไป และเนื่องจากเราไม่สามารถข้าม argument ตัวที่ 3 ได้ ดังนั้นถ้าต้องการลบอาร์เรย์ต้นฉบับทิ้งจนสุดแล้วค่อยต่อท้ายด้วยอาร์เรย์ใหม่ ให้ใส่ argument ตัวที่ 3 เป็น count เพื่อให้มั่นใจว่าลบตัวแปรทิ้งจนหมดก็ได้

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

March 26, 2012

Python: เรียงลำดับข้อมูล

การเรียงลำดับข้อมูลใน Python นั้น มีเครื่องมือที่ช่วยอำนวยความสะดวกคือ sorted และ list.sort

ความแตกต่างของสองตัวนี้ คือ sorted จะมองแบบ functional ได้ของใหม่ออกมา (โดยที่ของเก่าไม่เปลี่ยนค่า) ส่วน list.sort จะมองแบบ OOP คือเข้าไปเปลี่ยนแปลงการเรียงลำดับในตัว list object

หรือถ้าจะเรียงจากมากไปน้อย

สิ่งที่ต้องระวังคือ สมาชิกแต่ละตัวใน list ต้องอยู่ในรูปแบบของข้อมูลที่สามารถนำมาเปรียบเทียบมากกว่า-น้อยกว่าได้ (อย่าง [1, 2, 'a'] หมดสิทธิ์)



ตัวเลือกอีกตัวคือ key ที่ใช้ในการเปรียบเทียบ ซึ่งทำได้โดยส่งผ่านฟังก์ชันที่รับค่า 1 ตัวแปรเข้าไปเป็น key



ข้อมูลทางเทคนิคคือการเรียงลำดับจะใช้อัลกอริทึม Timsort ซึ่งมีประสิทธิภาพที่ O(n log n) และเป็นแบบ stable (สมาชิกหลังการจัดเรียงแล้วยังคงลำดับเดิมอยู่) ครับ

March 24, 2012

PHP: วัน และเวลา

ฟังชั่นที่แสดงวัน และเวลาออกมาใน PHP จะใช้ date()
string date ( string $format[, int $timestamp = time() ] )
$format เป็นตัวกำหนดให้แสดงวัน และ/หรือเวลาออกมาในรูปแบบต่าง ๆ สามารถรายละเอียดของตัวอักษรที่ใช้แสดงผลได้จาก function.date นอกจากจะเขียนรูปแบบเองแล้ว PHP ยังเตรียมรูปแบบที่ใช้บ่อย (ในงานเบื้องหลัง) ไว้ให้บางส่วน
  • DATE_RSS: รูปแบบที่ใช้สำหรับ RSS (Sat, 24 Mar 2012 12:23:34 +0700)
  • DATE_COOKIE: รูปแบบที่ใช้สำหรับ cookie (Saturday, 24-Mar-12 12:23:34 ICT)
  • DATE_W3C: รูปแบบตาม W3C (2012-03-24T12:23:34+07:00)
ส่วนที่เหลือดูจาก class.datetime $timestamp ใช้สร้างวันเวลาขึ้นมาแสดงผล ผ่านฟังชั่น mktime() และ strtotime()
int mktime (
     [  int $hour = date("H")
     [, int $minute = date("i")
     [, int $second = date("s")
     [, int $month = date("n")
     [, int $day = date("j")
     [, int $year = date("Y") ]]]]]] )

int strtotime ( string $time [, int $now = time() ] )
ผลของทั้ง 2 ฟังชั่นเหมือนกันคือได้ unix timestamp เหมือนกัน แต่ mktime() จะรับค่าเป็นตัวเลขระบุเจาะจง ส่วน strtotime() จะรับค่าเป็นข้อความได้

สำหรับ $now ใน strtotime() คือ เวลาที่เริ่มต้นคำนวณ หากระบุค่าของ $time เป็นข้อความสัมพันธ์กับการเพิ่มลดเวลา แม้ว่า strtotime() จะสามารถพิมพ์เป็นภาษาที่มนุษย์เข้าใจได้ทันที แต่ว่าให้ระวังเรื่อง วัน-เดือน-ปี ที่เป็นตัวเลขจำนวนน้อย ๆ เช่น 10-11-12 เพราะบอกยากว่าแท้ที่จริงแล้วเป็นวันที่เท่าไหร่กันแน่ ทาง PHP จะทึกทักเอาเองว่า ถ้าคั่นด้วย / จะเป็นแบบอเมริกัน (m/d/y) แต่ถ้าคั่นด้วย - หรือ . จะเป็นแบบยุโรป (d-m-y) ทางออกที่ดีคือใช้มาตรฐาน ISO 8601 (YYYY-MM-DD) แทน

ฟังชั่นอื่น ๆ ที่เกี่ยวกับ วัน และเวลา ยังมีอีกมากสามารถอ่านเพิ่มได้จาก ref.datetime

March 21, 2012

PHP: รับข้อมูลจาก form

ในครั้งก่อน ๆ จะเป็นการทำงานเฉพาะส่วนของ PHP ไม่มีการรับค่าจากผู้ใช้ ส่วนอันนี้จะพูดถึงการรับค่าจากผู้ใช้ผ่าน <form> ของ HTML กันบ้าง

การส่งค่าจาก <form> จะส่งมายังเซิฟเวอร์ได้ 2 แบบคือ
  • get: ข้อมูลจะถูกส่งผ่าน URL ทำให้มองเห็นได้ทุกคน และจำกัดปริมาณการส่ง
  • post: ข้อมูลที่ส่งจะมองไม่เห็น (ง่าย ๆ) และ (เกือบ) ไม่จำกัดปริมาณการส่ง (ขึ้นอยู่กับค่า post_max_size ในไฟล์ php.ini)
จะส่งค่าแบบใดนั้น ขึ้นอยู่กับข้อมูลที่จะส่ง ถ้าไม่เป็นความลับ หรือต้องการให้มีการคัดลอก URL แล้วเอาไปใช้ได้โดยไม่จำเป็นต้องกรอกข้อมูลอีกก็ใช้ get ไป แต่ถ้าข้อมูลนั้นเป็นความลับ หรือมีปริมาณมาก ๆ ก็ส่งผ่าน post โดยกำหนดค่า method ส่วน action คือไฟล์ .php ที่จะให้ประมวลผลข้อมูลจาก <form> อันนี้ ส่วนการนำค่ามาใช้งานนั้นจะใช้ผ่าน $_GET สำหรับ get และใช้ $_POST สำหรับ post ซึ่งเป็น associative array มี key เป็น name ของ <input> อันนั้น ๆ

ทั้ง $_GET และ $_POST ถือเป็นตัวแปรแบบ superglobal ที่เรียกใช้ได้ทุกที่ตลอดเวลา แตกต่างจากตัวแปรแบบ global ตรงที่ไม่ต้องใส่ global นำก่อนจะใช้งาน นอกจากนี้ยังมี $_REQUEST ที่เก็บค่าทั้งของ $_POST, $_GET และ $_COOKIE ไว้ด้วยกัน แต่เสี่ยงที่จะถูกโจมตี จึงไม่แนะนำให้ใช้

และสิ่งสำคัญอีกอย่างคือ ควรจะตรวจสอบความถูกต้อง (validation) ของค่าที่ได้จากผู้ใช้ และ/หรือแปลงค่า (cast) ให้ถูกต้องก่อนจะนำมาใช้งานเสมอ จะเต็มรูปแบบหรือคร่าว ๆ ก็แล้วแต่ความเหมาะสม และหากใช้ค่าที่รับมากับฐานข้อมูล หรือส่งต่อให้ระบบอื่น ๆ ควรจะเรียกใช้ฟังชั่น string escape ที่เกี่ยวข้องเสียก่อนทุกครั้ง ซึ่งจะป้องกันการโจมตีได้ระดับหนึ่ง

March 16, 2012

Python: ใช้ทศนิยมแบบ decimal และ fractions

การจัดการเลขทศนิยมในคอมพิวเตอร์ส่วนมาก จะใช้ floating point แบบ IEEE 754 ทำให้ความแม่นยำในหลักท้ายๆ หายไปเสมอ เช่น



ทางแก้นอกจากจะปัดเศษตามข้างต้นแล้ว ยังมี decimal เมื่อต้องการค่าที่แม่นยำครับ

ค่าปริยายของตำแหน่งทศนิยมอยู่ที่ 28 หลัก คือ

จะเห็นว่าเมื่อนำมาคำนวณ ความแม่นยำจะถอยกลับมาที่ 28 ตำแหน่ง ถ้าต้องการความละเอียดมากกว่านั้น

และการเปลี่ยนวิธีการปัดค่าตัวท้าย



ส่วน fractions เป็นการเก็บเศษส่วนที่เขียนได้ในรูปของจำนวนเต็มหารกัน (จำนวนตรรกยะ) วิธีใช้คือ

แม้ว่า fractions จะไม่สูญเสียความแม่นยำเลย แต่มันก็ไม่สามารถเก็บจำนวนอตรรกยะ (อย่างเช่น sqrt(2)) ก็เลือกใช้เครื่องมือเหล่านี้ให้ถูกงานนะครับ

March 14, 2012

PHP: Function

ฟังชั่น (function) จะแบ่งเป็น 2 ประเภทคือ
  • ฟังชั่นที่มากับตัวภาษา (built-in function) เช่น abs(), max(), min()
  • ฟังชั่นที่สร้างขึ้นเอง
ในที่นี้จะพูดถึงการสร้างฟังชั่นขึ้นมาใช้เอง ส่วน built-in function ดูได้จาก W3School (แต่อาจจะนำมาเขียนถึงในบางฟังชั่น)

การสร้างฟังชั่น จะมีรูปแบบประมาณนี้ ตัวอย่างการสั่งพิมพ์ข้อความโดยไม่มีการส่งค่า และคืนค่า สำหรับการส่งค่าไปยังฟังชั่น สามารถสั่งได้ 2 แบบคือ
  • By Value เป็นส่งค่าของตัวแปรไปให้ ซึ่งค่าที่ถูกแก้ไขในฟังชั่นจะไม่ทำให้ค่าของตัวแปรที่ส่งเปลี่ยนแปลงไป
  • By Reference เป็นส่งตำแหน่งอ้างอิงของตัวแปรไปให้ ซึ่งทำให้ค่าของตัวแปรที่ส่งเปลี่ยนแปลงไป การส่งค่าแบบ by reference จะใช้ & นำหน้าชื่อตัวแปร
นอกจากนี้ยังกำหนดค่าโดยปริยาย (default) ให้กับพารามิเตอร์ของฟังชั่นได้โดยใช้ = ส่วนมากมักจะใส่ตัวแปรที่มีค่าโดยปริยายไว้หลังสุด การคืนค่าจากฟังชั่น ใช้ return return จะถือว่าเป็นการจบการทำงานของฟังชั่นนั้น เมื่อเจอ return โค้ดที่อยู่หลัง return จะไม่ถูกเรียกใช้งาน จึงสามารถนำไปประยุกต์ให้หยุดการทำงานของฟังชั่นเมื่อพบข้อผิดพลาดได้ ใช้ฟังชั่นเมื่อไหร่? ใช้เมื่อรู้สึกว่า จะต้องเขียนชุดคำสั่งเดิม ๆ มากกว่า 1 ครั้ง จำไว้ว่า โปรแกรมเมอร์นั้นขี้เกียจ เราจะไม่เขียนชุดคำสั่งเดิม ๆ ซ้ำเป็นครั้งที่ 2 (ที่จริง เพื่อให้แก้ไขจัดการง่าย ถ้าหากโค้ดมีบั๊ก แก้ที่เดียวจบ)