Planet TLWG

Subscribe to Planet TLWG feed
Planet TLWG - http://debianclub.org/planet-tlwg
Updated: 1 hour 20 min ago

Kitt: ceph

2 July, 2016 - 12:06
วันนี้ ceph เดี้ยง สืบพบว่ามาจาก monitor node 2 ใน 3 ตัว ..  ทั้งสองตัว เป็น VM connect ได้ daemon running  แต่ disk ของตัว host เป็น read-only .. พอ monitor พยายามบันทึกข้อมูลลง disk ไม่ได้มันก็ค้าง พอ monitor ค้าง ceph cluster ทั้งก้อนหยุดทำงาน ได้ใช้ท่า troubleshooting: stop monitor / dump monmap / ลบ monitor node ที่ใช้งานไม่ได้ออกไปจาก monmap / inject monmap ตัวใหม่เข้า monitor node ที่ใช้งานได้ restart monitor … Continue reading ceph →

Kitt: Password authentication must die .. soon.

30 June, 2016 - 14:39
Password authentication depends on user input. To make it safe, one of the requirement is that you need to do it safely and quickly enough and hope that nobody could catch what you type on the keyboard. Nowadays, we can’t hope such. With naked eyes, we can simply read gestures, types, presses most of people … Continue reading Password authentication must die .. soon. →

Thep: LibThai 0.1.25 : More on Thread-safety

29 June, 2016 - 12:08

LibThai 0.1.25 ออกแล้ว ความเปลี่ยนแปลงหลักของรุ่นนี้อยู่ที่เรื่อง API ใหม่ที่ thread-safe กว่าเดิม และเรื่องย่อย ๆ คือการแก้ปัญหาการคอมไพล์ด้วย GCC 6 และการปรับพจนานุกรมตัดคำตามปกติ

Thread Safety

ในรุ่น 0.1.23 ได้ทำเรื่อง thread safety ไปแล้วส่วนหนึ่ง จากประเด็นที่พบใน Pango เมื่อมีหลายเธรดพยายามเรียกฟังก์ชันตัดคำพร้อมกัน ทำให้เกิดการแย่งใช้ free list ดังที่เคยอธิบายไว้ใน blog เก่า แต่ก็ยังแก้ไม่หมดจดพอ ดังที่คุณ Mark Brown ได้รายงานมาใน กลุ่มเมล Thai Linux/FOSS developers ว่ายังเหลืออีกจุดหนึ่ง คือขณะเปิดพจนานุกรมเป็นการภายในในการเรียกครั้งแรก เพราะจะยังมีการแย่งกันเปิดพจนานุกรมจนเกิดออบเจกต์พจนานุกรมหลายชุด แม้สุดท้ายจะใช้งานแค่ชุดเดียวและโปรแกรมก็ไม่แครช แต่ออบเจกต์ชุดที่เหลือก็เปลืองเนื้อที่ในหน่วยความจำ และจะไม่ถูกทำลายเมื่อจบโปรแกรมอีกด้วย

วิธีแก้ปัญหาได้พัฒนามาเป็นขั้นเป็นตอนดังนี้ :-

  1. ใช้ mutex ขณะเปิดพจนานุกรม เพื่อให้มีเพียงเธรดเดียวที่เปิด เธรดที่เหลือแค่รอใช้ แต่ปัญหาคือ mutex ของแต่ละ OS จะเรียกไม่เหมือนกัน (บน Linux และ Unix-like OS ทั้งหลาย ใช้ POSIX thread ส่วนวินโดวส์ใช้ Mutex Object ของตัวเอง แม้จะมี pthreads-win32 เป็น wrapper ให้ใช้ แต่ก็ยังต้องสร้างระบบ build และทดสอบขึ้นมาอีก) การจะใช้ mutex ใน libthai จะต้องสร้าง layer ใหม่เพื่อให้ยังคงทำงานข้ามแพลตฟอร์มได้ เป็นงานที่ใหญ่พอสมควรเมื่อเทียบกับปัญหาที่แก้
  2. แยกฟังก์ชันเปิดพจนานุกรมออกมาต่างหาก เป็นแนวคิดที่คุณ Mark Brown ปิ๊งขึ้นมาระหว่างทำอีกประเด็นหนึ่ง คือ การอนุญาตให้ระบุแฟ้มพจนานุกรมที่จะโหลด สำหรับใช้ในกรณีที่ผู้ใช้ไม่ต้องการใช้พจนานุกรมมาตรฐานของ libthai ซึ่งเมื่อกำหนดฟังก์ชันนี้ขึ้นมาแล้ว ก็จะเกิดขั้นตอนใหม่เพิ่มขึ้นก่อนที่ผู้ใช้จะตัดคำ คือการเช็กและเปิดพจนานุกรม ซึ่งผู้ใช้สามารถเรียกใช้ใน critical region ที่มีการล็อคด้วย mutex เองได้ กลายเป็นการยิงปืนนัดเดียวได้นกสองตัว ดังที่คุณ Mark Brown ได้อธิบายมาใน อีกกระทู้หนึ่ง
  3. กำหนด type ThBrk ผมชอบแนวคิดของคุณ Mark Brown ที่ทำให้สามารถเลี่ยงการสร้าง portability layer เพิ่มได้ จึงได้ generalize ออกมาเป็น API ชุดใหม่ คือให้ผู้ใช้สร้างออบเจกต์ชนิด ThBrk (ซึ่งภายในเก็บพจนานุกรมที่เปิดแล้ว) ภายใต้การปกป้องด้วย mutex ในตอนต้น แล้วจึงเรียกฟังก์ชันตัดคำต่าง ๆ แบบขนานตามต้องการ ดังที่ผมได้แสดงความเห็นต่อมาในกระทู้ดังกล่าว คลาส ThBrk นี้ สามารถซ่อนรายละเอียดเพื่อรองรับ implementation แบบอื่นในอนาคตได้ถ้าต้องการ เช่น การใช้สถิติ tri-gram ฯลฯ แต่ในขณะนี้เราจะใช้พจนานุกรมเพียงอย่างเดียวก่อน

API นี้มีการเปลี่ยนชื่อไปมาเพื่อความชัดเจน ในแบบสุดท้าย สรุปว่าตัวอย่างการใช้งานจะเป็นแบบนี้ :-

  // mutex lock here
  ThBrk *brk = th_brk_new (dictpath);
  // mutex unlock here

  // works in parallel
  int pos[N];
  int res = th_brk_find_breaks (brk, str, pos, N);

  // mutex lock here
  th_brk_delete (brk);
  // mutex unlock here

API เดิมจะยังคงมีอยู่เพื่อ backward compatibility โดยจะเป็น wrapper ที่แอบเปิดพจนานุกรมเป็นการภายในเช่นเดิม (ซึ่งไม่ thread-safe) ก่อนเรียก API ชุดใหม่ แต่จะเริ่ม deprecate API เก่าตั้งแต่รุ่นนี้เป็นต้นไป เพื่อให้ผู้ใช้เปลี่ยนไปใช้ API ชุดใหม่แทน ดังนี้ :-

  • th_brk() ให้ใช้ th_brk_find_breaks() แทน
  • th_brk_line() ให้ใช้ th_brk_insert_breaks() แทน
  • th_wbrk() ให้ใช้ th_brk_wc_find_breaks() แทน
  • th_wbrk_line() ให้ใช้ th_brk_wc_insert_breaks() แทน

โดยผู้ใช้ต้องสร้างออบเจกต์ชนิด ThBrk ต่างหากเพื่อส่งให้ฟังก์ชันเหล่านี้ และทำลายเมื่อใช้งานเสร็จ

GCC 6

การปรับโค้ดอีกส่วนหนึ่งเป็นส่วนที่สะสมมาตั้งแต่ช่วงที่ Debian กำหนดให้ใช้ GCC 6 เป็นรุ่น default ที่จะใช้ใน Stretch และได้รับรายงานจากคุณ Martin Michlmayr ใน Debian #811690 ว่าแพกเกจ scim-thai มีปัญหาในการคอมไพล์ด้วย GCC 6 ซึ่งต้นเหตุอยู่ที่ header file หนึ่งของ libthai จึงปรับแก้เสีย

Word Break Dictionary

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

คำที่น่าสนใจที่เพิ่มมาในรุ่นนี้:

  • กรังด์ปรีซ์ : จากดราม่าวอลเลย์บอลหญิงไทย-ญี่ปุ่น
  • สไตรีน, โมโนเมอร์ : จากกรณีถกเถียงเรื่องการใช้กล่องโฟมบรรจุอาหาร
  • ทรัมป์ : จากการเมือง USA ในช่วงนี้
  • แอมเฟตามีน, ปุ๊น : จากข่าวการถอดยาบ้าออกจากบัญชียาเสพติดร้ายแรง
  • สเตอร์ลิง, เบรกซิท : จากข่าวประชามติ UK ถอนตัวจาก EU

และเช่นเคย ส่งเข้า Debian เรียบร้อยแล้วครับ

bact: On Thailand’s new cybercrime law amendment (26 April 2016)

14 June, 2016 - 16:35

Quick points for my international friends who want to get some gists about the development of Thailand’s new amendment of Computer-related Crime Act, as of 4 June 2016. Here I discussed the timeline, small notes on two different revisions on April 2016, and points of concerns regarding freedom of expression, privacy, and encryption.

If you don’t have much time, look at Section 14 (1) [online defamation], 14 (2) [“public safety” and “economic stability”], 15 last para [burden of proof to the intermediary], 17/1 [Settlement Commission], 18 (7) [investigative power to access encrypted data-at-rest], 20 (4) [Computer Data Screening Committee can block content that is totally legal], and 20 last para [will be used to circumvent data-in-transit encryption].

Timeline
  • 26 April 2016 — The Cabinet submitted the Bill to National Legislative Assembly (NLA)
  • 28 April — NLA 1st hearing – approved the Bill in principles (160 to 0) and sent it to Review Subcommittee. The subcommittee has 60 days to review, with possible 30 days extension if needed.
  • ~27 June — NLA will received the revised Bill and continue for the 2nd hearing.
  • There will be three hearings in NLA. (In some cases, all the 1st, 2nd and 3rd hearings could be done in one day.)
  • ICT Minister said the government willing to have all Digital Bills in effect by the end of 2016
Read the Bill (in English)
  • Download Computer-related Crime Act (CCA) amendment draft and its English translation
  • Note that we have two revisions in April 2016: one from 19 April (one that submitted to Cabinet) and from 26 April (to NLA)
  • English translation is from 19 April 2016
  • Both are almost identical
    • 26 April rev merged Section 13 and 14 which both amend Section 18 into one single Section.
    • This means the following running Section numbers from Section 15 are shifting up. (for example, Section 15 [which amends Section 20] in 19 April revision is become Section 14 [amends Section 20] in 26 April revision)
    • To reduce confusion, we will refer here to the number of Section to be amended.
  • The Minister responsible for this Act will be the Minister of Digital Economy and Society (new name of Minister of ICT)
Points of Concerns Criminalisation of Speech and Computer Data
  • Section 14 (1) — Online defamation: language still open for online defamation
  • Section 14 (2) — Vague and general terms like “public safety” and “economic stability” in this Section are undefined specifically in any Thai criminal law, but will be used to criminalised computer data. From our communication with the lawmaker, they said this is meant to be for the crime against computer system of public infrastructure. If that’s the case, it can be better written in the draft, using terms like “critical infrastructure” or “critical information infrastructure”.
  • Read iLaw analysis on this (in Thai)
Intermediary Liability — Burden of Proof
  • Section 15 second paragraph — Blanket power: Minister power to issue additional procedural rules, which may additionally limits civil rights but require no review from Parliament
  • Section 15 third paragraph — Burden of proof: if service provider follows the Ministerial procedural rules, they may exempted from penalty, but service provider has to prove their innocence
Unpredicability of Law — Judicial Process
  • Section 17/1 — Settlement Committee: for offences with 2 years or less jail term. Not sure what is the consequence, but definitely will creates unpredictability of law enforcement
    • The Settlement Committee will be appointed by the Minister. It will be consisted of three persons, one of whom has to be an inquiry official according to the Criminal Procedure Code. No other requirements stated in the Bill.
Expanded Investigative Power, including access to encrypted Data-at-Rest
  • Section 18 — Expands investigative power of Section 18 to non-CCA offences
  • Section 18 (7) is about accessing encrypted computer system or data
  • (The entire Section 18 in 26 April 2016 revision of CCA amendment is almost identical to the 2007 CCA currently in use.)
Expanded Information Control
  • Section 20 — Expands blocking and data removal power to non-CCA offences
  • Section 20 (4) — Computer Data Screening Committee may ask Court the block data that breach “public order” or “moral high ground of people” even its not illegal (Thai Journalist Association also very concerned about this)
    • The Computer Data Screening Committee will be appointed by Minister. Will consisted of five persons. Two must come from relevant private sector. No other requirements stated in the Bill.
Surveillance of Encrypted Communication
  • Section 20 fifth paragraph — Minister can issue additional rules to facilitate data blocking/removal “in response to changing technology”
    • In the “reasons for amendment” document attached with the draft submitted to NLA, it said to block a web page that use public-key encryption a “special method and tools” are needed (see page 28-30 of the documents submitted to NLA, in the last column. You will see the keywords like “SSL”.)
  • Read Thai Netizen Network analysis on this Section 20 and how it related to MICT “Single Gateway” project (in Thai)
Petition to Stop Information Control and Surveillance

If you don’t agree with the Amendment proposal, please sign the Petition here: https://change.org/singlegatewayreturn

Thep: Fonts-TLWG 0.6.3

14 June, 2016 - 12:47

Fonts-TLWG 0.6.3 ออกแล้ว เมื่ออาทิตย์ที่แล้ว แต่เพิ่งจะได้เขียน blog บันทึกหลังจากที่เตรียมแพกเกจเพื่ออัปโหลดในที่ต่าง ๆ เสร็จ คือที่ Debian และ CTAN

รุ่นนี้เป็นรุ่นแรกที่ ออกรุ่นจาก GitHub หลังจากที่ ประกาศย้าย repository ของ TLWG ไปที่ GitHub เมื่อเดือนที่แล้ว แต่ยังคงใช้ linux.thai.net เป็นที่ประกาศหลักตามเดิม เพื่อความต่อเนื่องกับรุ่นก่อน ๆ

ความเปลี่ยนแปลงหลักที่เกิดขึ้นในรุ่นนี้ คือการเปลี่ยนให้ฟอนต์ Loma เป็นฟอนต์ UI หลักแทน Waree อันเนื่องมาจาก รายงานบั๊กของพี่สัมพันธ์ พร้อม follow-up ว่าฟอนต์ Waree นั้นตัวสูงเกินไปจนถูกขริบในบางเว็บ เช่น Facebook แต่ Loma นั้นเตี้ยพอที่จะเล็ดรอดมาได้ (เหตุผลก็คือ Waree นั้นออกแบบเพื่อเตรียมเพิ่มอักษรไทยให้กับฟอนต์ DejaVu Sans จึงมี glyph ละตินของ DejaVu Sans ซึ่งตัวสูงอยู่แล้วเป็นตัวตั้ง ส่วน Loma นั้น เข้าใจว่าออกแบบโดยอิสระของตัวเอง) หลังจากทดสอบและฟังความเห็นของหลาย ๆ ท่านที่มาคอมเมนต์ ก็เห็นว่าควรเลื่อนอันดับของฟอนต์ Loma ขึ้นมาสูงกว่า Waree ในการเลือกฟอนต์ sans-serif ของ fontconfig

ในการเลื่อนขั้นนั้น ตามหลักแล้วควรจะทำได้ง่าย ๆ ด้วยการเปลี่ยนลำดับแฟ้ม config ของ fontconfig ให้ Loma ขึ้นก่อน Waree เช่น เปลี่ยนชื่อแฟ้ม /etc/fonts/conf.d/64-12-tlwg-loma.conf ในรุ่น 0.6.2 ให้เป็น 64-10-tlwg-loma.conf แต่ทำแค่นั้นไม่ได้ช่วยให้ Loma มาก่อน Waree ได้ เพราะยังมีกฎชุด synthetic อีกชุดหนึ่งเข้ามามีส่วนด้วย ดังผลลัพธ์หลังเปลี่ยนชื่อแฟ้มดังกล่าว ก็ยังคง match sans-serif ได้ Waree เช่นเดิม (ในรุ่น 0.6.2):

$ cd /etc/fonts/conf.d
$ sudo mv 64-12-tlwg-loma.conf 64-10-tlwg-loma.conf
$ fc-match sans-serif
Waree.otf: "Waree" "Regular"

สาเหตุคือฟอนต์ Waree มีการจำลองตัวเองเพื่อทดแทนฟอนต์ Tahoma ด้วยการห้อยชื่อตัวเองในลำดับต่อจาก Tahoma (ในไฟล์ 89-tlwg-waree-synthetic.conf) แต่ด้วยความที่ Tahoma ได้ถูกกำหนดให้เป็น preferred font ตัวหนึ่งของละติน (ในไฟล์ 60-latin.conf) ซึ่งมาก่อนภาษาอื่น ๆ การจำลอง Tahoma ของ Waree จึงทำให้ Waree กลายเป็นฟอนต์ละตินตัวหนึ่งที่มาก่อนภาษาอื่น ๆ ไปด้วย!

รายละเอียดทางเทคนิคของการตรวจสอบ ผู้ที่สนใจสามารถอ่านได้ที่ส่วนท้ายของ blog

ฉะนั้น การจะเลื่อนขั้น Loma ขึ้น จึงติดที่กฎชุด synthetic ของ Waree นี้

แล้วกฎ synthetic นี้มาจากไหน? มันเริ่มมาจากแนวคิดการจำลองฟอนต์ที่ผู้ใช้นิยมใช้ในเอกสารต่าง ๆ บนวินโดวส์ด้วยฟอนต์ที่มีในชุด Fonts-TLWG เช่น จำลอง Angsana ด้วย Kinnari, จำลอง Browallia ด้วย Garuda ฯลฯ ดังมีบันทึกไว้ใน blog เก่าเมื่อปี 2550 ซึ่งฟอนต์ต่าง ๆ ก็มีเค้าหน้าตาให้จำลองกันได้ จนกระทั่งมาเจอกรณีการใช้ฟอนต์ MS Sans Serif และ Tahoma ในเว็บ จึงได้พยายามทดแทนด้วยฟอนต์ Loma และ Waree ตามลำดับ ดังอ้างถึงใน blog เก่าเมื่อปี 2552 โดยที่ทั้งสองฟอนต์นี้ไม่ได้มีเค้าของฟอนต์ที่จำลองเลย เพียงแต่ต้องการอุดช่องว่างของการแสดงหน้าเว็บเท่านั้น

กฎนี้เคยถูกรายงานว่าสร้างปัญหาให้กับภาษาอื่น เพราะ Waree จะโผล่มาแทน Tahoma ทั้ง ๆ ที่ตัวใหญ่กว่า Tahoma (LP #434054) ทำให้เคยตัดกฏนี้ออกไปชั่วระยะหนึ่ง แต่ก็เพิ่มกลับเข้ามาพร้อมกับการตรวจสอบภาษาเพิ่มเติมเพื่อให้ apply กับภาษาไทยเท่านั้น (LP #539008) ดังบันทึกใน thaifonts-scalable 0.4.14 แต่ในเมื่อมันมารบกวนการจัดอันดับฟอนต์ sans-serif ไทยทั้งระบบ ก็เห็นควรว่าควรตัดกฎ synthetic นี้ออก แล้วปล่อยให้ฟอนต์ sans-serif ที่ได้อันดับสูงสุดมาอุด Tahoma เอา

เมื่อตัดกฎ synthetic ออก ฟอนต์ Loma ก็ได้อันดับสูงกว่า Waree แล้ว

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

ก่อนปรับ:

หลังปรับ:

ต่อไปนี้เป็นวิธีตรวจสอบการ apply กฎของ fontconfig ด้วยตัวเอง ซึ่งทำให้เห็นว่ากฎ synthetic ใน 0.6.2 ทำให้ Waree ล้ำหน้าฟอนต์ไทยอื่น ๆ ได้อย่างไร

วิธีตรวจสอบตามที่ เอกสาร fontconfig ได้อธิบายไว้ คือกำหนดตัวแปร environment FC_DEBUG โดยในที่นี้เราสนใจการ edit match pattern ในกฎต่าง ๆ ก็กำหนดค่าเป็น 4 แล้วเรียก fc-match ดังนี้:

$ FC_DEBUG=4 fc-match sans-serif

มันจะพ่นข้อความแสดงการ edit match pattern ในแต่ละขั้นออกมายืดยาว ก็อาจจะ redirect ลงแฟ้มแล้วมาตรวจสอบดู ก็จะพบขั้นตอนที่น่าสนใจคือ:

...

FcConfigSubstitute test pattern any family Equal(ignore blanks) "sans-serif"
Substitute Edit family Prepend "Bitstream Vera Sans" Comma "DejaVu Sans" Comma "
Verdana" Comma "Arial" Comma "Albany AMT" Comma "Luxi Sans" Comma "Nimbus Sans L
" Comma "Helvetica" Comma "Lucida Sans Unicode" Comma "BPG Glaho International" 
Comma "Tahoma"

Prepend list before  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) 
[marker] "sans-serif"(s) "sans-serif"(w)
Prepend list after  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) "
Bitstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w)
 "Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG 
Glaho International"(w) "Tahoma"(w) "sans-serif"(s) "sans-serif"(w)

...

กฎนี้มาจาก 60-latin.conf ของ fontconfig เอง ที่เพิ่ม prefer list ของ sans-serif โดยมี Tahoma พ่วงอยู่ด้วย

จากนั้น ก็มีการ edit match pattern ต่อ ๆ มา จนมาถึงตรงนี้:

...

FcConfigSubstitute test pattern any family Equal(ignore blanks) "sans-serif"
Substitute Edit family Prepend "Loma"

Prepend list before  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) 
"Bitstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w
) "Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG
 Glaho International"(w) "Tahoma"(w) [marker] "sans-serif"(s) "sans-serif"(w)
Prepend list after  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) "
Bitstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w)
 "Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG 
Glaho International"(w) "Tahoma"(w) "Loma"(w) "sans-serif"(s) "sans-serif"(w)

...

FcConfigSubstitute test pattern any family Equal(ignore blanks) "sans-serif"
Substitute Edit family Prepend "Waree"

Prepend list before  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) 
"Bitstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w
) "Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG
 Glaho International"(w) "Tahoma"(w) "Loma"(w) [marker] "sans-serif"(s) "sans-se
rif"(w)
Prepend list after  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) "
Bitstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w)
 "Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG 
Glaho International"(w) "Tahoma"(w) "Loma"(w) "Waree"(w) "sans-serif"(s) "sans-s
erif"(w)

...

ซึ่งก็ดูเรียบร้อยดี Loma น่าจะ match ก่อน Waree แล้ว จนกระทั่งมาถึงตรงนี้:

...

FcConfigSubstitute test pattern any lang Contains "th"
FcConfigSubstitute test pattern any family Equal "Tahoma"
Substitute Edit family Append "Waree"

Append list before  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) "
Bitstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w)
 "Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG 
Glaho International"(w) "Tahoma"(w) [marker] "Loma"(w) "Waree"(w) "Garuda"(w) "U
mpush"(w) "Laksaman"(w) "Meera"(w) "Khmer OS"(w) "Nachlieli"(w) "Lucida Sans Uni
code"(w) "Yudit Unicode"(w) "Kerkis"(w) "ArmNet Helvetica"(w) "Artsounk"(w) "BPG
 UTF8 M"(w) "Waree"(w) "Loma"(w) "Garuda"(w) "Umpush"(w) "Saysettha Unicode"(w) 
"JG Lao Old Arial"(w) "GF Zemen Unicode"(w) "Pigiarniq"(w) "B Davat"(w) "B Comps
et"(w) "Kacst-Qr"(w) "Urdu Nastaliq Unicode"(w) "Raghindi"(w) "Mukti Narrow"(w) 
"padmaa"(w) "Hapax Berbère"(w) "MS Gothic"(w) "UmePlus P Gothic"(w) "SimSun"(w) 
"PMingLiu"(w) "WenQuanYi Zen Hei"(w) "WenQuanYi Bitmap Song"(w) "AR PL ShanHeiSu
n Uni"(w) "AR PL New Sung"(w) "MgOpen Moderna"(w) "MgOpen Modata"(w) "MgOpen Cos
metica"(w) "VL Gothic"(w) "IPAMonaGothic"(w) "IPAGothic"(w) "Sazanami Gothic"(w)
 "Kochi Gothic"(w) "AR PL KaitiM GB"(w) "AR PL KaitiM Big5"(w) "AR PL ShanHeiSun
 Uni"(w) "AR PL SungtiL GB"(w) "AR PL Mingti2L Big5"(w) "MS ゴシック"(w) "ZYSo
ng18030"(w) "NanumGothic"(w) "UnDotum"(w) "Baekmuk Dotum"(w) "Baekmuk Gulim"(w) 
"KacstQura"(w) "Lohit Bengali"(w) "Lohit Gujarati"(w) "Lohit Hindi"(w) "Lohit Ma
rathi"(w) "Lohit Maithili"(w) "Lohit Kashmiri"(w) "Lohit Konkani"(w) "Lohit Nepa
li"(w) "Lohit Sindhi"(w) "Lohit Punjabi"(w) "Lohit Tamil"(w) "Meera"(w) "Lohit M
alayalam"(w) "Lohit Kannada"(w) "Lohit Telugu"(w) "Lohit Oriya"(w) "LKLUG"(w) "F
reeSans"(w) "Arial Unicode MS"(w) "Arial Unicode"(w) "Code2000"(w) "Code2001"(w)
 "sans-serif"(s) "Arundina Sans"(w) "Roya"(w) "Koodak"(w) "Terafik"(w) "sans-ser
if"(w) "sans-serif"(w) "sans-serif"(w) "sans-serif"(w) "sans-serif"(w) "sans-ser
if"(w) "sans-serif"(w) "sans-serif"(w)
Append list after  "DejaVu Sans"(w) "DejaVu LGC Sans"(w) "DejaVu LGC Sans"(w) "B
itstream Vera Sans"(w) "DejaVu Sans"(w) "Verdana"(w) "Arial"(w) "Albany AMT"(w) 
"Luxi Sans"(w) "Nimbus Sans L"(w) "Helvetica"(w) "Lucida Sans Unicode"(w) "BPG G
laho International"(w) "Tahoma"(w) "Waree"(w) "Loma"(w) "Waree"(w) "Garuda"(w) "
Umpush"(w) "Laksaman"(w) "Meera"(w) "Khmer OS"(w) "Nachlieli"(w) "Lucida Sans Un
icode"(w) "Yudit Unicode"(w) "Kerkis"(w) "ArmNet Helvetica"(w) "Artsounk"(w) "BP
G UTF8 M"(w) "Waree"(w) "Loma"(w) "Garuda"(w) "Umpush"(w) "Saysettha Unicode"(w)
 "JG Lao Old Arial"(w) "GF Zemen Unicode"(w) "Pigiarniq"(w) "B Davat"(w) "B Comp
set"(w) "Kacst-Qr"(w) "Urdu Nastaliq Unicode"(w) "Raghindi"(w) "Mukti Narrow"(w)
 "padmaa"(w) "Hapax Berbère"(w) "MS Gothic"(w) "UmePlus P Gothic"(w) "SimSun"(w)
 "PMingLiu"(w) "WenQuanYi Zen Hei"(w) "WenQuanYi Bitmap Song"(w) "AR PL ShanHeiS
un Uni"(w) "AR PL New Sung"(w) "MgOpen Moderna"(w) "MgOpen Modata"(w) "MgOpen Co
smetica"(w) "VL Gothic"(w) "IPAMonaGothic"(w) "IPAGothic"(w) "Sazanami Gothic"(w
) "Kochi Gothic"(w) "AR PL KaitiM GB"(w) "AR PL KaitiM Big5"(w) "AR PL ShanHeiSu
n Uni"(w) "AR PL SungtiL GB"(w) "AR PL Mingti2L Big5"(w) "MS ゴシック"(w) "ZYS
ong18030"(w) "NanumGothic"(w) "UnDotum"(w) "Baekmuk Dotum"(w) "Baekmuk Gulim"(w)
 "KacstQura"(w) "Lohit Bengali"(w) "Lohit Gujarati"(w) "Lohit Hindi"(w) "Lohit M
arathi"(w) "Lohit Maithili"(w) "Lohit Kashmiri"(w) "Lohit Konkani"(w) "Lohit Nep
ali"(w) "Lohit Sindhi"(w) "Lohit Punjabi"(w) "Lohit Tamil"(w) "Meera"(w) "Lohit 
Malayalam"(w) "Lohit Kannada"(w) "Lohit Telugu"(w) "Lohit Oriya"(w) "LKLUG"(w) "
FreeSans"(w) "Arial Unicode MS"(w) "Arial Unicode"(w) "Code2000"(w) "Code2001"(w
) "sans-serif"(s) "Arundina Sans"(w) "Roya"(w) "Koodak"(w) "Terafik"(w) "sans-se
rif"(w) "sans-serif"(w) "sans-serif"(w) "sans-serif"(w) "sans-serif"(w) "sans-se
rif"(w) "sans-serif"(w) "sans-serif"(w)

...

กลายเป็นว่า Waree กลับมาแซงหน้า Loma อีกครั้งหลังจากกฎใน 89-tlwg-waree-synthetic.conf ผลก็คือ ไม่ว่าฟอนต์ไทยตัวไหนจะพยายามลิสต์ตัวเองขึ้นก่อนอย่างไรก็ตาม ก็จะแพ้ฟอนต์ที่ fallback ให้ Tahoma อยู่วันยังค่ำ และการสังเคราะห์ฟอนต์ Tahoma ด้วย Waree ก็ทำให้ Waree ไม่ยอมลงให้กับใคร!

Kitt: Add new disk to Linux VM without rebooting

6 June, 2016 - 09:18
You can always do this. Edit setting of VM hardware, add a SCSI new disk In Linux VM, # echo "- - -" > /sys/class/scsi_host/host#/scan where host# can be host0, host1 … Linux will re-scan SCSI, and your new disk should be recognized.

bact: เสรีภาพการแสดงออกในมหาวิทยาลัยสหราชอาณาจักร

5 June, 2016 - 21:49

Gary Slapper ผู้อำนวยการของมหาวิทยาลัยนิวยอร์ก วิทยาเขตลอนดอน เขียนถึงประเด็นเสรีภาพการแสดงออกที่ถูกจำกัดในมหาวิทยาลัยในสหราชอาณาจักร โดยอ้างอิงรายงาน Free Speech University Rankings ที่จัดอันดับเสรีภาพในการพูดของมหาวิทยาลัยยูเค 115 แห่งเป็นปีที่สองแล้ว และในรายงานปีที่สองนี้ พบมหาวิทยาลัยที่มีการเซ็นเซอร์การแสดงออก 90% เพิ่มขึ้นจากปีที่แล้วที่มี 80%

เขายกตัวอย่าง LSE ที่มีปัญหานี้มาก จนนักศึกษาต้องตั้งสมาคม Speakeasy Society ขึ้นมาเพื่อสนับสนุนการโต้เถียงอย่างเสรีและเปิดกว้าง แต่ไม่นานก็มีการเสนอในที่ประชุมของสหภาพนักศึกษา LSE Student Union ว่าให้แบนสมาคมนี้ซะ สุดท้ายก็รอดไป มีคนโหวตให้แบน 57 เสียง โหวตว่าไม่แบน 226 เสียง ได้อยู่ต่อ

ปัญหาอันหนึ่งก็คือ การยกข้องอ้างเรื่องการหมิ่นศาสนาหรือคำพูดที่สร้างความเกลียดชัง ขึ้นมาเพื่อห้ามหรือจำกัดการแสดงออก

ปัจจุบันกฎหมายที่เกี่ยวกับเรื่องนี้โดยตรงคือ Racial and Religious Hatred Act 2006 (แก้ไขเพิ่มเติม Public Order Act 1986) ซึ่ง Gary ก็บอกว่า ในกฎหมายก็เขียนไว้อย่างชัดเจนว่าไม่ได้ห้ามการวิพากษ์วิจารณ์ หรือกระทั่งประชดเสียดสีเยาะเย้ย (ridicule) ศาสนา ความเชื่อ และวิธีปฏิบัติของผู้ที่มีความเชื่อเหล่านั้น

กฎหมายเขาว่างี้

Part 3A — Hatred against persons on religious grounds

29A — Meaning of “religious hatred”

In this Part “religious hatred” means hatred against a group of persons defined by reference to religious belief or lack of religious belief.

[…]

29J — Protection of freedom of expression

Nothing in this Part shall be read or given effect in a way which prohibits or restricts discussion, criticism or expressions of antipathy, dislike, ridicule, insult or abuse of particular religions or the beliefs or practices of their adherents, or of any other belief system or the beliefs or practices of its adherents, or proselytising or urging adherents of a different religion or belief system to cease practising their religion or belief system.

กล่าวคือความหมายของ “ความเกลียดชังทางศาสนา” นี่ มันต้องเป็นความเกลียดชังต่อกลุ่มบุคคล ซึ่งอ้างอิงกับความเชื่อทางศาสนา (29A) สิ่งที่เขาต้องการปกป้อง คือปกป้องคน ส่วนการจะไปวิพากษ์วิจารณ์หรือเอาตัวศาสนาความเชื่อมาล้อเลียนอะไรนั้น เป็นเรื่องที่กฎหมายไม่ได้ห้าม และเขียนย้ำไว้เลยในกฎหมายว่า ไม่ได้ห้ามนะ (29J)

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

เขาปิดท้ายบทความว่า คำว่า “university” ในภาษาอังกฤษนั้น มาจากคำละตินว่า “universtas” ซึ่งแปลว่า “ทั้งหมด จำนวนทั้งหมด ผลรวมทั้งหมดของสรรพสิ่ง” มหาวิทยาลัยควรจะเป็นที่ถกเถียงของแนวคิดทุกอย่าง ไม่ใช่แค่บางอย่าง

อ่านบทความเต็มที่ Milkround “The best way to defeat harmful ideas is by debate — not a ban”

ภาพจาก Wikimedia Commons

LookHin: 10 Package ที่ควรติดตั้งบน ATOM Text Editor

13 May, 2016 - 12:58

คราวที่แล้วเราได้ทำการสร้าง Code Snippet เพื่อช่วยให้เราเขียนโค้ดได้เร็วขึ้นไปแล้ว วันนี้เรามาแนะนำ Package ที่ควรติดตั้งไว้เป็นเครื่องทุนแรงอีกสัก 10 ตัว เพื่อช่วยให้งานเขียนโปรแรกมของเรามีสีสันและคล่องตัวมากขึ้น โดยขึ้นตอนการติดตั้ง package ของ ATOM ให้เราเข้าไปที่เมนู File -> Settings -> Install จากนั้นพิมพ์ชื่อ package ที่เราต้องการจะติดตั้ง หรือถ้าอยากรู้ว่ามี package อะไรบ้างที่น่าสนใจให้เขาไปที่ https://atom.io/packages

Emmet
https://atom.io/packages/emmet
Emmet จะช่วยให้เราเขียน HTML ต่างๆ ได้อย่างรวดเร็วด้วยคีย์ลัดต่างๆ ที่มีให้ โดยเราสามารถดูคำสั่งต่างๆ ได้ที่ http://docs.emmet.io/cheat-sheet/

atom-beautify
https://atom.io/packages/atom-beautify
atom-beautify จะช่วยให้เราจัดฟอร์แมตของโค้ดให้สวยงามง่ายๆ เพียงกด Ctrl-Alt-B

autoclose-html
https://atom.io/packages/autoclose-html
autoclose-html ช่วยให้เราพิมพ์แทก HTML แล้วมันจะปิดแทกให้เอง แต่ปกติถ้าเราใช้ Emmet จนคล่องแล้วก็คงแทบไม่ต้องใช้ตัวนี้ก็ได้ แต่ติดตั้งไว้หน่อยก็ดี

color-picker
https://atom.io/packages/color-picker
color-picker ตัวช่วยที่จะทำให้เราเลือกสีได้อย่างง่ายดายโดยไม่ต้องจำโค้ดสีเอง เพียงแค่กด CTRL-ALT-C

jquery-snippets
https://atom.io/packages/jquery-snippets
jquery-snippets เป็น snippets ของ jQuery เราไม่ต้องสร้าง snippet เองทั้งหมด มีคนทำให้แล้ว สบายเลย

line-ending-converter
https://atom.io/packages/line-ending-converter
line-ending-converter อันนี้ตัวแปลงรหัสการขึ้นบรรทัดใหม่ EOL บางทีโค้ดที่เราเปิดดูอาจจะสร้างมาจาก UNIX/Linux/MAC หรือ Windows ซึ่งมี EOL ที่ต่างกัน ตัวนี้ช่วยได้

minimap
https://atom.io/packages/minimap
minimap เอาไว้แสดงโค้ดอย่างย่อด้านขวามือ จะทำให้เราเลือนหาโค้ดในตำแหน่งที่ต้องการได้เร็วขึ้น

script
https://atom.io/packages/script
script เป็นตัวช่วยที่จะทำให้เราสามารถ run code ของเราได้ภายในตัว ATOM เลย จะได้เห็น output ทันทีที่กด CTRL+SHIFT+B

linter
https://atom.io/packages/linter
https://atom.io/packages/linter-php
linter และ linter-php เป็นตัวช่วยในการ debug code php ช่วยให้เราเห็นได้ง่ายขึ้นว่าเราเขียนโค้ดผิดหรือเปล่า

snake
https://atom.io/packages/snake
เกมงู //อันนี้ไร้สาระหละ

LookHin: การสร้าง Code Snippet บน ATOM Text Editor

6 May, 2016 - 15:32

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

ถ้ายังไม่ได้ติดตั้ง ATOM หรือยังไม่เคยลองใช้ก็ติดตั้งและลองเล่นสักพักหนึ่งก่อนนะครับ https://atom.io/ ผมเองเคยใช้มาทั้ง Edit Plus, Sublime และตอนนี้มาจบที่ ATOM เหตผลเพราะมัน Open Source เลยอยากแนะนำให้ทุกๆ คนลองใช้ดู

มาเริ่มสร้าง code snippet ของเรากันต่อ ให้เลือกไปที่เมนู File -> Snippets (ถ้าเมนูไม่ขึ้นให้กด ALT ข้างไว้นะครับ)

ATOM จะเปิดไฟล์ snippets.cson ขึ้นมาให้เราแก้ไข โดยรูปแบบการเขียนจะเป็น CoffeeScript Object Notation (CSON) คล้ายๆ กับ JSON แต่ไม่มีวงเล็บเปิดปิด ใช้ tab อย่างเดียว แต่เราก็ไม่ต้องสนใจอะไรมาก เราใช้อยู่ไม่กี่คำสั่งครับ

ตรง comment ด้านบนที่เขาให้มาด้วย เขาแนะนำว่าถ้าเราจะสร้าง snippet ของตัวเองได้โดยแค่พิมพ์ snip แล้วกด tab เดียวมันจะสร้าง template ขึ้นมาให้เรา ซึ่งจะได้ code ออกมาประมาณนี้ ซึ่งเป็นตัวอย่างของการทำ snippet สำหรับไฟล์ .js โดย Snippet name คือชื่อคำอธิบายที่จะแสดงตอนที่มีคำสั่งขึ้นมาให้เลือก ส่วน prefix คือ keyword ที่เรากำหนดว่าเมื่อพิมพ์คำนี้แล้วให้แสดง snippet ตัวนี้ขึ้นมา และ body คือส่วนของโค้ดที่เราต้องการ จะเห็นว่าส่วนของ boby ถ้าเราใส่แค่บรรทัดเดียว ให้ใส่เครื่องหมาย ‘ ครอบคำสั่งได้ แต่ถ้ามีหลายๆ บรรทัดเราต้องใช้ “”” ครอบคำสั่งแทน และเราสามารถใส่ $1 เพื่อกำหนดตำแหน่งของ cursor ได้ เดียวเราลองดูจากตัวอย่างหละกัน จะได้เห็นภาพ

1
2
3
4
'.source.js':
  'Snippet Name':
    'prefix': 'Snippet Trigger'
    'body': 'Hello World!'

แต่ของเรา เราจะสร้าง snippet สำหรับ php และเพิ่ม snippet เข้าไปสัก 2 ตัว ให้แก้ตามตัวอย่างนี้ครับ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'.source.php':
  'Copyright by LookHin':
    'prefix': 'lookhin'
    'body': '//Copyright by LookHin khwanchai@gmail.com'$1
 
  'MySQL Select':
    'prefix': 'sqls'
    'body': """
    //Select Data
    $query = "SELECT * FROM `table_name` ORDER BY `ID` ASC";$1
    $result = mysql_query($query);
    while($line = mysql_fetch_array($result)){
      echo $line['ID'];
    }
    """

หลังจากสร้าง snippet เสร็จแล้ว ให้ลองสร้างไฟล์ test.php และใช้ ATOM เปิดไฟล์นี้ขึ้นมา จากนั้นพิมพ์คำว่า sqls เราจะเห็นว่ามี snippet ของเราขึ้นมาให้ใช้งานแล้วครับ

MrChoke: AUDAX 300 RAYONG CHAN

2 May, 2016 - 17:56
AUDAX เป็นรายการการปั่นจักยานทางไกลที่ผู้เข้าร่วมต้องช่วยเหลือตัวเองทุกอย่าง ในไทยมีตั้งแต่ 200 300 400 600 และ 1000 กิโลเมตร มีบางรายการเปืด 100 กม. แต่ไม่ได้ record เคยปั่น 200 และ 400 มาสองรายการความเหี้ยมโหดและความบันเทิงของแต่ละรายการไม่เหมือนกันขึ้นกับเส้นทาง ครั้งนี้เข้าร่วม 300 เส้นทางระยอง จันทบุรี ต้องยอมรับเลยว่าพังมากขึ้นๆลงๆเนินตลอดเส้นทาง คือเบื่อเนินไปเลย แถมอากาศร้อนอีก ปล่อยตัวตั้งแต่ตีห้าที่ลาดหินขาว หาดแม่รำพึง ประสบการณ์จาก 400 สอนว่าไม่ควรเอาอะไรแบกไปให้มากมายเพราะทุกอย่างคือภาระ โหลดของใส่กระเป๋าเสื้อจักรยานเต็มพอดี ไม่มีเป้นอก โหลดอะไรไปบ้าง? 1. สูบพก 2. น้ำยาอุดรูรั่ว ขวดครึ่ง 3. ปะแจแอลหนึ่งชุด 4. เลนส์แว่นกลางคืน 5.ถุงตังและบัตรประชาชน 6. Power bank 7. สายชาร์จ usb ทั้ง มือถือ และการ์มิน 8. [...]

Thep: From C++ to Python (2)

1 May, 2016 - 18:39

ใน blog ที่แล้ว ผมได้พูดถึงประเด็นต่าง ๆ ที่ผมสะดุดเมื่อเรียนภาษาไพธอนด้วยพื้นฐานความรู้ C/C++ ที่มี ซึ่งเป็นการปรับโหมดคิดพื้นฐานให้เพียงพอสำหรับใช้เขียนไพธอนได้ ใน blog นี้จะขอเขียนส่วนที่เป็น กำไร ที่ได้จากภาษาไพธอนบ้าง

Container สำเร็จรูป

ภาษาไพธอนมาตรฐานมาพร้อมกับ container สำเร็จรูป คือ list, dictionary และ set โดยสามารถเก็บข้อมูลหลายชนิดปนกันได้ตามธรรมชาติของภาษา dynamic type (heterogeneous list ที่ต้องอาศัย polymorphism หรือ generic programming ใน C++ กลายเป็นเรื่องที่แสนธรรมดาเมื่อมาเขียนไพธอน) การมี container สำเร็จรูปทำให้เขียนโปรแกรมได้สะดวกขึ้นมาก

list (และ tuple ที่เป็น immutable list) นั้น มีลักษณะเหมือนลิสต์ของภาษา Lisp ที่สามารถบรรจุข้อมูลหลากชนิดคละกันได้ รวมทั้งเก็บลิสต์ในลิสต์ กลายเป็น tree ก็ยังได้ (ยังมีอิทธิพลของภาษา Lisp ในไพธอนอีกอย่าง คือ lambda expression) นอกจากนี้ ยังสามารถเข้าถึงสมาชิกได้ในแบบดัชนีแอร์เรย์ คัดลอกสมาชิกบางส่วนได้ ฯลฯ ทำให้สามารถทำงานกับ collection ของข้อมูลได้สะดวกโดยไม่ต้องคิดเรื่องวิธีจองหน่วยความจำ (แม้เรื่องประสิทธิภาพจะไม่เท่า array indexing ของจริงใน C/C++)

dictionary ทำให้การใช้งาน associative array ที่พบบ่อยในโปรแกรมต่าง ๆ กลายเป็นเรื่องง่าย (กลไกภายในคือ hash table) แม้แต่ตัว interpreter ของไพธอนเองก็ยังใช้ dictionary เป็นกลไกในการทำงานหลายส่วน เช่น ใช้ในการเก็บ attribute และ method ของออบเจกต์ต่าง ๆ แบบ dynamic, การทำ symbol table ของโปรแกรม ฯลฯ

set เป็นการ implement แนวคิดของ ทฤษฎีเซ็ต ในทางคณิตศาสตร์นั่นเอง การใช้เซ็ตในโปรแกรมได้ ทำให้สามารถเขียนโปรแกรมได้ใกล้เคียงกับนิพจน์คณิตศาสตร์มากขึ้น

การมีเครื่องมือแบบนี้ พร้อม syntax ที่เรียบง่ายในการเข้าถึงในระดับตัวภาษาเอง ทำให้เขียนโปรแกรมได้สั้นกระชับ

else ในที่ต่าง ๆ

นอกจาก else ใน if แล้ว ไพธอนยังมี else ในลูป while, for, และใน exception handling ด้วย ซึ่งคนที่เขียนโปรแกรม C/C++ มาเยอะหน่อยอาจเคยพบกรณีที่ else เหล่านี้ช่วยลดขั้นตอนลงได้

สมมุติว่ามีการค้นหาสมาชิกในลิสต์ที่สอดคล้องกับเงื่อนไขที่กำหนด

for (const Elm* p = students.first(); p; p = p->next()) {
  log_visited (p);
  if (p->id() == id) {
    report_matched (p);
    break;
  }
  mark_unmatched (p);
}

if (!p) {
  // search exhausted
  report_no_match();
}

เราไม่จำเป็นต้องเช็กค่า p อีกครั้งหลังจบลูปถ้าเราใช้ else หลัง for แบบนี้ในไพธอน:

for s in students:
  log_visited (s)
  if (s.id == id):
    report_matched (s)
    break
  mark_unmatched (s)
else:
  # search exhausted
  report_no_match()

หรือจะเป็นโปรแกรมให้ผู้ใช้ทายตัวเลข โดยให้ผู้ใช้หยุดทายได้ด้วยการป้อนค่า 0 หรือเลขลบ:

guess = 0;
while (guess != secret) {
  std::cout << "Guess the number: ";
  std::cin >> guess;

  if (guess <= 0) {
    std::cout << "Sorry that you're giving up!" << std::endl;
    break;
  }

  if (guess > secret)
    std::cout << "The number is too large." << std::endl;
  else if (guess < secret)
    std::cout << "The number is too small." << std::endl;
}

if (guess == secret)
  std::cout << "Congratulations. You made it!" << std::endl;

ด้วย else ในไพธอน คุณก็ไม่ต้องเช็กค่า guess ซ้ำหลังจบลูป:

guess = 0
while guess != secret:
  guess = int (input ("Guess the number: "))

  if guess <= 0:
    print ("Sorry that you're giving up!")
    break

  if guess > secret:
    print ("The number is too large.")
  elif guess < secret:
    print ("The number is too small.")
else: 
  print ("Congratulations. You made it!")

โค้ดแบบนี้ผมเจอค่อนข้างบ่อยใน C/C++ บางทีคิด ๆ เหมือนกันว่าถ้าใช้ goto แทน break ซะก็อาจไม่ต้องมาเช็กซ้ำ พอมาเจอ else ของลูปในไพธอนก็เข้าใจได้ทันที

List Comprehension

list comprehension เป็นสิ่งที่ pythonic เอามาก ๆ ทำให้โค้ดกระชับและดูคล้ายนิพจน์คณิตศาสตร์

เช่น ถ้าต้องการหารายการข้อมูลในลิสต์ data ที่สูงกว่าค่าเฉลี่ย:

avg = sum(data)/len(data)
print([x for x in data if x > avg])

หรือแม้กระทั่งจะหาจำนวนเฉพาะทั้งหมดที่น้อยกว่าจำนวนที่กำหนด:

from math import sqrt
def primes(n):
  sqrt_n = int(sqrt(n))
  no_primes = {j for i in range(2, sqrt_n) for j in range(i*2, n, i)}
  return [i for i in range(2, n) if i not in no_primes]

พี่จะสั้นไปไหนครับ!

ในบทที่ว่าด้วย Lambda operator, map, filter, reduce บอกไว้ที่ส่วนต้นว่า Guido van Rossum ผู้สร้างและดูแลภาษาไพธอนได้แสดงความประสงค์ที่จะ ตัด lambda, map, filter, reduce ออกใน Python 3 เพราะ list comprehension ทำสิ่งเดียวกันได้ชัดเจนและเข้าใจง่ายกว่า แต่สุดท้ายก็ทนแรงต้านจากผู้นิยม Lisp, Scheme ไม่ไหว จำเป็นต้องคงไว้ ตัดออกเฉพาะ reduce() โดยย้ายไปไว้ในมอดูล functools

เทียบกันแล้ว list comprehension ถอดแบบมาจาก set-builder notation ในทฤษฎีเซ็ต ส่วน lambda นั้น ถอดแบบมาจาก lambda calculus เห็นได้ชัดว่าทฤษฎีเซ็ตเป็นที่คุ้นเคยและเข้าใจง่ายกว่า สิ่งที่ lambda ทำได้มากกว่า list comprehension ก็คือ reduce() ซึ่งในความเห็นของผู้สร้างไพธอนแล้ว ทำให้โค้ดซับซ้อนเกินไป ยอมเขียนเป็นลูปเพื่อความชัดเจนเสียจะดีกว่า

ไหนลองเขียนด้วย lambda ดูซิ:

หาข้อมูลที่สูงกว่าค่าเฉลี่ย:

avg = sum(data)/len(data)
print(list(filter(lambda x : x > avg, data)))

หาจำนวนเฉพาะ:

from math import sqrt
from functools import reduce
def primes(n):
  sqrt_n = int(sqrt(n))
  np_series = list(map(lambda i : set(range(i*2, n, i)), list(range(2,sqrt_n))))
  np_set = reduce(lambda a, b : a | b, np_series)
  return list(filter(lambda i : i not in np_set, list(range(2,n))))

จะเห็นว่า lambda ยาวและเข้าใจยากกว่า list comprehension

Generator

ลูป for ในไพธอนจะไม่มีรูปแบบการใช้ตัวนับเหมือนภาษาทั่วไป แต่จะใช้ iterator ล้วน ๆ คือเป็นลูป foreach นั่นเอง

เช่น ลูปหาผลรวมของกำลังสองของจำนวนเต็มบวก n ตัวแรกที่เขียนในภาษา C++ อาจเป็นแบบนี้:

int sum_sq (int n)
{
  int sum = 0;
  for (int i = 1; i <= n; i++)
    sum += i*i;
  return sum;
}

แต่ลูป for ในไพธอนจะใช้ฟังก์ชัน range() สร้าง iterator สำหรับไล่เรียง:

def sum_sq (n):
  sum = 0
  for i in range (1, n+1):
    sum += i*i
  return sum

หรือจะให้ pythonic จริง ๆ ก็ใช้ list comprehension:

def sum_sq (n):
  return sum([i*i for i in range (1, n+1)])

iterable object ต่าง ๆ เช่น list, tuple, dictionary, set สามารถใช้เป็น iterator ได้ทันที นอกจากนี้ ยังสามารถสร้าง iterator ขึ้นเองได้ โดยทำตามโพรโทคอลที่กำหนด (สร้างคลาสที่มีเมธอด __iter__(), next() โดย next() คืนค่าตัววิ่งแต่ละขั้น และ raise StopIteration exception เมื่อวิ่งสุดแล้ว) แต่เพื่ออำนวยความสะดวกยิ่งขึ้น ไพธอนได้บัญญัติสิ่งที่เรียกว่า generator ที่ใช้วิ่งลูปได้เหมือน iterator แต่เขียนง่ายกว่า

ตัวอย่างเช่น ถ้าจะเขียน generator สำหรับไล่ลำดับ Fibonacci:

def fibo (n):
  a, b = 0, 1
  for i in range(n):
    yield a
    a, b = b, a + b

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

จากนั้นก็สามารถไล่ลำดับ Fibonacci ได้ตามต้องการ:

>>> list(fibo(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> for x in fibo(5):
...   print (x)
... 
0
1
1
2
3
>>> [x for x in fibo(10) if x % 2 == 0]
[0, 2, 8, 34]

การมี construct แบบ generator ก็ทำให้มีความยืดหยุ่นของการเขียน iterator เช่น สามารถเขียน generator แบบ recursive ได้ หรือกระทั่งเขียน generator ซ้อน generator ได้ อ่านเพิ่มเติม

เมื่อมองย้อนกลับไปถึงตอนแรกที่เราพบว่าไพธอนมีแต่ลูป foreach เท่านั้น ก็ไม่ได้ทำให้ความสามารถด้อยไปกว่าภาษาที่มีลูปตัวนับแต่อย่างใด (ในเมื่อมีฟังก์ชัน range()) แต่กลับมีความยืดหยุ่นสูงมากในการวนลูปที่ซับซ้อน

Decorator

ข้อนี้ไม่แน่ใจนักว่าชอบหรือเปล่า แต่ก็เป็นสิ่งที่น่าจะมีประโยชน์ในบางโอกาส คือสิ่งที่ไพธอนเรียกว่า decorator ซึ่งหลังจากที่ทำความเข้าใจแล้ว อยากจะเรียกว่า wrapper มากกว่า

สมมุติว่าเราต้องการนับจำนวนการเรียกฟังก์ชันต่าง ๆ ในโปรแกรมของเรา เราสามารถเขียน wrapper function มาดักการเรียกของผู้ใช้แล้วแอบเพิ่มตัวนับก่อนเรียกฟังก์ชันตัวจริง และไพธอนมีวิธีการสร้าง wrapper ที่ว่านี้อย่างแนบเนียน

def call_counter (func):
  def wrapper (*args, **kwargs):
    wrapper.calls += 1
    return func (*args, **kwargs)
  wrapper.calls = 0
  wrapper.__name__ = func.__name__
  return wrapper

@call_counter
def square (x):
  return x*x

print (square.calls)
for i in range (10):
  print (square (i))
print (square.calls)

บรรทัด @call_counter คือ syntax ของไพธอนในการ decorate ฟังก์ชัน โดยโค้ดนี้:

@call_counter
def square (x):
  return x*x

มีความหมายเทียบเท่ากับ:

def square (x):
  return x*x
square = call_counter (square)

กล่าวคือ เป็นการส่งออบเจกต์ของฟังก์ชัน square() ให้กับฟังก์ชัน call_counter() แล้ว call_counter() คืนค่า wrapper() ซึ่งเป็นฟังก์ชันภายในมา จากนั้น ก็ใช้ค่าที่คืนมานี้ assign ค่าทับลงไปใน symbol square() เสีย ทำให้การเรียก square() หลังจากนี้ไปจะเป็นการเรียกตัวฟังก์ชัน wrapper() ที่ได้แอบเพิ่มตัวนับก่อนเรียกฟังก์ชัน square() ตัวจริงที่ได้ส่งมาก่อนหน้านี้ในพารามิเตอร์ชื่อ func

(คำอธิบายค่อนข้างซับซ้อนวนเวียนสักหน่อย หากงงก็ขอแนะนำให้อ่านรายละเอียดจาก บทเรียน นอกจากนี้ยังมี blog ที่ artima และ blog ของ Simeon Franklin ที่ให้คำอธิบายอย่างละเอียด)

อีกวิธีหนึ่งคือเขียน wrapper เป็นคลาสที่ callable ซึ่งดูสะอาดกว่า:

class call_counter:

  def __init__ (self, func):
    self.func = func
    self.calls = 0

  def __call__ (self, *args, **kwargs):
    self.calls += 1
    return self.func (*args, **kwargs)

ประโยชน์ที่พอมองเห็นได้คือ ไพธอนมีวิธีสร้าง wrapper ที่แนบเนียน wrapper มีประโยชน์ที่ไหนก็ใช้ได้ที่นั่น (เช่น การ cache ผลการคำนวณครั้งก่อน ๆ ของฟังก์ชัน, การเพิ่มการตรวจสอบอาร์กิวเมนต์ ฯลฯ)

Library

นอกจากตัวภาษาเองแล้ว ไพธอนยังมาพร้อมกับไลบรารีมาตรฐานอีกเพียบ ซึ่งผมคงต้องศึกษาเพิ่มเติมไปเรื่อย ๆ เท่าที่ได้ผ่านมาก็คือเรื่องการใช้ regular expression

ดูยังมีอะไรอีกเยอะให้ศึกษาข้างหน้า ที่สำคัญคือการฝึกฝนให้เกิด pythonic way ในการเขียนโค้ด และการใช้แพกเกจต่าง ๆ ให้เหมาะกับงาน

Thep: From C++ to Python (1)

27 April, 2016 - 14:37

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

ในการเรียนครั้งนี้ ผมได้อาศัยบทเรียนจาก python-course.eu (และไปอ่านทบทวนกับ บทเรียนที่ debianclub ที่คุณวิทยาเคยลงไว้)

บทเรียนที่ python-course.eu นับว่าเหมาะกับคนที่มีพื้นฐาน C/C++ มาแล้วมาก เพราะเขาอธิบายบนพื้นฐานของคนเคยเขียนโปรแกรมมาแล้ว ไม่ใช่เริ่มต้นจากศูนย์ ยกตัวอย่างเปรียบเทียบกับ C/C++ และเน้นประเด็นที่เป็นหลุมพรางของผู้ที่ย้ายมาจาก C/C++

เท่าที่ได้ตั้งโจทย์ฝึกหัดให้กับตัวเอง ด้วยการทดลองพอร์ตโค้ดภาษา C/C++ ของตัวเองที่เคยเขียนไว้ให้เป็นไพธอน พร้อมกับทำ unit test ไปด้วย ทำให้ได้พบเจอประเด็นต่าง ๆ ที่โปรแกรมเมอร์ภาษา C/C++ จะสะดุด ผมเองก็พบว่าต้องก้าวข้ามสิ่งเหล่านี้ถึงจะเริ่มจับทางไพธอนได้ จึงเขียนบันทึกไว้สักหน่อย

Indentation

ข้อนี้ดูเหมือนเป็นเรื่องเล็ก แต่กลับเป็นสาเหตุหลักข้อหนึ่งที่ทำให้ผมยี้ไพธอนก่อนหน้านี้ เพราะโปรแกรมเมอร์ภาษา C/C++ จะคุ้นกับ free-form syntax และพบเจอการ indent แบบตามใจฉันมาแล้วมากมาย บางคนใช้ tab บางคนใช้ space 2, 4 หรือ 8 ช่อง บางคนใช้ปนกันทั้ง tab ทั้ง space ซึ่งซอร์สจะเละทันทีเมื่อมีการเปลี่ยนขนาดของ tab stop นี่ยังไม่นับคนที่ไม่สนใจ style ใด ๆ ทั้งสิ้นอีกนะ แต่ไม่ว่าอย่างไรโปรแกรมก็ยังคอมไพล์ผ่าน แล้วถ้ามันกลายเป็นการกำหนด syntax ของภาษา มันจะเละเทะขนาดไหน

เมื่อพยายามนึกถึงตัวอย่างอื่นที่กำหนดอะไรทำนองนี้ ผมก็นึกถึง Makefile ที่ใช้ tab ในการกำหนดขอบเขตของ rule แต่มันก็แค่ระดับเดียว ไม่ได้ซ้อนกันหลายชั้น จึงดูไม่มีอะไรมาก และอีกตัวอย่างหนึ่งคือ Fortran 77 ที่เคยเขียนในสมัยเรียน ซึ่งอาศัยตำแหน่งคอลัมน์เป็นส่วนหนึ่งของ syntax อันเป็นมรดกตกทอดมาจากสมัยใช้บัตรเจาะรู (ตอนที่เรียน Fortran 77 รู้สึกอึดอัดตรงนี้มาก เพราะตอนนั้นผ่านภาษา Pascal มาแล้ว แม้แต่ภาษา BASIC [AppleSoft, GW] ที่ว่าไม่ค่อยมีโครงสร้างก็ยังไม่ทำอะไรโลว์เทคเยี่ยงนี้) แต่ละอย่างที่นึกถึงก็ไม่ได้ช่วนพิสมัยเอาเสียเลย

นั่นคือ mindset ของผมก่อนเรียน แต่ คำอธิบาย ในบทเรียนก็ทำให้ผมเข้าใจและยอมรับ ทำให้เรียนไพธอนต่อไปได้อย่างผ่อนคลาย มันคือการกำหนดขอบเขตของบล็อคคำสั่งโดยเปลี่ยนจาก style ให้เป็น syntax เสีย คนที่เขียนโปรแกรมภาษา C/C++ โดยเคร่งครัดกับ style อยู่แล้วจึงยอมรับตรงนี้ได้ไม่ยาก ส่วนประเด็นเรื่องการใช้ tab กับ space ปนกัน ไพธอนก็มีข้อกำหนดที่ชัดเจนที่ทำให้แยกแยะออกจากกันได้ คือถือเป็น indentation คนละระดับไปเลย ไม่นับปนกัน

มันคือการกำจัดวิจิกิจฉานิวรณ์ครับ ผ่านตรงนี้ไปได้ก็ลื่นไหลขึ้นเยอะ

Public, Protected, Private

ภาษา C++ จะมีการกำหนด access specifier สำหรับ member ต่าง ๆ ของคลาสเป็น public, protected, private จากนั้น โปรแกรมเมอร์แต่ละคนก็จะมีวิธีการต่าง ๆ ในการตั้งชื่อ member ให้สามารถแยกแยะ access ได้ บางคนใช้ตัวพิมพ์เล็ก-พิมพ์ใหญ่ต้นชื่อ บางคนใช้ prefix บางคนใช้ suffix บางคนไม่แยกเลย (อันนี้ยุ่ง)

แต่ไพธอนไม่มี access specifier แต่จะใช้การตั้งชื่อแยกแยะทันที โดยชื่อปกติจะถือเป็น public ชื่อที่ขึ้นต้นด้วย _ ถือเป็น protected และชื่อที่ขึ้นต้นด้วย __ ถือเป็น private นับว่าเป็นการเปลี่ยน style ให้เป็น syntax อีกหนึ่งเรื่อง

Dynamic Type & Scope

การย้ายจากภาษาที่เป็น static typing มาเป็น dynamic typing จะต้องปรับโหมดความคิดสักหน่อย ซึ่งพื้นฐานภาษา BASIC สมัยเก่า บวกกับความรู้จากวิชา Compilers & Programming Languages พอช่วยได้ ไม่เป็นปัญหาสำหรับผมนัก สิ่งที่ต้องปรับเปลี่ยนแนวคิดก็เช่น:

  • ชนิดของตัวแปรเป็น dynamic ไม่ใช่ตายตัวเปลี่ยนชนิดไม่ได้เหมือนในภาษา static type ทั้งหลาย ถึงตัวแปรหนึ่งจะเก็บค่า integer อยู่ คุณจะ assign สตริงหรือลิสต์ให้มันใหม่ก็ยังได้
    >>> x = 1
    >>> print(x)
    1
    >>> x = "Hello!"
    >>> print(x)
    Hello!
    
  • scope ของตัวแปรเป็นแบบ dynamic ฟังก์ชันหนึ่ง ๆ ทำงานแต่ละครั้งอาจเข้าถึงตัวแปรได้ไม่เหมือนกัน ขึ้นอยู่กับ state ของทั้งโปรแกรมในขณะนั้น
    >>> def f():
    ...   print(s)
    ... 
    >>> s = "Hello"
    >>> f()
    Hello
    >>> s = 2
    >>> f()
    2
    
    แต่ dynamic scope นี้ก็ถูกขี่ทับด้วย block scope ได้ ถ้ามีการกำหนดตัวแปรโลคอลในชื่อเดียวกัน
    >>> def g():
    ...   s = "Bye"
    ...   print(s)
    ... 
    >>> s = "Hello"
    >>> g()
    Bye
    >>> print(s)
    Hello
    
    อ่านเพิ่มเติม
Call by Value หรือ Call by Reference?

ภาษาไพธอนไม่มีพอยน์เตอร์เหมือน C/C++ แต่กลไกภายในกลับใช้พอยน์เตอร์เต็มไปหมด แม้แต่ตัวแปรกับค่าของมันก็เชื่อมกันด้วยพอยน์เตอร์ การ assign ตัวแปรก็เป็นการ assign พอยน์เตอร์ไปยังออบเจกต์ที่เป็นค่า ดังสามารถตรวจสอบได้ดังนี้:

>>> x = 1
>>> y = x
>>> id(x)
10861696
>>> id(y)
10861696

จะเห็นว่า หลัง assignment แล้ว x กับ y ชี้ไปยังออบเจกต์เดียวกัน ไม่ใช่แค่มีค่าเท่ากัน! อย่างไรก็ดี ถ้าเรา assign ค่าใหม่ให้กับ y จะไม่ได้ทำให้ค่าของ x เปลี่ยน แต่เป็นการกำหนดให้ y ชี้ไปยังออบเจกต์ใหม่:

>>> y = 2
>>> id(y)
10861728
>>> x
1

ก็น่าจะปลอดภัยดี แถมยังเป็นการประหยัดหน่วยความจำด้วยถ้าค่าเป็นออบเจกต์ใหญ่ ๆ ที่ไม่ใช่ integer เรื่องประหยัดน่ะใช่ แต่แน่ใจหรือเรื่องความปลอดภัย?

>>> p = [1, 2, 3]
>>> q = p
>>> q[1] = 'x'
>>> p
[1, 'x', 3]

จะเห็นว่าลิสต์ p เปลี่ยนตาม q หลัง assignment ทั้งนี้เพราะทั้ง p และ q ชี้ไปยังออบเจกต์เดียวกันตลอดเวลา และการ assign q[1] ก็เป็นการเปลี่ยนพอยน์เตอร์ q[1] จากที่ชี้ไปยัง integer 2 ให้ชี้ไปยัง string 'x' แทน แต่ในเมื่อ q ชี้ไปยังออบเจกต์เดียวกันกับ p การเปลี่ยนพอยน์เตอร์ q[1] จึงเป็นการเปลี่ยนพอยน์เตอร์ p[1] ด้วย!

พฤติกรรมแบบนี้ เกิดกับออบเจกต์ที่เป็น complex data structure ทั้งหมด ไม่ว่าจะเป็น list, dictionary หรือ class instance ซึ่งหากอธิบายในภาษาของ C/C++ ด้วยคำว่า พอยน์เตอร์ (จากชื่อตัวแปรไปยังค่า) ก็จะสามารถทำความเข้าใจได้ รวมถึงประโยชน์ของ deepcopy ด้วย

ความสนุกเกิดขึ้นเมื่อเราส่งอาร์กิวเมนต์ให้กับฟังก์ชัน

แบบนี้พฤติกรรมจะเหมือน call by value:

>>> def f(x):
...   x = x + [1]
... 
>>> p = [1, 2, 3]
>>> f(p)
>>> p
[1, 2, 3]

แต่แบบนี้กลับเหมือน call by reference:

>>> def g(x):
...   x += [1]
... 
>>> p = [1, 2, 3]
>>> g(p)
>>> p
[1, 2, 3, 1]

คำอธิบายก็คือ x ซึ่งเป็นพารามิเตอร์ของ f() และ g() นั้น จะก็อปปี้พอยน์เตอร์ไปยังค่าของ p มาทั้งคู่ จากนั้น x ใน f() ถูก assign ให้ชี้ไปยังออบเจกต์ใหม่ที่เป็นผลลัพธ์ของการบวก x กับลิสต์ [1] ในขณะที่ x ใน g() ถูกเพิ่มอิลิเมนต์ใหม่ต่อท้ายโดยตรงในออบเจกต์เดิม ซึ่งเป็นออบเจกต์เดียวกับที่ p ของผู้เรียกชี้อยู่ ผลจึงเป็นการเปลี่ยนค่าของ p ของผู้เรียกไปด้วย

คำถามที่ว่า การเรียกฟังก์ชันของไพธอนเป็น call by value หรือ call by reference จึงตอบได้แค่ว่า ไม่ใช่ทั้งสองอย่าง ผู้ที่ย้ายมากจากภาษา C/C++ ต้องระวังให้ดี ภาษาไพธอนไม่มีพอยน์เตอร์ก็จริง แต่เวลาเขียนไพธอนให้นึกถึงพอยน์เตอร์เข้าไว้

Function Overloading?

ฟังก์ชันในภาษา C++ สามารถโอเวอร์โหลดเพื่อรับพารามิเตอร์หลายแบบได้ ถึงแม้มันจะเพิ่มความยุ่งยากในการลิงก์จากโค้ดภายนอกอันเนื่องมาจาก symbol mangling แต่มันก็ไม่ได้สร้างขึ้นมาเท่ ๆ มี use case ที่ได้ประโยชน์จากการโอเวอร์โหลดฟังก์ชัน เช่น:

  • ช่วยให้สร้าง constructor หลายแบบได้
  • ช่วยในการ overload operator เพื่อรับ operand หลายชนิดได้

แต่ไพธอนไม่สามารถโอเวอร์โหลดฟังก์ชันได้ มีให้อย่างมากก็แค่การใช้ default argument แต่ถ้ากำหนดฟังก์ชันชื่อเดียวกันหลายครั้ง มันก็จะเอาอันหลังสุดเท่านั้น ถือว่าทับอันแรก ๆ ไป

ด้วยความเป็น dynamic typing ของภาษาไพธอน ทำให้พอเข้าใจได้ว่าการโอเวอร์โหลดฟังก์ชันสามารถทำให้เกิดความยุ่งยากได้ กรณีทั่วไปเราอาจเลี่ยงได้ด้วยการตั้งชื่อฟังก์ชันหลบกันเสีย แต่สำหรับกรณีของ magic methods ทั้งหลายของคลาส เราไม่สามารถตั้งชื่อหลบได้ ซึ่งกรณีของ constructor และ operator overloading ก็เข้าข่าย magic methods ทั้งสิ้น

แล้วจะจัดการกับ use case ข้างต้นได้อย่างไร?

การโอเวอร์โหลด constructor

กรณีที่สามารถใช้ default argument ได้ ก็ใช้ default argument เช่น:

class Time:
  def __init__ (self, h=0, m=0, s=0):
    self.h, self.m, self.s = h, m, s

t1 = Time()
t2 = Time(8)
t3 = Time(8, 20)
t4 = Time(8, 20, 45)

หากโอเวอร์โหลดโดยใช้อาร์กิวเมนต์ต่างชนิดกัน ก็ไม่สามารถใช้ default argument ได้ ก็อาจใช้วิธีพิเศษ เช่น การใช้ argument tuple หรือ argument dict:

class Time:
  def __init__ (self, *args):
    if len (args) == 1 and type (args[0]) == str:
      h, m, s = args[0].split(":")
      self.h, self.m, self.s = int(h), int(m), int(s)
    else:
      self.h, self.m, self.s = 0, 0, 0
      if len (args) >= 1:
        self.h = int(args[0])
      if len (args) >= 2:
        self.m = int(args[1])
      if len (args) >= 3:
        self.s = int(args[2])

t1 = Time()
t2 = Time(8)
t3 = Time(8, 20)
t4 = Time(8, 20, 45)
t5 = Time("8:20:50")

หรือใช้ factory method ซะเลย:

class Time:
  @classmethod
  def from_str (cls, s):
    h, m, s = s.split(":")
    return cls (int(h), int(m), int(s))

  @classmethod
  def from_hms (cls, h=0, m=0, s=0):
    return cls (h, m, s)

  def __init__ (self, h, m, s):
    self.h, self.m, self.s = h, m, s

t1 = Time.from_hms()
t2 = Time.from_hms(8)
t3 = Time.from_hms(8, 20)
t4 = Time.from_hms(8, 20, 45)
t5 = Time.from_str("8:20:50")

(ดัดแปลงจาก กระทู้ Stack Overflow)

การโอเวอร์โหลด operator

สมมุติว่าเรามีคลาส Date (วันที่) ซึ่งต้องการโอเวอร์โหลดเครื่องหมายลบดังนี้:

d1 = Date("2016-04-26")
d2 = Date("2016-05-03")
assert d2 - d1 == 7
assert d2 - 7 == d1

กล่าวคือ:

  • ถ้าตัวลบเป็นชนิด Date ให้หาจำนวนวันระหว่างวันที่ทั้งสอง
  • ถ้าตัวลบเป็นชนิด int ให้หาวันที่ถอยหลังเป็นจำนวนวันที่ลบ

กรณีนี้ดูจะไม่มีทางอื่น นอกจากตรวจสอบชนิดของตัวลบเอา:

class Date:
  # ...

  def __sub__ (self, other):
    if type (other) == int:
      # ... subtract days from self ...
    elif type (other) == Date:
      # ... subtract two Dates ...
    else:
      raise TypeError ("Invalid operand type")

ซึ่งออกจะดูอัปลักษณ์ถ้าเป็นโค้ด C++ แต่นี่คือไพธอนมาตรฐาน

อย่างไรก็ดี เท่าที่ลองค้นดู ดูเหมือนจะมีแพกเกจ overload และ multimethod เพื่อการนี้ และดูจะมี PEP 3124 เพื่อเพิ่มการรองรับการโอเวอร์โหลดฟังก์ชันอย่างเป็นทางการ แต่ยังอยู่ในสถานะ Deferred อยู่

Polymorphism?

หนึ่งในเครื่องมือที่ใช้กันมากของ OOP ก้คือ polymorphism ซึ่งในภาษา C++ จะใช้ virtual function ประกอบกันกับ class hierarchy ซึ่ง implementation ภายในคือ pointer to function ใน v-table ของคลาส

แต่พอมาเขียนไพธอน คุณจะหา keyword หรือ naming convention ที่เทียบเคียงกับ virtual function ไม่ได้เลย ทั้งนี้เพราะไพธอนเป็น polymorphic โดยธรรมชาติอยู่แล้ว!

เวลาที่เขียนฟังก์ชันแบบนี้ใน C++:

inline int max (int x, int y) { return (x > y) ? x : y; }
inline int max (double x, double y) { return (x > y) ? x : y; }

หรือจะใช้ generic programming ด้วย template ซึ่ง implementation ภายในเทียบเท่ากัน แต่ใช้กับชนิดใดก็ได้ที่รองรับ operator > :

template <class T>
inline int max (T x, T y) { return (x > y) ? x : y; }

แต่ในไพธอนคุณเขียนแค่นี้ก็ทำงานได้กับทุกชนิดแล้ว:

def max (x, y):
  return x if x > y else y

เพราะ dynamic typing นั่นเอง ทำให้สามารถส่งออบเจกต์ชนิดไหนก็ได้ แล้วไปว่ากันที่ run-time ถ้าชนิดนั้น ๆ รองรับ operation ที่เรียกใช้ ก็เป็นอันใช้ได้ ตามแนวคิดที่เรียกว่า duck-typing ซึ่งกล่าวว่า If it looks like a duck and quacks like a duck, it must be a duck.

คุณจึงสามารถใช้ polymorphism ได้ ไม่ว่าจะเขียนโค้ดแบบนี้:

class Animal:
  def cry (self):
    pass

class Cat (Animal):
  def cry (self):
    print ("Meow!")

class Duck (Animal):
  def cry (self):
    print ("Quack!")

farm = []
farm.append (Cat())
farm.append (Duck())

for i in farm:
  i.cry()

หรือแบบนี้:

class Cat:
  def cry (self):
    print ("Meow!")

class Duck:
  def cry (self):
    print ("Quack!")

farm = []
farm.append (Cat())
farm.append (Duck())

for i in farm:
  i.cry()

คงพอเห็นภาพนะครับ

blog นี้เขียนถึงสิ่งที่สะดุด ก็ชักจะยาวแล้ว เดี๋ยว blog หน้าค่อยเขียนถึงสิ่งที่ผมชอบในไพธอนต่อนะครับ

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

MrChoke: สิ่งที่ควรรู้กับ Garmin Edge รุ่น ไทย

22 April, 2016 - 12:09
ผมเป็นลูกค้า Garmin มาสักระยะ ตั้งแต่ผมปั่นจักรยานและมองหาตัวเก็บสถิติดีๆ สักตัว เริ่มตั้งแต่ Edge 810 ตัวแรก และ ต่อมาโชคดีได้จับรางวัลได้ Edge 1000 สิ่งแตกต่างของ 810 กับ 1000 คือ ตัว 1000 เป็นรุ่น Thai bundle มาส่วน 810 มีแค่ภาษาอังกฤษ ตอนแรกผมก็ไม่รู้ว่า Garmin จัดการเรื่อง Firmware ยังไง จนมาถึงการ update ครั้งใหญ่ความสามารถใหม่ๆ เพิ่มมาเพียบใน Edge 1000 ผมตั้งตารอคอยมานานติดตามข่าวตั้งแต่เค้ายังออกรุ่นทดสอบกัน และ เมื่อไม่นานก็มีการปล่อยออกมาจริงๆ แต่ต้องพบกับความผิดหวัง เมื่อ firmware ไทยไม่ update ตามรุ่น inter เมื่อไล่เรียงกลับไปพบว่าปัจจุบัน firmware edge 1000 ไทยยังอยู่ที่ รุ่น 5.2 ส่วนรุ่น [...]

Kitt: Using docker-machine to deploy multi-manager docker swarm

16 April, 2016 - 16:06
Of course, HA is a must in production environment. So, you gonna need multi-manager docker swarm. It is super easy when you know how. First, you cannot use token, you need a discovery service (consul, etcd, zookeeper). So, create one if you don’t have, e.g. $ docker-machine create -d virtualbox consul $ docker-machine ssh consul docker@consul:~$ docker run -d … Continue reading Using docker-machine to deploy multi-manager docker swarm →

Kitt: สงกรานต์ 2559

11 April, 2016 - 13:02
สงกรานต์ปี 2559 เป็นปี จ.ศ. (2559 – 1181) = 1378 วันเถลิงศก ตรงกับ (1378 * 0.25875) + floor(1378 / 100 + 0.38) - floor(1378 / 4 + 0.5) - floor(1378 / 400 + 0.595) - 5.53375 = 356.5575 + 14 - 345 - 4 - 5.53375 = 16.02375 = วันที่ 16 เมษายน 2559 เวลา 00:34:12 วันสงกรานต์ ตรงกับ … Continue reading สงกรานต์ 2559 →

Neutron: LAB: Overlay Network across multiple cloud providers

7 April, 2016 - 11:10

หลังจากทำ Lab เรื่อง
Manage VPN clients to route over multiple data centers
เมื่อคราวก่อน ซึ่งแต่ละโหนด อยู่บน cloud providers จากหลาย ๆ data center (Thailand, Singapore, Japan, USA) และเราสามารถเชื่อมต่อทุก node เข้าหากัน ด้วย OpenVPN และ OSPF แล้ว นั่นหมายความว่า เรามี Unicast Routing ที่ใช้งานได้แล้ว

ขั้นต่อไป คือ การเตรียม Multicast Routing และเชื่อมต่อ Overlay Network ด้วย VXLAN

  • ผมเลือกใช้ PIMv2 (Protocol Independent Multicast) เพื่อบริหารจัดการ multicast routing table เพื่อให้ทุก node สามารถคุย multicast หากันได้ โดย PIM จะอาศัย Unicast Routing ที่เราได้เตรียมไว้ก่อนหน้าแล้ว
  • สร้าง vxlan interface โดยอาศัย multicast ที่เตรียมไว้
    SG VPN SERVER (sg1-dev)
    # ip link add vxlan100 type vxlan id 100 group 239.0.0.100 ttl 10 dev tap0
    # ip link set up dev vxlan100
    # ip addr add 172.30.0.1/24 dev vxlan100
    

    TH VPN SERVER (th1-dev)

    # ip link add vxlan100 type vxlan id 100 group 239.0.0.100 ttl 10 dev tap0
    # ip link set up dev vxlan100
    # ip addr add 172.30.0.2/24 dev vxlan100
    

ทดสอบ


จะเห็นได้ว่า จาก 172.30.0.1 ไปหา 172.30.0.2 เสมือนต่อกันตรง ๆ และนั่นก็คือ Overlay Network

Creative Commons License ลิขสิทธิ์ของบทความเป็นของเจ้าของบทความแต่ละชิ้น
ผลงานนี้ ใช้สัญญาอนุญาตของครีเอทีฟคอมมอนส์แบบ แสดงที่มา-อนุญาตแบบเดียวกัน 3.0 ที่ยังไม่ได้ปรับแก้