December 21, 2012

Haskell: กรองและแปลงค่าสมาชิกใน List

โดยทั่วไปแล้ว List ตั้งต้นมักไม่ค่อยมีประโยชน์ที่จะเอาไปใช้ต่อเท่าไหร่ จนกว่าเราจะเปลี่ยนค่าของสมาชิกบางตัวไปตามเราที่ต้องการ

เราสามารถสร้าง List ใหม่จากของเดิมได้ โดยคำนวณค่าสมาชิกเก่าทุกตัวภายใต้ฟังก์ชันเดียวกัน วิธีการนี้เรียกว่า list comprehension และสามารถทำได้ดังนี้


นอกจากนี้ เรายังสามารถกรองสมาชิกตัวที่ไม่ต้องการทิ้งก่อนเอาไปคำนวณไปได้ เช่น


อย่างไรก็ตาม ถ้าต้องทำงานกับ List อนันต์ (อย่าง [1..]) การกรองสมาชิกด้วยวิธีการข้างต้นนี้ไม่พอ ต้องใช้งานร่วมกับ take, takeWhile เช่นเดิมครับ



นอกจาก list comprehension ยังมีทางเลือกที่ช่วยให้ทำงานได้เช่นเดียวกันคือ map กับ filter ครับ


ข้อแตกต่างหลักๆ คือ list comprehension จะทำงานกับ List หลายๆ อัน (มองในรูป Cartesian product) ได้ง่ายกว่า แต่การใช้ฟังก์ชัน map, filter แยกกัน จะเป็นธรรมชาติกว่าเมื่อทำ filter หลังจาก map เช่น




ตัวอย่างเลื่องชื่อของ Haskell อย่าง quicksort ก็สามารถทำโดยใช้เทคนิค filter ร่วมกับการทำ pattern matching บน List ดังนี้ครับ

December 13, 2012

Haskell: ทำงานกับ List ที่ยาวเป็นอนันต์

การประกาศ List นอกจากจะทำได้ตามวิธีปรกติแล้ว Haskell ยังทำท่านี้ได้อีกด้วย


ค่าที่เพิ่มจะเพิ่มขึ้นครั้งละ 1 หน่วย (ถ้าตัวเลขก็คือ +1 ถ้าเป็นตัวอักษรก็คือตัวอักษรถัดไป) เราสามารถแก้ไขค่าที่จะเพิ่มขึ้นได้โดยบอกสมาชิกตัวถัดไปใน List นี้แทน


สมาชิกตัวสุดท้ายที่บอกขอบเขตบนนั้น อาจอยู่หรือไม่อยู่ใน List ที่สร้างเสร็จก็ได้ แต่จะไม่มีสมาชิกที่มีค่าใหญ่กว่านี้ครับ

ส่วนการประกาศค่าแบบลดลงนั้น ไม่สามารถบอกแค่ [10..1] ได้ ต้องบอกสมาชิกตัวถัดมาด้วย


หมายเหตุว่าให้ระวังการประกาศ List แบบนี้กับเลขทศนิยม เพราะ bug ของตัว Haskell เองทำให้อาจมีสมาชิกที่ใหญ่กว่าขอบเขตบนโผล่มาได้ครับ

อนึ่ง เราสามารถละส่วนขอบเขนบนได้ โดย Haskell จะเอาขอบเขตบนของ type นั้นมาเติมให้เอง เช่น


ปล่อยไว้ซักพัก จะเห็นว่า List ที่สร้างขึ้นนี้วิ่งไปจบที่ตัวอักษรซักตัวเอง และถ้าสงสัยว่าขอบเขตบนนั้นคืออะไร ก็สามารถหาได้โดย


แต่สำหรับข้อมูลบาง type ที่ไม่มีขอบเขตบน (เช่น Integer) เมื่อสั่ง [1..] ตัวเลขจะวิ่งขี้นไปเรื่อยๆ ไม่มีที่สิ้นสุดครับ (เป็น List ที่ยาวเป็นอนันต์)



List ที่ยาวเป็นอนันต์อาจฟังดูไม่มีประโยชน์ เพราะมันคล้ายว่าเราสั่งให้โปรแกรมไปสร้าง List ที่ไม่มีวันเสร็จ (และโปรแกรมก็จะไม่สามารถไปทำงานอย่างอื่นต่อได้) อย่างไรก็ตาม การดึงสมาชิกเพียงบางส่วนจากออกมานั้นเป็นไปได้ใน Haskell เพราะมันจะไม่พยายามสร้าง List นั้นๆ จนเสร็จ แต่จะสร้างเพียงเท่าที่เราขอไปใช้ต่อเท่านั้น

ฟังก์ชันที่ใช้เพื่อขอสมาชิกเพียงไม่กี่ตัวแรกของ List จากทางด้านหน้าคือ take


ส่วน drop นั้นจะใช้เพื่อทิ้งสมาชิกตัวหน้าออกไป


(drop เพียงอย่างเดียวนั้นไม่ค่อยมีประโยชน์กับ List ที่ยาวเป็นอนันต์ครับ)

ฟังก์ชันอีกกลุ่มที่ทำหน้าที่เช่นเดียวกันนี้ เพียงแต่เปลี่ยนจากจำนวนสมาชิกที่ต้องการแน่นอน ไปเป็นการตรวจสอบค่าสมาชิกว่าตรงเงื่อนไขหรือยัง คือ takeWhile และ dropWhile


ย้ำอีกทีว่าฟังก์ชันพวกนี้จะเริ่มทำงานจากด้านหัว และคืนค่าทันทีเมื่อเงื่อนไขครบ เห็นได้จาก dropWhile (/=3) ที่ทิ้งเลข [1,2] เฉพาะทางด้านหัวเท่านั้น



นอกจากจะใช้การประกาศ List ตามด้านบนเพื่อสร้าง List ที่ยาวอนันต์ได้แล้ว ยังสามารถสร้าง List ที่ยาวเป็นอนันต์โดยใช้ฟังก์ชันเหล่านี้

cycle รับตัวแปรเป็น List ที่จะเอามาทำซ้ำเรื่อยๆ


ส่วน repeat จะรับตัวแปรคือสมาชิกตัวเดียวของ List ที่จะเอาไปทำซ้ำๆ แต่ถ้ารู้ขนาดที่แน่นอนอยู่แล้ว สามารถใช้ฟังก์ชัน replicate แทนได้

December 12, 2012

Haskell: เก็บข้อมูลชุดด้วย List

การสร้าง List ใน Haskell โดยพื้นฐานก็จะมีหน้าตาคล้ายภาษาขั้นสูงทั่วไป คือใช้วงเล็บปีกแข็งครอบสมาชิกไว้เช่นนี้


อย่างไรก็ตาม สมาชิกใน List ทุกตัวต้องเป็น type เดียวกันเท่านั้น เช่นเดียวกับ String ที่มีสมาชิกทุกตัวเป็น Char นั่นเอง

การเพิ่มสมาชิกให้ List นอกจากจะทำผ่าน ++ ได้แล้ว ยังสามารถทำผ่าน : ได้อีกด้วย โดยฝั่งซ้ายของ : คือสมาชิกที่จะเพิ่ม และฝั่งขวาคือ List ตั้งต้น ผลลัพท์จะเป็น List ตั้งต้นที่มีสามาชิกใหม่ด้านหัว


List ใน Haskell นั้น แท้จริงแล้วถูกออกแบบมาเป็น stack โดยด้านที่รับ-ส่งสมาชิกคือด้านหัว (ฝั่งซ้ายมือ) ดังนั้นเรามักจะเพิ่มสมาชิกตัวใหม่ๆ โดย : มากกว่าที่จะใช้ ++ เพิ่มสมาชิกต่อหลังครับ (มีผลต่อความเร็ว)



เมื่อต้องการดึงหัว List ออกมาใช้ ทำได้โดยฟังก์ชัน head หรือเก็บสมาชิกทุกตัวยกเว้นหัวก็ใช้ tail


แม้เราจะไม่ค่อยยุ่งกับสมาชิกตัวท้ายสุดซักเท่าไหร่ แต่ถ้าต้องการสมาชิกตัวสุดท้าย หรือต้องการสมาชิกทุกตัวยกเว้นท้ายสุด ก็สามาถทำได้ผ่าน last, init ตามลำดับ


ข้อควรระวังคือการดำเนินการพวกนี้ ไม่สามารถทำได้กับ List ที่ไม่มีสมาชิกเลย ซึ่งเราสามารถตรวจว่ามีสมาชิกหรือไม่ได้โดยคำสั่ง null


ส่วนการดึงสมาชิก ณ ตำแหน่งใดๆ ออกมา ทำได้ผ่านเครื่องหมาย !! เช่นนี้ครับ


แน่นอนว่ามันจะให้ error ถ้าส่วน index นั้นใหญ่เกินกว่า List เราสามารถหาขนาดได้โดยฟังก์ชัน length


ส่วนถ้าจะตรวจสอบว่ามีสมาชิกที่เราอยากรู้อยู่ใน List นั้นหรือเปล่า ก็ทำได้ผ่านฟังก์ชัน elem

December 11, 2012

Haskell: ข้อความและ IO เบื้องต้น

ใน Haskell จะแบ่งข้อมูลตัวอักษรเป็น 2 แบบ ได้แก่ Char (อักษร 1 ตัว) ที่ประกาศโดยใช้ single quote ส่วน String (อักษรหลายตัว) ประกาศโดยใช้ double quote


อย่างไรก็ตาม ถ้าลองตรวจสอบ type ของ String จะเห็นแบบนี้


วงเล็บปีกแข็งที่ครอบ Char นั้นบอกว่า String เป็นข้อมูลแบบ List ของ Char (คือการเอา Char หลายๆ ตัวมาต่อกัน)

การเชื่อม String เข้าด้วยกัน ทำได้โดยวางเครื่องหมาย ++ ไว้ระหว่างข้อความ 2 ข้อความ


อย่างไรก็ตาม การเชื่อม String กับ Char ต้องแปลง type ของ Char ให้เป็น String เสียก่อน


ถ้าต้องการแสดงตัวเลข (และข้อมูลชนิดอื่นๆ) ให้ใช้ฟังก์ชัน show แทน


ส่วนการแปลง type จากข้อความไปเป็น type อื่นเพื่อนำไปคำนวณต่อ สามารถทำได้ผ่านฟังก์ชัน read เช่นนี้


แต่การสั่ง read "23" เพียงอย่างเดียวจะเกิด error เพราะตัวแปรนั้นจะหา type ไม่ได้ (เนื่องจากไม่รู้ว่าจะถูกเอาไปใช้ทำอะไรต่อ)

ทางออกคือถ้ารู้ type ที่จะเอาไปใช้ต่อแน่นอน ก็กำหนดลงไปได้เลย




เนื่องจากหลักการของภาษาเชิง functional นั้นบอกว่า ฟังก์ชันใดๆ ที่เรียกขึ้นมาโดยส่งผ่านตัวแปรเดิมเข้าไป ผลลัพท์ก็ต้องออกมาเหมือนเดิมเสมอ

ตัวแปรของฟังก์ชันในที่นี้ คือตัวแปรที่กำหนดโดยโปรแกรมเมอร์ ไม่ใช่ IO จากฝั่งผู้ใช้ ดังนั้นถ้ายึดตามหลักนี้ โปรแกรมเดียวกันจะให้ผลลัพท์เดิมทุกครั้ง (เพราะ IO ที่รับมาจากผู้ใช้ไม่มีความหมาย) Haskell แก้ปัญหานี้โดยการนิยามฟังก์ชันสำหรับจัดการ IO แยกออกมาโดยเฉพาะ

เมื่อต้องการรับข้อความเป็นบรรทัดจากผู้ใช้ ทำได้โดยฟังก์ชัน getLine ดังนี้


ฟังก์ชันนี้จะรอเราพิมพ์ข้อความไปเรื่อยๆ จนกว่าจะป้อน EOL ให้ สังเกตว่าการเก็บผลลัพท์นั้นไม่ได้ใช้ let แล้วตามด้วย = แล้ว แต่เปลี่ยนมาใช้ <- แทน ซึ่งเป็นสัญลักษณ์พิเศษสำหรับเก็บค่า String ที่รับมาจาก IO ครับ

อนึ่ง การพิมพ์ค่าโดยฟังก์ชัน putStrLn นั้น ตัวแปรแบบต้องเป็นข้อความเท่านั้น ถ้าต้องการพิมพ์ค่าตัวแปรที่ไม่ใช่ข้อความ สามารถใช้ฟังก์ชัน print แทนได้

ตัวอย่างต่อไปนี้เป็นโปรแกรมแบบ IO คอยรับข้อมูลจากผู้ใช้ แล้วคำนวณด้านที่ยาวที่สุดของสามเหลี่ยมมุมฉากครับ

หมายเหตุว่าการ recursive main นั้นเป็นเรื่องปรกติใน Haskell ครับ

December 7, 2012

Haskell: ฟังก์ชันและขอบเขตของตัวแปร

นอกจากเราจะสร้างฟังก์ชันด้วย if-else แล้ว Haskell ยังมีความสามารถในการทำ pattern matching กับตัวแปรฟังก์ชันได้อีกด้วย

ตัวอย่างฟังก์ชันจากคราวก่อนในแบบ pattern matching


pattern matching จะช่วยให้อ่านฟังก์ชันที่อยู่ในรูป recursive ง่ายขึ้นมาก


แต่ฟังก์ชัน fact นี้ยังมีปัญหาอยู่ ตรงที่มันจะ terminate ไม่ได้ ถ้าใส่จำนวนเต็มลบเข้าไป

จากบรรทัดที่สอง x จะไป match กับค่าใดๆ ก็ตามที่ไม่ใช่ 0 เราอาจนึกถึงการใช้ if-else เพื่อดักตัวแปรเหมือนที่ผ่านมา อย่างไรก็ตาม Haskell มีสิ่งที่เรียกว่า guard ซึ่งเทียเท่าได้กับ switch-case ในภาษาอื่น โดยสามารถเรียกใช้งานได้เช่นนี้


นอกจากนี้ เรายังสามารถระบุ type ฟังก์ชันได้เช่นเดียวกับตัวแปร โดยเพิ่มบรรทัดนี้เข้าไปด้านบนสุดครับ




ด้านขอบเขตของตัวแปรนั้น เราเคยใช้ let เก็บค่าตัวแปรที่คำนวณเอาไว้ก่อนมาแล้ว ซึ่งนี่เป็นคำสั่งพิเศษเฉพาะบน ghci เท่านั้น เมื่อเขียนเป็นฟังก์ชันจะต้องมีส่วน in เพื่อบอกขอบเขตของตัวแปรที่โดนประกาศด้วย let เสมอ


เช่นตัวอย่างนี้จะประมาณค่า Fibonacci ในตำแหน่งที่ n โดยคำนวณค่า phi กับ psi เก็บไว้ก่อน ซึ่งมันจะถูกนำไปใช้ได้ในหลังจาก in ตรงบรรทัดที่ 3 เท่านั้นครับ

นอกจากการประกาศด้วย let-in แล้ว ยังมีอีกวิธีคือใช้ where ซึ่งคราวนี้ตัวแปรที่ถูกประกาศจะเห็นได้ทั้งฟังก์ชัน รวมถึงส่วน guard เพื่อทำ switch-case อีกด้วย


สิ่งที่ต้องระวังในการเขียนหลายบรรทัดคือการ indent (จัดย่อหน้า) สังเกตว่าประโยคหลัง where หรือ let นั้นต้อง indent ให้เท่ากัน เช่นเดียวกับส่วน let และ in ครับ

ด้านการออกแบบลำดับตัวแปรของฟังก์ชันนั้น จะให้ตัวแปรแรกๆ เป็นตัวแปรที่เปลี่ยนค่าไม่บ่อย เนื่องจากเราสามารถทำ partial application เพื่อสร้างฟังก์ชันที่มี initial value ได้เช่นนี้

December 6, 2012

PHP: ระบบสมาชิก - log in, log out

คราวก่อน เขียนถึงระบบสมาชิกส่วนแรกซึ่งก็คือ การสมัครสมาชิก คราวนี้มาดูการ log in กับ log out กันบ้าง เริ่มต้นจากสร้างแบบฟอร์มสำหรับ log in กันก่อน แบบฟอร์มก็แบบเดียวกับที่พบได้ตามเว็บไซต์ทั่วไป ส่วน .php ที่ใช้ในการ log in จะเริ่มต้นด้วยการตรวจสอบเบื้องต้นก่อนว่า ผู้ใช้ลืมกรอกข้อมูล หรือเปล่า ถ้าทุกอย่างไม่มีปัญหาอะไร ก็มาตรวจสอบบัญชีผู้ใช้ในฐานข้อมูล โดยใช้การเชื่อมต่อผ่าน PDO เหมือนเดิม ซึ่งตอนที่ตรวจสอบรหัสผ่านนั้น ในตอนสมัครสมาชิก เราใช้การ hash และ salt ในการเก็บข้อมูลของรหัสผ่าน ในขั้นตอน log in เราก็จะต้องแปลงรหัสผ่านให้กลายเป็น hash ที่ถูกต้องด้วย ตอนนี้เราสามารถ log in เข้ามาได้แล้ว แต่ผู้ใช้ต้อง log in ใหม่ทุกครั้งเวลาเปลี่ยนหน้า อันนี้ต้องใช้ session เข้ามาช่วยในการจดจำผู้ใช้

December 4, 2012

Haskell: Boolean และ Control Flow

ค่าความจริงใน Haskell มีเพียงแค่ True กับ False เท่านั้น (ไม่สามารถใช้ 0 หรือค่าว่างแบบอื่นๆ แทน False ได้)

ฟังก์ชันสำหรับตรวจสอบความเท่ากัน ใช้ == (เท่ากัน) กับ /= (ไม่เท่ากัน) ส่วนฟังก์ชันสำหรับการตรวจสอบลำดับก็ได้แก่ <= (น้อยกว่าเท่ากับ), < (น้อยกว่า), > (มากกว่า), >= (มากกว่าเท่ากับ)


logic สำหรับเชื่อมค่าความจริงเหล่านี้ได้แก่ && (และ), || (หรือ), not (นิเสธ)


สาเหตุที่นิเสธไม่ใช้สัญลักษณ์เช่นเดียวกับการดำเนินการอื่นๆ เพราะฟังก์ชันใน Haskell จะเขียนแทนด้วยสัญลักษณ์ได้ มันต้องเป็นฟังก์ชัน 2 ตัวแปรและทำหน้าที่เป็น infix เท่านั้นครับ



ด้วยความที่เป็นภาษา functional ทุกๆ expression จะต้องคืนค่ากลับมาเสมอ ทำให้การใช้ if ใน Haskell ต้องมีส่วน else ตามท้ายตลอด


ถ้าต้องการใช้แค่ if อย่างเดียว และบังคับโปรแกรมให้หยุดทำงานเมื่อเจอเงื่อนไขที่ผิด ก็สามารถใช้ฟังก์ชัน error แบบนี้แทนได้


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


สิ่งที่ต่างไปจากการประกาศฟังก์ชันบน ghci คือ ไม่ต้องมี let นำหน้า เนื่องจากฟังก์ชันนี้ไม่ได้ถูกประกาศใน main ครับ

เซฟเป็นไฟล์ func.hs แล้วกลับมาที่ ghci เราจะเรียกฟังก์ชันที่เขียนเก็บด้วยคำสั่ง :l หรือ :load ดังนี้

December 3, 2012

Haskell: ชนิดของฟังก์ชัน

ถ้าลองใช้ :t ตรวจชนิดของฟังก์ชันดูบ้าง จะเห็นแบบนี้


ส่วนที่บอก type จะอยู่หลังเครื่องหมาย => เช่นเคย โดยฝั่งซ้ายของ -> จะเป็นตัวแปรที่รับเข้ามาในฟังก์ชัน ส่วนฝั่งขวาคือผลลัพท์จากฟังก์ชัน นั่นหมายความว่าฟังก์ชัน fromIntegral รับตัวแปรใน type class Integral แล้วได้ผลลัพท์เป็น type class Num

การเขียนฟังก์ชันนั้น เช่นเดียวกับการประกาศค่าคงที่ คือใช้ let พร้อมชื่อฟังก์ชันและตัวแปร เช่น




ทีนี้ลองดูฟังก์ชันของ 2 ตัวแปรกันบ้าง


สิ่งที่แตกต่างไปจากภาษา imperative และถือได้ว่าเป็นจุดเด่นของภาษาแบบ functional เลยคือ แทนที่ type ของฟังก์ชัน 2 ตัวแปรจะใช้ช่องว่างคั่นตัวแปร (type น่าจะอยู่ในรูป a a -> a) แต่ระหว่างตัวแปรกลับใช้สัญลักษณ์ -> เช่นเดียวกับการบอกผลลัพท์ที่ได้จากฟังก์ชัน

เราอาจลองแบ่งกลุ่มเพื่อความกระจ่างใน type ว่ามันคือ a -> (a -> a) ซึ่งหมายความว่า ฟังก์ชันนี้เป็นฟังก์ชันที่รับตัวแปร 1 ตัว แล้วให้ผลลัพท์เป็นฟังก์ชันที่รับตัวแปร 1 ตัวแล้วคืนค่าออกมาก็ย่อมได้

นั่นหมายความว่า ฟังก์ชัน 2 ตัวแปร แท้จริงแล้วคือฟังก์ชัน 1 ตัวแปร 2 ชั้น เราจึงสามารถเรียกฟังก์ชันแบบนี้


ได้เช่นกัน นี่เป็นเหตุผลว่าทำไม Haskell ถึงไม่ใช้วงเล็บล้อมรอบตัวแปร เราเรียกเทคนิคการทำให้ฟังก์ชันหลายตัวแปรกลายร่างเป็นฟังก์ชันตัวแปรเดียวซ้อนๆ กันเช่นนี้ว่า currying และเรียกฟังก์ชันที่รับตัวแปรเข้าไปแล้วบ้างแต่ยังไม่ครบว่า partial application ครับ



อนึ่ง เราสามารถเขียนการหารเก็บเศษข้างบนเป็นแบบนี้ 123456789 `mod` 1009 และถ้าเราต้องการเอาวงเล็บล้อมรอบ `mod` 1009 ด้านหลัง มันจะกลายเป็นฟังก์ชันที่รับตัวแปรไปแล้ว 1 ตัว และต้องการอีก 1 ที่ต้องทำต่อคือย้ายมันไปไว้ข้างหน้าเท่านั้นเอง


ซึ่งสามารถจับมาประกาศเป็นฟังก์ชันได้ว่า


หรือยิ่งไปกว่านั้น




กลับไปดูฟังก์ชัน half ที่เขียนไว้ตอนต้นอีกที เราลองเขียนมันใหม่เป็น


จะเห็นว่าไม่เพียงแต่ฟังก์ชันเท่านั้น ที่เราสามารถนำวงเล็บไปล้อมรอบได้ แต่นี่ยังรวมไปถึง operator ด้วย นอกจากนี้



นั่นหมายความว่า operator เหล่านั้นก็ถือเป็นฟังก์ชันเช่นเดียวกันนั่นเอง

December 2, 2012

PHP: ระบบสมาชิก - สมัครสมาชิก

งานการทำงานด้านเว็บด้วยภาษาฝั่งเซิฟเวอร์ มักจะมีความเกี่ยวข้องกับระบบฐานข้อมูลอยู่เสมอ ๆ หนึ่งในนั้นคือ ระบบสมาชิก เว็บหลายเว็บในปัจจุบัน มีการเชื่อมต่อระบบสมาชิกเข้ากับ social network ต่าง ๆ เพื่อความสะดวกให้กับผู้ใช้ แต่ผู้ใช้บางคนอาจจะไม่ต้องการผูกบัญชีของ social network เข้ากับบางเว็บ ด้วยเหตุผลต่าง ๆ นานา ระบบสมาชิกจึงยังจำเป็นอยู่

ตอนแรกขอพูดถึงการสมัครสมาชิกก่อน แล้วตอนต่อไปจะว่ากันเรื่องการ log in และ log out เพราะถ้ายังไม่สมัครสมาชิกก่อน ก็ log in ไม่ได้...

ในระบบสมาชิก สิ่งที่จำเป็นจริง ๆ มีอยู่ 2 อันคือ ชื่อผู้ใช้ และ รหัสผ่าน แต่ควรจะมี อีเมล เพื่อใช้ในการติดต่อกับสมาชิกไว้ด้วย

จะขอข้ามไม่พูดถึงเรื่องการสร้างฐานข้อมูล และตาราง แต่จะบอกแค่ชื่อ และโครงสร้างของตาราง นอกจากนั้น ใช้ความรู้เรื่อง PHP และ PDO เขียนสคริปท์ PHP กันเอาเอง หรือไม่ก็ใช้เครื่องมือจัดการฐานข้อมูลอย่าง phpMyAdmin (MySQL), phpPgAdmin (PostgreSQL) หรือตัวอื่น ๆ ตามแต่ละชนิดฐานข้อมูลกันตามสะดวก
เมื่อฐานข้อมูลพร้อมแล้ว ก็สร้างแบบฟอร์มสำหรับสมัครสมาชิกขึ้นมาก่อน เมื่อมีคนมาสมัครสมาชิก ข้อมูลจะถูกส่งมายัง regis.php ก็ต้องตรวจสอบค่าให้ถูกต้องเรียบร้อยก่อนว่าผู้ใช้กรอกข้อมูลครบถ้วนสมบูรณ์ดี หากผิดพลาดก็แจ้งเตือนกลับไป เมื่อตรวจสอบข้อมูลเรียบร้อยแล้ว หากไม่มีอะไรผิดพลาดก็เชื่อมต่อกับฐานข้อมูล และตรวจสอบว่ามีใครใช้ชื่อนี้ไปก่อนหรือยัง เพราะชื่อบัญชีห้ามซ้ำกัน

Haskell: ชนิดของตัวเลข

ระบบชนิดตัวแปรของ Haskell จะแตกต่างจากภาษาอื่นๆ ตรงที่แบ่งออกได้เป็น 2 ส่วน คือ
  1. type class ลำดับชั้นของตัวแปรที่บ่งบอกความสัมพันธ์กับฟังก์ชันต่างๆ
  2. type ชนิดของตัวแปร โดยตัวแปรชนิดนึงสามารถมีได้หลาย class
อาจมองได้ง่ายๆ อีกอย่างว่า type คือวิธี implement ข้อมูล ส่วน type class จะเป็นตัวกำหนดความสามารถของ type นั้นๆ ครับ

ซึ่งตัวเลขใน Haskell สามารถจัดแบ่งคร่าวๆ ได้ดังนี้
  • ตัวเลข class: Num
    • จำนวนเต็ม class: Integral
    • ทศนิยม class: Floating, Fractional
      • type: Float ทศนิยมแบบ single ความละเอียด 9 หลัก
      • type: Double ทศนิยมแบบ double ความละเอียด 18 หลัก



และเราสามารถตรวจสอบ type ได้โดยใช้ command :t (หรือเต็มๆ คือ :type) แล้วตามด้วยตัวแปร เช่น


type class พร้อมตัวแปรที่ใช้แสดงแทนนั้น จะถูกบอกไว้ก่อนเครื่องหมาย => ส่วนอีกด้านจะบอก type ของตัวแปร ซึ่งในกรณีที่เราไม่ได้เจาะจง type ลงไปเช่นนี้ Haskell จะจัดมันไว้ใน type class ที่ใหญ่ที่สุดไว้ก่อน เพื่อที่ว่าถ้าดำเนินการกับ type class ที่เป็นลูกหลานของมันจะยังให้ผลลัพท์ที่ถูกต้อง

ส่วนการประกาศ type แบบเจาะจงก็ทำได้โดยใส่ 2 colon แล้วตามด้วย type ไว้ข้างหลังตัวแปรนั้น


จะเห็นว่าคราวนี้ไม่มีการบอก type class กับ => แล้ว เพราะเรารู้ type ที่แน่นอนของมันนั่นเอง

อย่างที่บอกไว้แต่ต้น การดำเนินการข้าม type หรือ type class ที่ไม่เกี่ยวข้องกันนั้น ไม่สามารถทำได้ใน Haskell เช่น error ในตัวอย่างนี้


เลขทั้งสองตัวนั้นก็ต่างอยู่ใน type class Integral เหมือนกัน แต่เมื่อถูกกำหนดโดย type ต่างกัน จะทำให้ไม่สามารถนำมาบวกกันได้

ทางออกคือ
  • จำนวนเต็มสามารถใช้ฟังก์ชัน fromIntegral เพื่อเปลี่ยน type class กลับไปเป็น Num ก่อนเอาไปคำนวณต่อ
  • ทศนิยมใช้ฟังก์ชัน truncate (ปัดทิ้ง), floor (ปัดลง), ceiling (ปัดขึ้น), round (ปัดครึ่ง) เพื่อเปลี่ยน type class เป็น Integral ได้

December 1, 2012

Haskell: ใช้เป็นเครื่องคิดเลข

ตัวเลขใน Haskell ก็เหมือนภาษาอื่น ที่แบ่งออกหลักๆ เป็นจำนวนเต็มกับทศนิยม

โดยการประกาศทศนิยม จะมีข้อบังคับว่า หน้าและหลังจุดทศนิยมต้องมีตัวเลขเสมอ (แม้จะเป็น 0 ก็ตาม)



การดำเนินการบวกลบคูณหาร ก็ใช้ operator +, -, *, / เช่นเดียวกับภาษาอื่นๆ

สังเกตว่าการหารจะให้ผลลัพท์เป็นทศนิยมเสมอ ส่วนการหารแบบไม่เก็บเศษ (ให้ผลลัพท์เป็นจำนวนเต็ม) จะใช้ฟังก์ชัน div แทน

จะเห็นว่า การเรียกฟังก์ชั่นใน Haskell ไม่ใช้วงเล็บล้อมรอบตัวแปรเหมือนภาษาอื่นๆ

ส่วนถ้ารู้สึกไม่สะดวกที่จะเขียนอย่างข้างบน ก็สามารถใช้ grave accent (`) ล้อมรอบชื่อฟังก์ชัน เพื่อให้มันทำงานเป็น infix แบบเดียวกับ operator พวกนั้นได้ (จะใช้ได้ก็ต่อเมื่อเป็นฟังก์ชันที่รับตัวแปร 2 ตัวเท่านั้น)

ข้อแตกต่างอีกอย่างจาก / คือ div จะรับตัวแปรทั้ง 2 ตัวเป็นจำนวนเต็มเท่านั้น

และเมื่อต้องการหารเก็บเศษ ก็ใช้ฟังก์ชัน mod นั่นเอง



ส่วนการยกกำลังก็เช่นเดียวกับการหาร คือมี operator 2 แบบให้เลือกใช้

ท้ายนี้ ถึงแม้ว่า Haskell จะไม่ใช้วงเล็บเพื่อส่งตัวแปรให้ฟังก์ชัน แต่วงเล็บก็สามารถใช้เพื่อบอกกลุ่มของตัวแปรได้เช่นเดียวกับภาษาทั่วไปครับ

หรือถ้ากลัวงง ก็สามารถประกาศค่าเหล่านั้นเป็นตัวแปร (ค่าคงที่) ได้โดยใช้คำสั่ง let

ย้ำอีกครั้งว่า Haskell เป็นภาษา purely functional เพราะฉะนั้นจะไม่มี operator สำหรับเปลี่ยนแปลงค่าของตัวแปรเหล่านั้นนะครับ

November 30, 2012

Haskell: เกร็ดน่ารู้ก่อนเริ่มใช้งาน

จากตอนที่ผ่านมา จะเห็นว่าแบบ interactive นั้นไม่ต้องเขียน main = do ขึ้นต้น นั่นเป็นเพราะว่าเมื่อเริ่ม ghci เราจะเข้ามาอยู่ด้านในของ main ที่ว่านั้นทันที ซึ่งการเขียน code ภายใน main จะมีกฏที่ต่างออกไปเล็กน้อยด้วย

ความสะดวกอย่างอื่นๆ ของตัว interactive
  • แสดงผลลัพท์จากคำสั่งที่พิมพ์เข้าไปทันที (read-eval-print-loop)
  • ทำ keyword completion ด้วย tab
  • macro สำหรับการ debug โดยใช้เครื่องหมาย colon ตามด้วย command (เช่น :q ที่ได้เจอไปแล้ว)
อย่างไรก็ตาม ตัว interactive นั้นไม่รองรับการเขียนหลายบรรทัดเป็นค่าเริ่มต้น ถ้าต้องการเขียนหลายบรรทัด ให้เริ่มด้วย :{ และขึ้นบรรทัดใหม่ แล้วค่อยเริ่มส่วน code เรียบร้อยแล้วจบด้วยบรรทัดที่มีแต่ :} ครับ

อนึ่ง เครื่องหมายหน้าบรรทัดที่บอกว่ากำลังอยู่ใน interactive จะเขียนว่า Prelude> ซึ่งต่อไปเมื่อทำการ load module เยอะๆ แล้ว ข้อความตรงนี้จะเปลี่ยนไปบอกว่า load อะไรมาแล้วบ้าง (ซึ่งอาจจะยาวมาก) ดังนั้น ที่นี้จะใช้แค่ ghci> แทน (สามารถ set ให้เป็นข้อความนี้ได้ด้วย :set prompt "ghci>" ครับ)



ส่วนการเขียนโปรแกรมเก็บเป็นไฟล์นั้น นอกจากจะใช้ ghc เพื่อ compile เป็น binary code ออกมาทดสอบแล้ว ยังมี runhaskell ที่อ่านไฟล์แล้วรันให้ทันทีโดยไม่สร้างไฟล์พวก .o, .hi หรือตัว binary code ให้ แน่นอนว่ามันจะทำงานได้ช้ากว่าแบบ binary code เพราะไม่โดน optimize ด้วยตัว compiler และยังต้องแปลใหม่ทุกครั้งที่เรียกใช้ครับ

ก็เลือกใช้ ghci, ghc และ runhaskell กันตามความเหมาะสมนะครับ



ด้านนามสกุลไฟล์นอกจาก .hs แล้ว ก็มี .lhs ซึ่งเป็นไฟล์ที่เน้นการเขียนโปรแกรมแบบเอกสาร แล้วแทรก code ตามจุดต่างๆ (literate programming) ตัวอย่างก็เช่น


โปรแกรมนี้จะแสดงผลลัพท์เช่นเดียวกับตอนที่แล้ว อย่างไรก็ตามส่วนที่อธิบายโปรแกรมนั้น ไม่ได้ส่งผลให้เกิดความแตกต่างของ binary code แต่อย่างใด

ส่วนการ comment source ใน Haskell แบบบรรทัดเดียวใช้ -- เพื่อ comment ข้อความจนจบบรรทัด และแบบหลายบรรทัดใช้ {- กับ -} ครอบส่วนที่ต้องการ comment

ในที่นี้จะไม่ใช้ comment ให้ดู เนื่องจากตัว syntax highlighter ไม่ระบายสีให้ครับ :P

November 29, 2012

Haskell: สวัสดีโลก

1990 มาตรฐานภาษา Haskell 1.0 ปรากฏตัว
1998 รวบรวมมาตรฐานภาษารุ่นก่อนๆ ได้เป็น Haskell 98
2003 GHC เครื่องมือแปลภาษา Haskell ได้รับความนิยมจนเป็นมาตรฐานกลายๆ
2010 ตัวภาษาเดินทางมาถึงรุ่น Haskell 2010

ชื่อภาษาตั้งเพื่อเป็นเกียรติแก่ Haskell Curry นักคณิตศาสตร์ผู้บุกเบิก combinatory logic ครับ



Haskell เป็นภาษา purely functional ซึ่งทำให้เราไม่สามารถเปลี่ยนค่าตัวแปรหลังจากที่ประกาศมันได้ ส่งผลให้การคำนวณซ้ำแบบ loop ต้องเปลี่ยนไปเขียนในรูป recursion หรือ list comprehension แทน ซึ่งจะช่วยลด side effect อย่างการแชร์ตัวแปรจากที่เดียวกันได้ นอกจากนี้ตัวภาษายังเป็นแบบ lazy evaluation คือจะทำการคำนวณให้เมื่อขอดูผลลัพท์เท่านั้นครับ

โหลดโปรแกรม haskell-platform มาติดตั้งได้จากที่นี่ รองรับทั้ง Windows, Mac และ Linux (สำหรับ Debian ติดตั้งจาก apt-get ได้ด้วย) หรือจะไปทดลองเล่น intepreter ขนาดย่อมผ่านหน้าเว็บก่อนก็ได้



Haskell นั้นมาพร้อมกับ compiler และ interpreter ในตัว ซึ่งทั้งสองแบบนี้จะมีการทำงานแตกต่างกันเล็กน้อย ว่าแล้วก็มาลอง hello กัน โดยเริ่มที่แบบ interpreter ก่อน โดยพิมพ์

เพื่อเข้าสู่ Haskell interpreter ซึ่งมีข้อความต้อนรับดังนี้

พิมพ์

ก็จะพบกับข้อความทักทาย เรียบร้อยแล้วออกโปรแกรมโดยพิมพ์ :q [Enter] หรือป้อน EOF ก็ได้

ส่วนแบบที่ต้อง compile สร้างไฟล์ใหม่ที่มีข้อความดังนี้

เซฟเป็นไฟล์ hello.hs แล้วสั่ง

จะได้ไฟล์ hello มา (ต่างจาก compiler อย่าง gcc ที่จะได้ไฟล์ a.out) และเรียกโปรแกรมโดย

ก็เป็นอันเสร็จพิธี

November 22, 2012

PHP: PDO กับฐานข้อมูล

ในการใช้ PHP ทำงานร่วมกับฐานข้อมูล จำเป็นต้องรู้ภาษา SQL เสียก่อน และต้องรู้ด้วยว่า ฐานข้อมูลที่ต้องทำงานด้วยนั้นเป็นระบบไหน เพราะ PHP มีคำสั่งเฉพาะสำหรับฐานข้อมูลแต่ละระบบแยกกันอยู่

แต่เมื่อมีเทคโนโลยีมากขึ้นระบบฐานข้อมูลมีความหลากหลายมากขึ้น ความคล่องตัวในการย้ายฐานข้อมูลจากระบบหนึ่งไปยังอีกระบบหนึ่งก็ยุ่งยากขึ้น ทาง PHP จึงสร้าง extension ชื่อ PDO (PHP Data Objects) ขึ้นมาเพื่อเป็นตัวกลางในการจัดการฐานข้อมูล โดยโปรแกรมเมอร์แทบไม่ต้องสนใจว่า ระบบฐานข้อมูลที่จะทำงานร่วมกันเป็นแบบไหน

การเชื่อมต่อกับระบบฐานข้อมูลผ่าน PDO ก็ให้สร้างออปเจ็กของคลาสขึ้นมา ตัวอย่างข้างต้นใช้ MySQL แต่ถ้าต้องการเปลี่ยนเป็นอย่างอื่นก็แก้ไขบรรทัดที่ 12 ให้เป็นระบบฐานข้อมูลนั้น ๆ หากไม่มีการใช้คำสั่ง SQL ที่จำเพาะต่อระบบฐานข้อมูลนั้น ๆ ก็จะไม่ต้องแก้ไขโค้ดในส่วนอื่น ๆ อีก ส่วนการปิดการเชื่อมต่อนั้น ตามหลักแล้ว PHP จะปิดการเชื่อมต่อให้โดยอัตโนมัติเมื่อจบสคริปท์ แต่หากต้องการสั่งเองก็ใช้ เมื่อเชื่อมต่อกับฐานข้อมูลได้แล้ว ก็สามารถใช้คำสั่ง SQL จัดการกับฐานข้อมูลได้ 2 วิธี คือ
  • สั่งโดยตรงผ่านฟังชั่น exec() และ query()
  • ใช้ฟังชั่น prepare() และ excute()

November 12, 2012

SQL: สร้าง เพิ่ม ลบ อัพเดทข้อมูลในฐานข้อมูล

ในตอนก่อน พูดถึงการแสดงข้อมูล และค้นหาข้อมูลตามที่ต้องการ คราวนี้มาดูวิธีการสร้างฐานข้อมูล ตาราง และการเพิ่ม ลบ แก้ไขเปลี่ยนแปลงข้อมูลของตารางกันกันบ้าง

การสร้างฐานข้อมูลนั้นจะใช้คำสั่ง CREATE ซึ่งผู้ใช้จะสร้างฐานข้อมูลได้หรือไม่นั้น ขึ้นอยู่กับว่า บัญชีนั้นมีสิทธิ์ที่จะทำได้หรือไม่ด้วย ซึ่งในหลาย ๆ โฮสต์ จะอนุญาตให้สร้างฐานข้อมูลผ่านทาง back-end แทนที่จะสร้างโดยตรงผ่านทางคำสั่ง SQL เราก็จะได้ฐานข้อมูลเปล่า ๆ มาอันนึง ซึ่งจำเป็นต้องสร้างตารางขึ้นมาเพื่อเก็บข้อมูลเพิ่มเติมโดยใช้คำสั่ง CREATE เช่นเดียวกัน โดยต้องระบุ ชื่อคอลัมภ์พร้อมคุณลักษณะต่าง ๆ อย่างเช่น ชนิดของข้อมูลในคอลัมภ์นั้น ๆ ด้วย

นอกจากนี้ ยังต้องคำนึงด้วยว่าระบบฐานข้อมูลที่ใช้อยู่เป็นตัวไหน เพราะคำสั่งนี้บางส่วนในบางระบบจะใช้ไม่เหมือนกัน (อย่างเช่นการกำหนด primary key และ การเพิ่มค่าอัตโนมัติในตัวอย่าง) ตอนนี้ก็จะมีตาราง application เปล่า ๆ ขึ้นมาอันหนึ่ง
idnamecategorydeveloperyear
การเพิ่มข้อมูล (แถว) เข้าตารางจะใช้คำสั่ง INSERT INTO ในที่นี้คอลัมภ์ id จะเพิ่มโดยอัตโนมัติ (auto_increment) ถ้าส่งค่า null ไประบบฐานข้อมูลจะใส่ค่าถัดไปให้โดยอัตโนมัติ และถ้าต้องการเพิ่มข้อมูลที่ละหลาย ๆ แถวก็สามารถใส่ค่าของ VALUES ลงไปหลาย ๆ ชุดได้เช่นกัน ผลที่ได้จะเป็น
idnamecategorydeveloperyear
1WindowsOSMicrosoft1985
2OS XOSApple Inc.2001
3Microsoft OfficeOffice SuiteMicrosoft1990
4Libre OfficeOffice SuiteThe Document Foundation2011
5Adobe PhotoshopGraphic EditorAdobe1990
6GIMPGraphic EditorThe GIMP Development Team1996
7FirefoxBrowserMozilla2004
ถ้าตารางนั้นมีข้อมูลเริ่มต้นอยู่ หรือไม่จำเป็นต้องใส่ค่าลงไปได้ สามารถที่จะระบุเฉพาะคอลัมภ์ที่ต้องการใส่ข้อมูลลงไปได้เช่นเดียวกัน ผลที่ได้จะเป็น
idnamecategorydeveloperyear
1WindowsOSMicrosoft1985
2OS XOSApple Inc.2001
3Microsoft OfficeOffice SuiteMicrosoft1990
4Libre OfficeOffice SuiteThe Document Foundation2011
5Adobe PhotoshopGraphic EditorAdobe1990
6GIMPGraphic EditorThe GIMP Development Team1996
7FirefoxBrowserMozilla2004
8Chromium  2008
ส่วนการแก้ไขข้อมูลนั้นจะใช้ UPDATE ผลที่ได้จะเป็น
idnamecategorydeveloperyear
1WindowsOSMicrosoft1985
2OS XOSApple Inc.2001
3Microsoft OfficeOffice SuiteMicrosoft1990
4Libre OfficeOffice SuiteThe Document Foundation2011
5Adobe PhotoshopGraphic EditorAdobe1990
6GIMPGraphic EditorThe GIMP Development Team1996
7FirefoxBrowserMozilla2004
8Google ChromeBrowserGoogle Inc.2008
สิ่งที่ควรจำให้ขึ้นใจคือ จะแก้ไขข้อมูลต้องมี WHERE เสมอ ไม่เช่นนั้นจะเป็นการแก้ไขข้อมูลทั้งตาราง! และควรจะใช้เงื่อนไขของ WHERE อย่างรอบคอบ เพื่อไม่ให้ข้อมูลที่ไม่เกี่ยวข้องได้รับผลกระทบ

ส่วนการลบข้อมูลก็จะใช้ DELETE ซึ่งจำเป็นต้องมี WHERE เช่นกัน และที่สำคัญคือถ้าใช้ WHERE ไม่รอบคอบ หรือไม่ใช้ ข้อมูลอาจจะหายไปทั้งตารางได้เช่นกัน
idnamecategorydeveloperyear
1WindowsOSMicrosoft1985
2OS XOSApple Inc.2001
5Adobe PhotoshopGraphic EditorAdobe1990
6GIMPGraphic EditorThe GIMP Development Team1996
7FirefoxBrowserMozilla2004
8Google ChromeBrowserGoogle Inc.2008
แม้ว่าจะใช้ DELETE ลบข้อมูลทั้งตารางทิ้งไป แต่โครงสร้างตาราง ค่า index (ที่ใช้ในการค้นหาข้อมูล) รวมถึงค่าคุณลักษณะ (attribute) ต่าง ๆ ของตารางยังคงอยู่ ไม่ได้ถูกลบไปด้วย ตามด้วย ผลจะเป็น
idnamecategorydeveloperyear
9Internet ExplorerOSMicrosoft1995
แต่หากต้องการจะลบข้อมูลในตารางทิ้งจริง ๆ แนะนำให้ใช้ TRUNCATE เพราะมันรวดเร็วกว่า และใช้ทรัพยากรน้อยกว่า หากจะลบตารางทั้งตารางทิ้งจะใช้คำสั่ง DROP ตาราง application ทั้งตารางรวมทั้งโครงสร้างจะหายไปจากฐานข้อมูล ถ้าจะใช้ตารางนี้อีก ต้องสร้างตารางขึ้นมาใหม่เสียก่อน

นอกจากลบตารางทิ้งแล้ว DROP ยังสามารถลบฐานข้อมูลทั้งฐานข้อมูลทิ้งได้เช่นกัน (ทั้งนี้ขึ้นอยู่กับสิทธิ์ในการเข้าถึงฐานข้อมูลของบัญชีผู้ใช้อันนั้น ๆ ด้วย ว่าสามารถทำได้ถึงขั้นไหน)

November 5, 2012

SQL: ดึงข้อมูลจากฐานข้อมูลด้วย SELECT

SQL (Structured Query Language - ภาษาสอบถามเชิงโครงสร้าง) เป็นภาษาสำหรับจัดการฐานข้อมูลที่นิยมใช้กันมากในปัจจุบัน

SQL พัฒนาที่ IBM โดย Donald D. Chamberlin และ Raymond F. Boyce ในปี ค.ศ. 1970 โดยมีแนวคิดบางส่วนมาจาก Edgar F. Codd เพื่อใช้กับระบบฐานข้อมูล System R

เดิม SQL จะใช้ชื่อว่า SEQUEL (Structured English Query Language) แต่ดันมีปัญหาเรื่องเครื่องหมายการค้า เลยเปลี่ยนชื่อเป็น SQL ซึ่งอ่านได้ทั้ง เอส-คิว-แอล, ซี-เควล และ ซี-ควล ตามชอบใจ

ระบบฐานข้อมูลส่วนใหญ่ ไม่ว่าจะเป็น MySQL, PostgreSQL, SQL Server, Access, Oracle, DB2 หรือ SQLite ก็ใช้ SQL ในการจัดการข้อมูลทั้งสิ้น

แต่... แม้ว่าระบบฐานข้อมูลเหล่านี้จะใช้ SQL ในการจัดการเหมือนกัน แต่รายละเอียดบางคำสั่งย่อย ๆ บางคำสั่งอาจจะแตกต่างกัน หากผลที่ได้ไม่ได้ออกมาตามต้องการ ให้คิดถึงจุดนี้ด้วย

สำหรับข้อมูลที่ใช้จะใช้ข้อมูลตามนี้ ขอตั้งชื่อตารางว่า application
idnamecategorydeveloperyear
1WindowsOSMicrosoft1985
2OS XOSApple Inc.2001
3Microsoft OfficeOffice SuiteMicrosoft1990
4Libre OfficeOffice SuiteThe Document Foundation2011
5Adobe PhotoshopGraphic EditorAdobe1990
6GIMPGraphic EditorThe GIMP Development Team1996
การนำข้อมูลมาแสดงจะใช้ SELECT ... FROM ... เป็นหลัก โดยอาจจะกำหนดเงื่อนไขผ่านทาง WHERE (ที่จริงแล้วคำสั่ง SQL จะใช้ตัวพิมพ์เล็ก หรือตัวพิมพ์ใหญ่ก็ให้ผลเหมือนกัน เพียงแต่ใช้ตัวพิมพ์ใหญ่จะอ่านได้เข้าใจง่ายกว่า) ผลที่ได้จะเหมือนกับตารางด้านบน คือ เอาข้อมูลทุกอย่างมาแสดง อันนี้เลือกเอาจะเฉพาะคอลัมภ์ name กับ year มาแสดง
nameyear
Windows1985
OS X2001
Microsoft Office1990
Libre Office2011
Adobe Photoshop1990
GIMP1996
อันนี้จะแสดงข้อมูลที่ไม่ซ้ำกันเท่านั้น ที่ซ้ำตัดทิ้งไป อาจะใช้หาว่า ลูกค้าของบริษัทเป็นคนจังหวัดใดบ้าง เป็นต้น
category
OS
Office Suite
Graphic Editor
แต่ถ้าหากจะกำหนดเงื่อนไขว่าเอาข้อมูลที่มีอย่างนั้นอย่างนี้จะต้องใช้ WHERE เข้าช่วย โดยใช้การเปรียบเทียบทั่ว ๆ ไป รวมทั้ง BETWEEN (ระหว่างค่า 2 ค่า) LIKE (ค้นหาโดยใช้ pattern) และ IN (ข้อมูลในคอลัมภ์เป็นค่าใดค่าหนึ่งในนี้)

หมายเหตุ: สำหรับการเปรียบเทียบ ไม่เท่ากับ ระบบฐานข้อมูลบางตัวใช้ <> แต่บางตัวใช้ != ผลที่ได้คือ จะแสดงเฉพาะแอพที่เปิดตัวตั้งแต่ปี 2000 ขึ้นไป
idnamecategorydeveloperyear
2OS XOSApple Inc.2001
4Libre OfficeOffice SuiteThe Document Foundation2011
หากจะให้ค้นหาข้อมูลจาก 2 คอลัมภ์ จะต้องใช้ AND (และ) หรือ OR (หรือ) เพิ่มต่อจาก WHERE หมายเหตุ: ถ้าสังเกต จะเห็นว่า การเปรียบเทียบ เท่ากับ ใน SQL จะใช้เครื่องหมายเท่ากับเพียงอันเดียว แทนที่จะเป็นสองอันแบบหลาย ๆ ภาษา ผลที่ได้คือ แสดงข้อมูลจะเฉพาะที่ปีต่ำกว่า 2000 และอยู่ในหมวด OS เท่านั้น
idnamecategorydeveloperyear
1WindowsOSMicrosoft1985
หากพบว่า ข้อมูลที่อยู่ในฐานข้อมูลเรียงไม่ถูกใจ สามารถสั่งเรียงข้อมูลใหม่ได้โดยผ่าน ORDER BY ผลที่ได้คือ ข้อมูลที่ได้จะเรียงตามปีจากน้อยไปหามาก (ASC) แต่ถ้าจะให้เรียงจากมากไปหาน้อยจะใช้ DESC
namecategorydeveloperyear
WindowsOSMicrosoft1985
Microsoft OfficeOffice SuiteMicrosoft1990
Adobe PhotoshopGraphic EditorAdobe1990
GIMPGraphic EditorThe GIMP Development Team1996
OS XOSApple Inc.2001
Libre OfficeOffice SuiteThe Document Foundation2011
สำหรับข้อมูลในฐานข้อมูลนั้นจะยังคงเรียงอยู่เหมือนเดิม คำสั่งนี้ใช้เฉพาะการดึงข้อมูลมาแสดงเท่านั้น ถ้าลองสั่ง SELECT * FROM application ดู ผลจะได้เหมือนกับตาราางแรกสุด

ถ้าสังเกตดูภาษา SQL จะอ่านเข้าใจง่าย และคล้ายภาษาอังกฤษมาก

September 20, 2012

let's code: แก้โจทย์คณิตศาสตร์กับ Project Euler

Project Euler คือสถานที่ฝึกปรือฝีมือการแก้โจทย์ปัญหาอีกที่หนึ่ง โดยโจทย์ส่วนใหญ่จะเป็นโจทย์ทางคณิตศาสตร์-คอมพิวเตอร์ ทำให้งานนี้เขียนโปรแกรมเป็นอย่างเดียวไม่พอ ยังต้องงัดสารพัดเทคนิคทางคณิตศาสตร์ออกมาเพื่อให้แก้ปัญหาได้อย่างสวยงามอีกด้วย

ถึงแม้จะไม่มีข้อกำหนดที่ชัดเจนว่าควรใช้ภาษาอะไร/เวลาประมวลผลเท่าไหร่ แต่ทางเว็บก็ได้ออกแบบโจทย์ทุกข้อไว้ให้สามารถแก้ได้ภายในเวลาที่น้อยกว่า 1 นาที (ด้วย algorithm ที่ optimize มาอย่างถูกต้อง) นอกจากนี้ผู้เล่นก็ควรจะซื่อสัตย์ต่อตัวเองด้วย เพราะโจทย์ทุกข้อจะใช้ test case เดิมไปตลอดครับ

ภาษาที่ใช้ได้
ภาษาใดก็ได้ หรือจะทดคำตอบในกระดาษก็ยังไหว

รูปแบบการตรวจคำตอบ
รับ test case จากหน้าเว็บมาประมวลผล แล้วส่งเฉพาะคำตอบ

ตัวอย่างโจทย์
  • 1: หาผลรวมของเลขจำนวนเต็มบวกทุกตัวที่ต่ำกว่า 1000 ซึ่งเลขแต่ละตัวเป็นผลคูณของ 3 หรือ 5
  • 25: จงหาลำดับของเลขฟีโบนักชีตัวแรก ที่เมื่อเขียนเป็นเลขฐานสิบแล้ว มีตัวเลขถึง 1000 หลัก
  • 80: สังเกตว่า √2 สามารถเขียนเป็นทศนิยมไม่รู้จบได้คือ 1.41421356237309504880... จงหาผลรวมของผลรวมของตัวเลข 100 หลักแรกในทศนิยมไม่รู้จบนี้ สำหรับรากที่สองของจำนวนเต็มบวกที่น้อยกว่า 100 ที่เป็นทศนิยมไม่รู้จบ
  • 206: จงหาจำนวนเต็มบวกเพียงหนึ่งเดียว ที่กำลังสองของมันสามารถเขียนให้อยู่ในรูป 1_2_3_4_5_6_7_8_9_0 เมื่อ _ แทนตัวเลขอะไรก็ได้ 1 หลัก

September 4, 2012

Python: รีดความเร็วด้วย PyPy

เนื่องจากตัว Python เป็นภาษาสคริปต์ เวลาสั่งทำงานโปรแกรมจะช้ากว่าภาษาที่ compile เตรียมไว้ก่อนอย่างไม่ต้องสงสัย

ทางออกสำหรับโปรแกรมเมอร์ Python ที่ต้องการให้โปรแกรมเร็วขึ้นก็มีหลายวิธี ตั้งแต่วิเคราะห์ algorithm เพื่อทำ optimization ย้ายไปใช้ module ที่เขียนในภาษา C ไปจนถึงใช้ Python เป็นโครงต้นแบบแล้วเขียนโปรแกรมใหม่ทั้งหมดด้วยภาษาอื่นเลย

จะเห็นว่าทุกวิธีที่ว่ามา มี cost ในการเปลี่ยนที่สูงพอควร ทั้งที่วิธีแก้ปัญหามันควรจะง่ายกว่านั้น เพี่ยงแค่ compile โปรแกรมที่เขียนด้วย Python เก็บไว้ก่อนเช่นเดียวกับภาษาอื่นๆ

ตัวโครงการหลักของ Python ยังไม่มีความสามารถนี้ แต่ไม่ต้องเสียใจไป เพราะโครงการ PyPy สามารถเร่งความเร็วให้โปรแกรมที่เขียนใน Python โดยการใช้ JIT compiler เข้าช่วย แน่นอนว่าเราแทบไม่ต้องเขียน code ใหม่หมดเพื่อความเร็วนี้เลย

สำหรับ Linux สามารถดาวน์โหลด binary package มาติดตั้งเองได้ หรือจะสั่งติดตั้งผ่าน apt-get ก็ได้เช่นกัน เพียงแค่เพิ่ม repository ก่อนดังนี้

ส่วน Windows จะได้ไฟล์ .exe มาเลย ก่อนใช้ก็อย่าลืมเพิ่ม environmental path เพื่อความสะดวกนะครับ



วิธีใช้งานก็เรียบง่ายตรงไปตรงมา แค่เปลี่ยนจาก

ไปเป็น

ก็เรียบร้อย โดย option ที่เพิ่มขึ้นมาจาก python ธรรมดาได้แก่

ที่จะทำการปิด JIT compiler ทิ้งไป โดย option นี้จะเหมาะกับโปรแกรมขนาดเล็กที่ไม่ได้ทำงานซ้ำๆ กันซักเท่าไหร่ครับ

ข้อควรระวังในการใช้ PyPy คือตอนนี้มันยังทำงานได้กับสคริปต์ Python2 เท่านั้น ถ้าเขียoptionนเป็น Python3 แล้วต้องการทดสอบโปรแกรมใน PyPy ก็มีจุดแตกต่างหลักๆ ดังนี้

สำหรับรายละเอียดเต็มๆ ว่าอะไรที่ใช้ได้และไม่ได้บ้าง สามารถอ่านได้จากหน้า compatibility

July 29, 2012

Git: แก้ conflict จากการ merge

ตอนก่อนจะเห็นว่า เราทำการ merge ได้อย่างสบายใจไร้กังวล เพราะ code ส่วนที่แก้นั้นอยู่คนละที่กันเลย แต่ถ้าแก้ code ในที่ๆ ซ้อนทับกัน จะเกิด conflict ซึ่งโปรแกรมไม่สามารถหยั่งรู้ได้ว่า เราต้องการ code ตอนสุดท้ายออกมาเป็นยังไงกันแน่ และเราต้องลงมือจัดการกับ conflict นั้นเองครับ

ศึกษาการแก้ conflict โดยการแตก branch แรกเพื่อเพิ่มความสามารถให้โปรแกรมรับ -v สำหรับบอกเวอร์ชั่นโปรแกรม

ที่ไฟล์ hello.py ส่วนที่เช็ค -h (แถวบรรทัดที่ 5) เพิ่ม code ให้เป็นอย่างนี้

แล้วก็ commit

เรียร้อยกับ branch แรกไปแล้ว ก็มาทำ branch ที่สอง โดยเขียนให้โปรแกรมรับ -l เพื่อบอกลิขสิทธิ์โปรแกรม

แก้ไฟล์ hello.py ที่เดิมเลย (แค่เปลี่ยนหน้าตา code นิดหน่อย)

เช่นเดิม commit มันซะ

คราวนี้กลับมาที่ master แล้วทำการ merge เหมือนตอนที่ผ่านมา

พบว่ามีฟ้อง conflict และส่งผลให้ merge ไม่ผ่าน ตอนนี้ถ้าดูสถานะจะพบว่า

หมายเหตุว่า Git จะไม่ยอมให้ย้าย branch ถ้าหากยังมี conflict เช่นนี้อยู่ ดังนั้นมาเก็บข้อผิดพลาดนี้กัน โดยเปิดไฟล์ hello.py ขึ้นมา จะเห็นดังนี้

ส่วนที่เกิด conflict จะถูกคั่นด้วยบรรทัดที่ขึ้นต้นด้วยเครื่องหมาย <<<<<<< , ======= , >>>>>>> ในตัวอย่างนี้ จัดการลบ 3 บรรทดนั้นทิ้งไปก็พอครับ (สำหรับงานจริง เปิดอ่านโปรแกรมแล้วจัดการเรียบเรียง code ใหม่ให้ทำงานถูกต้องนะครับ)

ตอนนี้อาจจะทดสอบเพิ่มกันอีกเล็กน้อย เมื่อมั่นใจว่าโปรแกรมทำงานถูกต้องแล้ว ก็สั่ง commit ความเปลี่ยนแปลงนี้ครับ (ไม่ใช่สั่ง merge ไปอีกรอบนะครับ -- ตัว Git จะรู้ได้เองว่านี่เป็นการแก้ conflict ที่เกิดจากการ merge)

ถึงตอนนี้ถ้ายังไม่มั่นใจว่าทั้ง 2 branch นั้นถูก merge แล้วจริงหรือเปล่า สามารถดูด้วย branch --no-merged ซึ่งควรจะไม่แสดงผลลัพท์ออกมา (เพราะถูก merge เรียบร้อยแล้ว) และถ้าสั่ง branch --merged ก็ควรจะเห็นครบ

เรียบร้อยแล้วก็ลบ branch ทั้ง 2 ทิ้งเลยครับ

July 28, 2012

Git: ทำการ merge หลาย branch

ในการทำงานร่วมกันหลายคน ย่อมมี branch เกิดขึ้นมากมายแน่นอน (มันคงไม่มีประโยชน์ถ้าจะรอให้คนนึงทำงานแรกเสร็จก่อน แล้วค่อยอนุญาตให้อีกคนทำงานถัดมา) ความสามารถในการ merge หลายๆ branch ที่แตกออกมาในเวลาเดียวกันนั้น จึงเป็นสิ่งที่โปรแกรม SCM ควรทำได้อย่างเป็นเรื่องปรกติธรรมดา

ตอนนี้จะทดสอบการ merge หลาย branch โดยเพิ่มความสามารถ 2 อย่างให้โปรเจค hello

ความสามารถแรกคือการแสดง help ซึ่งสามารถเรียกโดยเพิ่ม -h ทาง command line เช่นเดียวกับโปรแกรมทั่วๆ ไปครับ

แก้ code ในไฟล์ hello.py ดังนี้

(ทดสอบแล้วผ่าน) ก็ commit ตามปรกติครับ

เรียบร้อยกับความสามารถแรกไปแล้ว ทีนี้มาเพิ่มความสามารถที่สอง โดยกลับไปแตก branch มาจากตัว master นะครับ

ความสามารถนี้ จะทำการเรียงลำดับชื่อที่รับเข้ามาก่อน แล้วค่อยทักทายแต่ละชื่อที่เรียงแล้วนั้น ซึ่งสามารถเรียกโดยเพิ่ม -s เข้าไปตอนเรียกโปรแกรมครับ

(ทดสอบกันเอง) แล้วก็ commit ครับ

สลับกลับมาที่ master ถึงตอนนี้เราอาจจะลืมไปแล้วว่าได้ทำการเปลี่ยนแปลงอะไรไว้ที่ branch อื่นบ้าง เราสามารถสั่งให้แสดงความแตกต่างระหว่าง branch ดังนี้ครับ

ข้อมูล 4 บรรทัดแรกจากคำสั่ง diff จะบอกว่าทำการหาความแตกต่างระหว่าง branch ไหน ข้อมูลในบรรทัดที่ 5 (ขึ้นต้นด้วยเครื่องหมาย @@) จะบอกช่วงบรรทัดที่นำมาแสดง และข้อมูลที่เหลือจะแสดงอยู่ในรูปของ patch file คือถ้ามีเครื่องหมาย + หมายถึงมีข้อมูลเพิ่มขึ้นมา เครื่องหมาย - คือข้อมูลที่ถูกลบทิ้งครับ

เมื่อพร้อมแล้วก็ merge กันเลย

สังเกตว่าการ merge ย่อยครั้งแรก (show-help) แม้จะเป็นการ Fast-forward เช่นเดียวกับที่ผ่านมา แต่เมื่อ merge เสร็จ จะมี code ส่วนที่ตรวจว่ามีการขอ help หรือเปล่าเพิ่มขึ้น ทำให้บรรทัดต่างๆ ใน code ส่วนที่เหลือถูกเลื่อนลงมาข้างล่างด้วย เมื่อ Git พยายามจะ merge ครั้งถัดมา (sort-name) มันจะทำการเลื่อน code ส่วนที่ต้องตรวจว่าจะ sort หรือเปล่าลงไปให้เองโดยอัตโนมัติครับ (เปิดไฟล์ hello.py เพื่อศึกษาผลลัพท์)

เสร็จแล้วก็อย่าลืมลบ branch ทิ้งนะครับ

July 27, 2012

Git: รวม branch เข้าด้วยกัน

การแตก branch นั้นช่วยเก็บรักษา code รุ่นที่ยังทำงานถูกต้องไว้ก็จริง แต่มันจะไม่มีประโยชน์เลยถ้าจะแตก branch ต่อไปกันเรื่อยๆ เพราะสุดท้ายเราจะไม่รู้เลยว่า branch ไหนมีความสำคัญอย่างไร code ที่เพิ่มมาใน branch ย่อยๆ เมื่อทดสอบว่าทำงานได้ถูกต้องสมบูรณ์แล้ว ควรถูกย้ายกลับมาอยู่ใน branch master ครับ

ตอนนี้จะเขียนโปรแกรมให้รับ argument เป็นชื่อคนจาก command line แล้วเปลี่ยนไปทักทายคนเหล่านั้นแทน

สร้าง branch ใหม่เช่นเคย (สังเกตว่าเปลี่ยนไปใช้ checkout -b ซึ่งเป็นรูปย่อของการสร้าง branch พร้อม checkout) แล้วแก้ไขไฟล์ hello.py เป็นดังนี้

ทดสอบโปรแกรมกันหน่อย

โปรแกรมทำงานได้ตามที่ออกแบบไว้ ก็ได้เวลา commit เข้าระบบ

เรียบร้อยแล้วสลับกลับมาที่ branch master และสั่ง merge (รวม branch) hi-name กับ master เข้าด้วยกัน

เพียงเท่านี้ที่ branch master ก็จะได้รับ code ที่เพิ่ม feature ใหม่ๆ มาจาก branch ที่แตกไปแล้วครับ

อันที่จริง ถ้าสังเกตดูรายงาน จะเห็นว่าเป็นการ Fast-forward ซึ่งหมายถึงหมายเลข commit ของ branch master ย้ายไปใช้ตัวเดียวกับของ hi-name เท่านั้นเอง ยังไม่ใช่การ merge ที่แท้จริงแต่อย่างใด

และเนื่องจากตอนนี้ branch hi-name ก็ได้ทำหน้าที่ของมันเรียบร้อยแล้ว สั่งลบได้เลยครับ

สังเกตว่าเปลี่ยนไปใช้ -d (ตัวเล็ก) แทน ซึ่งตัวเลือกนี้จะเช็คดูให้แน่ในก่อนว่า branch นั้นถูก merge เรียบร้อยแล้วถึงจะลบทิ้งได้ครับ

July 26, 2012

Git: แตก branch

จุดแข็งที่ทำให้เราต้องใช้โปรแกรมจำพวก SCM คือความสามารถในการกู้คืนความเสียหายให้กลับมาเป็นเหมือนเดิม

การแตก branch ก็เป็นหนึ่งในจุดแข็งนี้ คอนเซ็ปง่ายๆ ของมันคือทำสำเนาไฟล์ทั้งหมด แล้วก็ทำงานในไฟล์ที่สำเนามานั่นเอง ถ้าแก้ไขต่อเติมไฟล์แล้วผลลัพท์ออกมาไม่ดี ก็สามารถลบ branch นั้นๆ ทิ้งได้โดยไม่กระทบกับโปรเจคหลัก

ลองแตก branch จากโปรเจค hello โดยให้ชื่อว่า polyglot สามารถสั่งได้ดังนี้

และดู branch ที่มีด้วย

จะเห็นว่ามี branch master กับ polyglot โดยตอนนี้อยู่ที่ branch master ซึ่งเป็น branch หลักที่ Git สร้างไว้ให้ โดย branch นี้ควรจะเก็บโปรเจคช่วงที่ stable เอาไว้ครับ

ตอนนีเรารู้แล้วว่าต้องการทำงานที่ branch polyglot ก็สามารถสลับ branch ไปได้โดยสั่ง

จะทดลองลองเขียนโปรแกรมแบบรองรับหลายภาษา โดยแก้ไขไฟล์ hello.py กันนิดหน่อย ให้เป็นดังนี้

เรียบร้อยแล้วคอมไพล์เป็น C++ และทดสอบการทำงานทั้ง 2 ภาษาดู

ทำงานได้ผ่านเรียบร้อยก็เตรียมตัว commit code ซึ่งถ้าลองตรวจสถานะโปรเจคดู จะพบว่ามีไฟล์ a.out ซึ่งจะไม่ถูกจำเข้าระบบด้วย

เนื่องจากไฟล์ a.out เกิดจากการคอมไพล์ไฟล์ hello.py และยังไม่มีความจำเป็นต้องใช้ในเวลานี้ เราอาจสั่งให้ Git ลบไฟล์ทั้งหมดที่ไม่รู้จักโดย

ถึงตรงนี้ ก็พร้อม commit แล้ว ซึ่งถ้าไม่อยากเสียเวลาไล่สั่ง add ไฟล์ที่แก้ไข สามารถใส่ -a เพื่อบอกให้ add ไฟล์เหล่านั้นแบบอัตโนมัติได้ครับ



แต่เนื่องจาก branch polyglot นี้ มันค่อนข้างพัฒนาโปรแกรมต่อได้ยาก เราจะลบ branch นี้ทิ้งไปครับ โดยก่อนอื่นต้องเปลี่ยนไปอยู่ branch master ก่อน แล้วสั่งลบด้วยคำสั่งนี้

July 25, 2012

Git: จัดการไฟล์พื้นฐาน

ตอนก่อนเราได้สวัสดีชาวโลกกันไปแล้ว แต่จะเห็นว่าไวยากรณ์ผิดพลาดอยู่บ้าง ลองแก้ไฟล์ hello.py เป็น

พร้อมกับเขียนไฟล์ใหม่ชื่อ README.md เพื่อบอกข้อมูลพื้นฐานของโปรแกรมนี้ โดยมีเนื้อหาว่า

และอีกไฟล์ชื่อ MANUAL.md

ลองเช็คสถานะของโปรเจคนี้ดู จะได้ผลลัพท์ดังนี้

ซึ่งบอกว่า ทั้งไฟล์ hello.py ที่เพิ่งแก้ไขไป และไฟล์ MANUAL.md, README.md ที่เพิ่มมาใหม่นั้น จะไม่ถูกจำสำหรับการ commit ในครั้งถัดไป

ดังนั้น เราต้องเพิ่มไฟล์ทั้งสองเข้าไปในระบบก่อน โดยสั่ง add เหมือนตอนที่แล้วครับ

เช็คสถานะไฟล์อีกครั้ง

พร้อมแล้วก็สั่ง commit เลยครับ



แต่ตอนนี้เราจะเห็นว่า ไฟล์ MANUAL.md และ README.md นั้น มีเนื้อหาน้อยมาก และสามารถจับเอามารวมกันได้ แก้ไขไฟล์ README.md เป็นดังนี้

ถึงตอนนี้ เราจะไม่ต้องการไฟล์ MANUAL.md แล้ว ก็สั่งลบไฟล์ออกจากระบบโดย

จะเห็นว่าไฟล์ MANUAL.md หายไปจากไดเรคทอรี่เป็นที่เรียบร้อย ตอนนี้ก็ได้เวลาสั่ง commit งานที่เพิ่งแก้ไขกันครับ

อย่าลืมว่าก่อน commit ต้อง add ไฟล์ที่ถูกแก้ไขทุกครั้งนะครับ

PHP: Send mail

การใช้ PHP ส่งเมลนั้นทำได้ง่ายมาก อาจจะเป็นภาษาที่ส่งเมลได้ง่ายที่สุดในบรรดาภาษาคอมพิวเตอร์ที่มีอยู่ในโลก ฟังชั่นที่ใช้ส่งเมลคือ mail()
mail ( $to, $subject, $message [, $headers[ , $additional_param] ] )
พารามิเตอร์ที่จำเป็นมีอยู่ 3 ตัว และพารามิเตอร์เสริม 2 ตัว คือ
  • $to ที่อยู่อีเมลผู้รับ
  • $subject หัวข้อของอีเมล
  • $message เป็นเนื้อหาของอีเมล
  • $headers เป็นพารามิเตอร์เสริม เอาไว้กำหนดหัวเมลเพิ่มเติม
  • $additional_param เป็นพารามิเตอร์เสริม สำหรับส่งค่าให้กับโปรแกรมส่งเมล (ไม่ค่อยได้ใช้)
การขึ้นบรรทัดใหม่ในเมลนั้นจะใช้ \r\n (เหมือนการขึ้นบรรทัดใหม่ในโปรแกรมของฝั่ง Windows) แทนที่จะใช้ \n อย่างเดียว ไม่ว่าจะส่งจาก OS ที่เป็น Windows หรือ Linux แค่นี้ก็จบ

แต่ถ้าต้องการระบุอีเมลที่ใช้ส่ง รวมทั้งการใช้เนื้อหาในอีเมลเป็น HTML จะต้องใส่ $headers ลงไปด้วย ข้อจำกัดของอีเมลที่ควรรู้ไว้คือ
  • แนบ JavaScript ลงไปไม่ได้ (ที่จริงก็แนบได้ แต่โปรแกรมอ่านเมลทั้งหลายปิดการทำงานของ JavaScript ทิ้งเพื่อความปลอดภัย)
  • หากใช้ CSS จะต้องแทรกลงไปในเนื้อหาอีเมล ไม่สามารถแนบเป็นไฟล์ภายนอกไปได้
  • หากต้องการแทรกรูปภาพจะต้องใช้พาธเต็ม แต่หากไม่จำเป็นก็ไม่ควรจะใส่ลงไป

July 24, 2012

Git: มารู้จักกับโปรแกรมบริหารซอร์สกันเถอะ

1969 Linus Torvalds (คนเดียวกับผู้สร้าง Linux) เกิด
1972 กำเนิดโปรแกรม SCCS โปรแกรมจัดการซอร์สโปรแกรมแรกๆ
1990 กำเนิดโปรแกรม CVS
2000 กำเนิดโปรแกรม SVN
2005 Linus ให้กำเนิด Git



Git เป็นโปรแกรมบริหารจัดการซอร์สตัวหนึ่ง เนื่องจากในการพัฒนาโปรแกรมนั้น เราอาจมีการลองผิดลองถูกอยู่หลายทางว่าได้ผลลัพท์เป็นที่น่าพอใจหรือไม่ หรือบางครั้งฟีเจอร์ใหม่ๆ ที่เพิ่มเข้าไปอาจจะทำให้ระบบล่ม ต้องถอยกลับไปก่อนหน้าที่จะเกิดข้อผิดพลาดนั้น การใช้โปรแกรมบริหารซอร์สจะช่วยให้เราแก้ไขปัญหาต่างๆ ได้อย่างมีประสิทธิภาพมากขึ้น

โดย Git นั้นเป็นโปรแกรมบริหารซอร์สแบบกระจายศูนย์ ที่จะแจกซอร์สทั้งหมดให้ผู้ใช้ทุกคนเหมือนกัน ไม่แบ่งแยกชนชั้นครับ (ถ้าใครเป็น CVS/SVN มาก่อน ก็อาจถือได้ว่าต้องเรียนรู้ใหม่เกือบหมดเลยทีเดียว)

ผู้ใช้ Windows สามารถใช้ msysGit แทนได้ (โปรแกรมจะไปรันบน MinGW อีกที)

ส่วนผู้ใช้ Linux ก็เช่นเคย apt-get install git กันได้เลย

หลังจากติดตั้งโปรแกรมเรียบร้อยแล้ว ก็ต้องตั้งค่าเริ่มต้นที่จำเป็นกันหน่อย โดยสั่ง



ลองเริ่มใช้โปรแกรมเลยดีกว่า สมมติจะสร้างโปรเจค hello ก็สั่งดังนี้

Git จะสร้างไดเรกทอรี .git/ พร้อมไฟล์ระบบต่างๆ ของโปรเจคนี้ให้ครับ

คราวนี้มาเขียนโปรแกรมหลักของโปรเจคนี้กัน

ทดสอบแล้วรันได้ไม่มีปัญหา ก็ได้เวลาสั่งให้ Git จำ code เข้าระบบโดย

เท่านี้ก็เรียบร้อยครับ

ปล. โปรแกรมบริหารซอร์สแบบนี้ ใช้คนเดียวยังไงก็ไม่สนุก อย่างลืมชวนเพื่อนๆ มา code เล่นด้วยกันนะครับ

July 21, 2012

Python: เขียน TDD อย่างง่ายด้วย doctest

วิธีที่ง่ายที่สุดในการทำ TDD บน Python คือ ใช้ module doctest แล้วแนบคำสั่งที่ต้องการทดสอบพร้อมผลลัพท์ (เขียนรูปแบบเดียวกับการสั่งงาน Python shell) ลงไปที่ docstring ของฟังก์ชัน เช่นนี้

เซฟเป็นไฟล์ to_test.py แล้วเรียกโปรแกรมนี้ผ่าน terminal จะเห็นผลลัพท์ดังนี้

ซึ่งหมายความว่า test เราไม่ผ่านอยู่ 1 test case เมื่อส่งข้อความ 'World' ให้กับฟังก์ชั่น คราวนี้ลองปรับปรุงบรรทัดที่สั่ง print ให้เป็น

เมื่อสั่ง $ python to_test.py อีกรอบ จะไม่ได้ค่าอะไรคืนมาแล้ว ซึ่งก็คือ test ผ่านหมดนั่นเองครับ

หรือจะขอดูรายงานแบบเต็มๆ ก็ย่อมได้ โดยการเพิ่ม argument -v เข้าไปเช่นนี้




สำหรับส่วน docstring เราสามารถแนบคำอธิบายอื่นๆ ลงไปกับ test ได้ โดยต้องเว้นบรรทัดว่างเอาไว้เพื่อบอกให้รู้ว่าส่วนไหนที่หมด output จาก test แล้ว และสามารถเขียนส่วน input test หลายบรรทัดได้ สำหรับกรณี error สามารถใส่ ... ตามตัวอย่าง เพื่อบอกว่าไม่ต้องสนใจ output ส่วนที่เป็นรายงานอย่างละเอียดก็ได้ครับ

นอกจากนี้ ใน production code จริง การเรียกฟังก์ชัน doctest.testmod() ลงไปอาจไม่เหมาะสมนัก เราสามารถยกส่วนนั้นทิ้งไปได้ (เหลือแต่ code เปล่าๆ กับ docsting ที่มี test case อยู่) แล้วสั่งผ่าน shell เช่นนี้แทน



หรือถ้าส่วน test case นั้นใหญ่มากจนไม่ควรจะไปอยู่กับ docstring เราอาจยก test case มาเขียนเป็นไฟล์ใหม่ที่บรรจุเฉพาะข้อมูล test ไปเลยก็ได้ ดังนี้

ไฟล์ to_test.py


ไฟล์ info_test.txt

เรียกผ่าน shell ไม่ต่างจากเดิม เพียงแค่เปลี่ยน argument เป็นไฟล์ที่เขียน test เอาไว้เท่านั้นครับ

July 4, 2012

PHP: Session และสิ่งที่แตกต่างจาก Cookie

ในการเขียนโปรแกรมทั่ว ๆ ไป เราสามารถเก็บข้อมูลของผู้ใช้ เช่น ชื่อ ข้อมูลของแต่ละขั้นตอนที่ผู้ใช้ได้ทำลงไปในตัวแปรแบบปกติได้ แต่สำหรับการเขียนโปรแกรมสำหรับเว็บนั้นทำแบบนั้นไม่ได้ เพราะ URL ไม่สามารถบ่งบอกตัวผู้ใช้ และไม่สามารถเก็บข้อมูลตัวแปรได้ (ยกเว้นจะใช้ GET ซึ่งไม่ปลอดภัยสักเท่าไหร่ เพราะถ้ามีใครรู้ URL ก็สามารถปลอมตัวเป็นคน ๆ นั้นได้ มันคงไม่สนุก ถ้าเขาปลอมเป็นเรา แล้วโอนเงินในธนาคารออกไปจนหมด) PHP จึงสร้างตัวแปรพิเศษขึ้นมาคือ session

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

จะใช้ session ต้องประกาศก่อน และต้องทำก่อนที่จะส่งข้อมูลใด ๆ ออกมา ไม่ใช่นั้นจะเจอข้อความ headers already sent เวลาเรียกใช้ก็เรียกใช้เหมือนอาเรย์ปกติ ผ่านทางตัวแปร $_SESSION ซึ่งเป็นตัวแปรชนิด superglobal หากต้องการลบค่านั้นออกจาก session สามารถสั่ง unset ทีละตัวหรือทำลาย session นั้นทิ้งไปเลยก็ได้ แต่ถึงจะไม่ทำลาย ถ้าผู้ใช้ออกจากเว็บข้อมูลใน session ก็ถูกทำลายอยู่ดี โดยทั่วไปแล้ว session จะคล้าย ๆ กับ cookie คือเก็บข้อมูลของผู้ใช้ไว้เหมือน ๆ กัน ต่างกันตรงที่ว่า
  • cookie จะส่งข้อมูลนั้นกลับเป็นไฟล์ไปยังเบราว์เซอร์ และเก็บไว้ที่ฝั่งผู้ใช้ด้วย แต่ session ส่ง UID ของ session ไปอย่างเดียว ข้อมูลยังอยู่ฝั่งเซิฟเวอร์
  • cookie เก็บข้อมูลไว้เป็นไฟล์ข้อความธรรมดา สามารถเปิดขึ้นมาแก้ไขได้ หากรู้วิธี
  • cookie จะยังคงอยู่แม้ว่าจะออกจากเว็บไปแล้ว (เพราะเก็บอยู่บนเครื่องผู้ใช้) เมื่อกลับเข้ามาใหม่ ก็สามารถเรียกใช้ข้อมูลเหล่านั้นได้อยู่
  • หากต้องการเก็บข้อมูลใน session เอาไว้ ต้องเขียนลงไฟล์ หรือฐานข้อมูลเอาเอง

June 4, 2012

PHP: ว่าด้วยเรื่อง Cookie

Cookie

คุกกี้ใช้ในการอ้างถึงอัตลักษณ์ (identity) ของผู้ใช้ อาจจะเป็น username การตั้งค่าการแสดงผล หรือข้อมูลอื่น ๆ ที่ไม่จำเป็นต้องใช้ระบบสมาชิก

คุกกี้จะเป็นไฟล์ข้อความขนาดเล็กที่เก็บอยู่ฝั่ง client และถูกส่งไปยังเซิฟเวอร์ทุกครั้งที่มีการร้องขอ (request) หน้าเว็บ และ PHP สามารถ สร้าง เรียกใช้ เปลี่ยนค่า และลบทิ้งตัวแปรในคุกกี้ได้

การสร้างคุกกี้ใน PHP จะต้องสร้างก่อนที่จะส่ง output ใด ๆ ออกไป ไม่เช่นนั้นอาจเจอปัญหา headers already sent ได้
setcookie(string $name, string $value, int $expire = 0, string $path);
ด้านบนแสดงเฉพาะพารามิเตอร์ที่น่าสนใจ ถ้าต้องการดูพารามิเตอร์ทั้งหมดดูได้จาก function.setcookie

  • $name: ชื่อคุกกี้ เป็นพารามิเตอร์ที่จำเป็นตัวเดียวของฟังชั่นนี้
  • $value: ค่าของคุกกี้
  • $expire: อายุของคุกกี้ ถ้าไม่กำหนดไว้ค่าจะเป็น 0 ซึ่งจะหมดอายุทันทีที่เบราว์เซอร์ถูกปิด
  • $path: พาธบนเซิฟเวอร์ที่จะให้เข้าถึงคุกกี้ได้ หากไม่กำหนดจะเป็น / ซึ่งสคริปท์สามารถเข้าถึงได้ทั้งหมด ถ้าเป็น /board ก็เฉพาะสคริปท์ในไดเร็คทอรี่ /board เท่านั้นถึงจะเข้าถึงได้ เป็นต้น
การอ่านค่าจากคุกกี้ใช้ตัวแปร $_COOKIE ซึ่งเป็นตัวแปรชนิด superglobal ส่วนการเปลี่ยนค่าก็ใช้ setcookie() ใส่ค่าใหม่ลงไป ลบทิ้งก็ใส่ $expire ให้ติดลบ ส่วนใหญ่ที่เห็นจะใช้ -3600 (1 ชั่วโมงที่แล้ว) ตามคู่มือของ PHP แต่ทำไมถึงใช้ค่านี้ อันนี้ผมไม่รู้นะ

ในส่วนของฝั่งผู้ใช้จะทำผ่าน JavaScript ผู้ใช้แก้ไขโดยตรงไม่ได้ (ยกเว้นจะใช้โปรแกรมแก้ไขคุกกี้)
document.cookie = "cookie_name=cookie_value; expires=expire_time; path=usage_path";
แต่การสั่งแบบนี้ไม่ค่อยสะดวกนัก แนะนำให้สร้างฟังชั่นขี้นมาใช้แทน อายุของคุกกี้จะขึ้นอยู่กับค่า expires หากยังไม่หมดอายุ ถึงจะปิดเบราว์เซอร์ไปแล้วคุกกี้ก็ยังอยู่ (แต่ถ้าสั่งเคลียร์คุกกี้ผ่านทางเบราว์เซอร์มันก็จะหายไปได้นะ)

May 28, 2012

vi: ตั้งค่าให้ highlight ตามภาษาที่กำหนด

ในบาง project เราอาจได้เจอไฟล์ที่ลงท้ายด้วยนามสกุลใน format ของตัวเอง หรือบางทีก็ต้องการเปลี่ยนไป highlight ในภาษาอื่นๆ สามารถใช้คำสั่งเช่น

เพื่อเปลี่ยนการ highlight จากภาษา C ไปเป็นภาษา Python ได้

แต่การสั่งตามข้างบนนี้ จะมีผลเพียงแค่การแก้ไขเอกสารครั้งนั้นๆ ถ้าต้องการให้มีผลไปตลอด ให้เปิดไฟล์ .vimrc มาแล้วเพิ่มบรรทัดนี้เข้าไป

โดยเปลี่ยนส่วน *.ext เป็นชื่อไฟล์ที่ต้องการกำหนด highlight และเปลี่ยน language เป็นภาษาที่ต้องการ เช่นจากข้างต้นจะได้

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 แทนครับ