31 January 2013

เทียบความเร็วระหว่าง binary กับ text files

ผมมักจะแนะนำให้นักศึกษาเก็บข้อมูลแบบ binary (หรือที่เก็บไฟล์ แล้วเราจะเรียกมันว่า binary file) เวลาที่ต้องการความเร็วในการอ่านเขียนข้อมูล และลดพื้นที่ในการเก็บข้อมูล แม้ว่าจะมีข้อเสียอยู่อย่างหนึ่ง คือ เราเปิดดูข้อมูลใน binary file ได้ไม่สะดวกนัก ไม่สามารถเปิด text editor มาแก้ binary file ได้ง่ายๆ แบบ text file แต่บางครั้งผมก็สงสัยว่า ถ้าเปรียบเทียบความเร็วในการเขียนอ่านข้อมูลระหว่าง binary file กับ text file แล้ว จะต่างกันมากน้อยแค่ไหน ผมเลยลองเขียนโปรแกรม Java ง่ายๆ ขึ้นมาทดลองดู ให้โปรแกรมสร้างไฟล์เก็บข้อมูล double จำนวน 10,000,000 ตัวลองไฟล์สองแบบ แล้วลองอ่านขึ้นมาดูว่าใช้เวลาต่างกันแค่ไหน

package org.cholwich.binvstxt;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class BinVsTxt {
  
  public void writeTextFile(String fname, int N) {
    try {
      Random r = new Random();
      r.setSeed(10241024);
      PrintWriter out = new PrintWriter(new FileWriter(fname));
      for(int i=0; i<N; i++) {
        out.println(r.nextDouble());
      }
      out.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  
  public void writeBinaryFile(String fname, int N) {
    try {
      Random r = new Random();
      r.setSeed(10241024);
      DataOutputStream out = new DataOutputStream(
                                new BufferedOutputStream(
                                    new FileOutputStream(fname)));
      for(int i=0; i<N; i++) {
        out.writeDouble(r.nextDouble());
      }
      out.close();
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  
  public List<Double> readTextFile(String fname) {
    List<Double> l = new ArrayList<Double>(); 
    try {
      BufferedReader in = new BufferedReader(
                            new FileReader(fname));
      double d;
      String buf;
      while((buf = in.readLine()) != null) {
        d = Double.parseDouble(buf);
        l.add(d);
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (NumberFormatException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return l;
  }

  public List<Double> readBinaryFile(String fname) {
    List<Double> l = new ArrayList<Double>();
    DataInputStream in = null;
    try {
      in = new DataInputStream(
                new BufferedInputStream(
                    new FileInputStream(fname)));
      double d;
      while(true) {
        d = in.readDouble();
        l.add(d);
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (EOFException e) {
      try {
        in.close();
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    return l;
  }
  
  public static void main(String[] args) {
    boolean read = true;
    boolean bin = true;
    final int N = 10000000;
    
    for(String s : args) {
      if (s.equals("write")) {
        read = false;
      }
      else if (s.equals("text")) {
        bin = false;
      }
    }
    BinVsTxt m = new BinVsTxt();
    long start = System.currentTimeMillis();
    if (read) {
      if (bin) {
        List<Double> l = m.readBinaryFile("out"+N+".dat");
        System.out.println(l.get(N-1));
      }
      else {
        List<Double> l = m.readTextFile("out"+N+".txt");
        System.out.println(l.get(N-1));
      }
    }
    else {
      if (bin) {
        m.writeBinaryFile("out"+N+".dat", N);
      }
      else {
        m.writeTextFile("out"+N+".txt", N);
      }
    }
    long stop = System.currentTimeMillis();
    long len = stop - start;
    System.out.println("Required time = " + len);
  }
}

เมื่อลองรันทั้งสี่แบบดูแล้ว ปรากฎว่าความเร็วที่ได้คือ

  • เขียน text file ใช้เวลา 6.109 วินาที
  • อ่าน text file ใช้เวลา 11.666 วินาที
  • เขียน binary file ใช้เวลา 0.583 วินาที
  • อ่าน binary file ใช้เวลา 3.698 วินาที
จึงสรุปได้ว่า การใช้ binary file เร็วกว่าการใช้ text file พอสมควร แต่อาจจะไม่จำเป็นเท่าไหร่ ถ้าไม่ได้อ่านเขียนข้อมูลจำนวนมหาศาล

07 January 2013

สอนหนังสือด้วยเครื่องมือแบบต่างๆ

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

MS PowerPoint/Apple Keynote

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

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

LaTeX Beamer/foiltex

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

Partial Note และ Visualizer

ปัญหาที่ผมพบเสมอที่ใช้สไลด์ในการสอน เกิดจากสิ่งที่ต้องแรกผมเห็นว่าเป็นข้อดี การที่สไลด์มีเนื้อหาที่จะเรียนครบถ้วนทั้งหมด ทำให้ควบคุมการบรรยายได้ง่าย เนื้อหาบางส่วนสามารถพูดอธิบายไปได้อย่างรวดเร็ว ทำให้นักศึกษาละความสนใจจากเนื้อหาได้ง่าย เพราะเห็นว่ามีเนื้อหาครบอยู่แล้ว อ่านทีหลังก็ได้ ไม่เป็นไร บางครั้งการนั่งฟังบรรยายเฉยๆ ในเวลาบ่าย ก็ทำให้เกิดความง่วงได้ง่ายๆ ความสนใจในสิ่งที่ผมบรรยายจึงน้อยลงมากๆ บางครั้งมีนักศึกษาไม่กี่คนในห้องที่พยายามตั้งใจฟัง ส่วนใหญ่หันไปสนใจสิ่งอื่นๆ รวมทั้งการหลับ แม้ว่าผมจะพยายามเขียนอธิบายเพิ่มเติมบนกระดาน แต่ก็ไม่ได้ผลมากนัก บางครั้งขนาดและตำแหน่งของกระดานก็ไม่เหมาะสมกับห้องเรียนขนาดใหญ่
หลังจากได้พูดคุยกับ @cutiening ก็ได้รับคำแนะนำและความเห็นที่น่าสนใจมากๆ เกี่ยวกับวิธีการสอน แนวคิดที่ว่าคือ การเขียนหรือจดบันทึกในสิ่งที่กำลังเรียนรู้ ทำให้นักศึกษาเกิดความเข้าใจได้มากกว่า มีการศึกษาว่าการเขียนทำให้เกิดการบันทึกลงในสมอง การทำแบบฝึกหัดก็มีส่วนสำคัญในการทำความเข้าใจในสิ่งที่การเรียนรู้ แต่การจดบันทึกที่วุ่นวายและมากเกินจำเป็น ก็อาจจะทำให้เนื้อหาที่สอนเดินไปได้ช้า
@cutiening จึงเสนอเทคนิคที่เรียกว่า partial note สำหรับทำเอกสารประกอบการสอน (แนวคิดนี้กลายเป็นบทความวิจัยเรียบร้อยแล้ว) คือ เอกสารมีเนื้อหาเกือบทั้งหมด แต่เว้นเนื้อที่ไว้ให้นักศึกษาเขียนเพิ่มเติม มีเนื้อที่สำหรับทำแบบฝึกหัด พร้อมทั้งมีแบบฝึกหัดที่หลากหลาย เริ่มจากระดับง่ายไปยาก ทำให้เพิ่มความสนใจและความเข้าใจในเนื้อหาได้อย่างดี
ผมเห็นว่าแนวคิดของ partial note น่าสนใจมากๆ ก็ลองออกแบบเอกสารประกอบการสอนใหม่ ตามแนวคิดนั้น ยังใช้ LaTeX เป็นเครื่องมือเหมือนเดิม เพียงแต่เปลี่ยนรูปแบบไป จากสไลด์ที่มีเนื้อที่จำกัด ไปเป็นเอกสารปกติ มีเนื้อหาให้นักศึกษาสำหรับจดบันทึกเพิ่มขึ้น เวลาบรรยายก็ใช้ร่วมกับ Visualizer ซึ่งฉายภาพเอกสาร หาปากกาหลายๆ สีมาใช้เขียนลงบน partial note ไปจนถึงใช้ปากกาแบบลบได้เพื่อให้เกิดความสะดวกในการแก้ไข การฉายภาพผ่าน projector ทำให้นักศึกษามองเห็นและจดตามได้อย่างชัดเจนมากกว่าการใช้กระดาน
บางครั้งก็ใช้ร่วมกับคอมพิวเตอร์ เมื่อสอนวิชาเขียนโปรแกรม เพื่อจะแสดงตัวอย่างให้นักศึกษาดูการทำงานได้ทันที การสอนแบบนี้ได้ผลตอบรับจากนักศึกษาที่ดี ก็เลยคิดจากสอนแบบนี้ต่อไปเรื่อยๆ

Partial Note กับ Tablet

หลังจากลองใช้ partial note มาจนติดใจ ก็เริ่มเพิ่มความสะดวกสบายในการสอน ด้วยการนำ tablet มาใช้ในการเขียนเนื้อหาลงใน partial note แทนที่จะเขียนลงกระดาษ เพราะสะดวกทั้งการเตรียมการ และการจัดการข้อมูลที่เกิดขึ้นภายหลัง ผมเริ่มจากใช้ iPad กับ stylus ซึ่งจะไม่ค่อยสะดวกเพราะไม่สามารถวางมือลงบนหน้าจอได้โดยตรง ต้องใช้กรรมวิธีต่างๆ ช่วย จนปัจจุบันเปลี่ยนมาใช้ Sony Vaio Duo 11 ที่สะดวกมากขึ้น เครื่องมาพร้อมกับ stylus และทำให้สามารถวางมือลงไปบนจอได้เลย (เอาไว้ค่อยเล่าถึงเรื่องนี้ให้ละเอียดทีหลัง) จุดเด่นหลักๆ ของการใช้ tablet ก็คือ เอกสารที่สอนแล้วทั้งหมดสามารถเก็บไว้เป็น PDF ได้เลย สามารถนำไปปรับปรุงเอกสารได้ในปีต่อไป และยังสวยงามอีกต่างหาก


วันนี้เอาไว้แค่นี้ก่อนละกัน เอาไว้ค่อยมาเล่าต่อ

04 January 2013

แปลง PDF ให้เป็น PDF แบบรูปภาพ

ปัจจุบันมีโรงเรียนกวดวิชาระดับมหาวิทยาลัย เกิดขึ้นเยอะแยะ แถมอาจารย์หลายคนก็มีปัญหาถูกเอาเอกสารการสอนไปใช้โดยไม่ได้รับอนุญาต ทำให้หลังๆ เลยไม่ค่อยอยากแจกเอกสารให้นักศึกษาในแบบ PDF เพราะรูปบางรูปใช้เวลาวาดอยู่นานมาก บางรูปก็ต้องเขียนโปรแกรมเพื่อสร้างข้อมูลก่อนถึงจะวาดได้ ไม่ใช่เรื่องง่าย เลยไม่อยากให้ใครเอาไปใช้ทำธุรกิจโดยเราไม่ได้อนุญาต หรือเอาไปใช้ไม่ตรงที่เราตั้งใจไว้
แต่ครั้นจะไม่แจก PDF ให้นักศึกษา ก็ทำให้ไม่ค่อยสะดวกนัก นักศึกษาหลายคนอยากใช้ tablet เพื่อจดบันทึก ตัวผมเองก็ใช้ Vaio Duo 11 กับโปรแกรม xournal และ Note Anytime เวลาสอนหนังสือ แล้วใช้ปากกาเขียนลงไปใน PDF เลย
ทางออกที่ทำตอนนี้ คือ แปลง PDF ให้เป็นรูปภาพ ความละเอียดไม่สูงนัก ให้นักศึกษาพออ่านได้ และใช้ร่วมกับ tablet ได้ แต่ถ้าใครจะเอาไปใช้อยากอื่นก็คงไม่สะดวกนัก หรือภาพที่ตัดไปก็จะไม่คมชัด เท่าเดิม เมื่อได้ทางออกนี้ก็เลยหาวิธีแปลง PDF ที่ได้จาก LaTeX ไปเป็น PDF แบบรูปภาพ โดยที่ไม่ต้องลงแรงมากสุดท้ายเลยมาลงที่ imagemagick เพราะเป็น command line ใช้ง่ายดี เลยเขียน shell script ไว้แปลงไฟล์แบบง่าย
#!/bin/bash

convert -density 200x200 $1.pdf $1-%03d.png
convert $1-*png $1i.pdf
rm $1-*png

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

28 December 2012

หัวข้ออักษรไทยใน LaTeX

ปกติผมมักจะใช้ LaTeX กับภาษาอังกฤษเป็นหลัก ด้วยหน้าที่การทำงานจะใช้ภาษาอังกฤษเป็นหลัก แต่สัปดาห์ก่อน มีความจำเป็นจะต้องเตรียมเอกสารที่เป็นภาษาไทย และจำเป็นจะต้องใช้ enumerate ที่เป็นลำดับ ก ข ค ง

หลังจากลองดูวิธีนิยาม \alph ก็เลยเลียนแบบตามข้างล่างนี้

\documentclass{article}

\usepackage{xltxtra}
\XeTeXlinebreaklocale "th"
\XeTeXlinebreakskip = 0pt plus 1pt
\setmainfont[Scale=MatchLowercase]{TH Sarabun New}

\makeatletter 
\def\thalph#1{\expandafter\@thalph\csname c@#1\endcsname} 
\def\@thalph#1{\ifcase#1\or ก\or ข\or ค\or ง\or จ\or ฉ\else\@ctrerr\fi} 
\makeatother 

\renewcommand{\theenumi}{\thalph{enumi}}

\begin{document}
 \begin{enumerate}
 \item หัวข้อแรก
 \item หัวข้อที่สอง
 \end{enumerate}
\end{document}

เมื่อคอมไพล์เอกสารนี้ด้วย XeLaTeX จะได้


แต่ตอนหลังเพิ่งมาพบว่า จริงๆ แล้ว Thai LaTeX มีคำสั่ง \thaialph กับ \thaiAlph อยู่แล้ว เพียงใช้ \usepackage[thai]{babel}

ที่มา: เรื่องวุ่น ๆ เกี่ยวกับสารบัญ

01 November 2012

เรียงหน้า PDF ด้วย PDFTK

ผมต้องการ scan เอกสาร โดยแต่ละแผ่นมีข้อความทั้งด้านหน้าและด้านหลัง แต่ scanner ที่มีอยู่ มี feeder ที่สามารถใส่เอกสารเป็นปึกได้ แต่ scan ได้แค่ด้านเดียว ผมก็เลยแก้ปัญหาด้วยการ scan สองรอบ
  • รอบแรก scan หน้าคี่ทั้งหมด  โดยเริ่มจากหน้าแรกของเอกสาร
  • รอบที่สอง scan หน้าคู่ แต่เริ่มจากหน้าสุดท้าย เพราะไม่ต้องเรียงกระดาษใหม่
สุดท้ายได้ไฟล์ PDF มาสองไฟล์ ไฟล์แรกเป็นหน้าคี่เรียงจากหน้าไปหลัง ไฟล์ที่สองเป็นหน้าคู่เรียงจากหลังมาหน้า ผมเอาสองไฟล์ที่มารวมกัน (ถ้าจะใช้ภาษาไทยแบบหรูๆ หน่อยก็ต้องเรียกว่า ผสาน มั้ง) โดยใช้ Pdftk ซึ่งเป็นโปรแกรมสำหรับช่วยจัดการเอกสาร PDF แบบ command-line ปรากฏว่าผมสามารถทำสิ่งที่ผมต้องการได้ด้วยคำสั่งเดียว คือ

$ pdftk A=front.pdf B=back.pdf shuffle A1-end Bend-1 output merged.pdf

คำสั่งนี้สั่งให้ pdftk โหลดไฟล์ PDF ขึ้นมาสองไฟล์ ไฟล์แรกเรียกว่า A ไฟล์ที่สองเรียกว่า B เสร็จแล้วใช้คำสั่ง shuffle ซึ่งเป็น operation สำหรับผสานเอกสาร 2 ฉบับ โดยกำหนดช่วงของหน้าที่ต้องการ ผมกำหนดว่าไฟล์แรกใช้ 1-end เพราะเรียงหน้าไปหลังตามปกติ ส่วนไฟล์ที่สองใช้ end-1 เพราะเรียงกลับจากหลังไปหน้า รวมเสร็จให้เอาไปเก็บไว้ในไฟล์ชื่อ merged.pdf เป็นอันเสร็จสิ้น

ทั้งๆ ที่รู้มาก่อนว่าควรจะใช้ Pdftk แต่ตอนแรกผมก็ไม่คิดว่าจะง่ายขนาดนี้ ประทับใจจริงๆ นี่แหละหนาที่ทำให้เลิกใช้ Linux ไม่ได้ (ถ้าต้องทำบน MS Windows เนี้ย ไม่รู้จะทำไงเลยนะ งมกันนานแน่ๆ)

01 September 2012

บวกเลข 128 บิต

เมื่อวานหลังจากสอนวิชา comp arch ซึ่งกำลังพูดถึงเรื่อง computer arithmetic ก็มีนักศึกษา (ที่ยังไม่ได้ถามว่าเจ้าตัวอยากจะให้ออกนามหรือเปล่า) สงสัยว่า ถ้าเราต้องการบวกเลขที่มีขนาดใหญ่กว่า 64 บิต ซึ่งเป็นขนาดที่คอมพิวเตอร์ปัจจุบันรองรับ จะทำย้งไง หลังจากอธิบายไปจนคิดว่าคนถามน่าจะเข้าใจแล้ว ก็เกิดอาการคันไม้คันมือเล็กน้อย เลยลองเขียนฟังก์ชันบวกเลขขนาด 128 บิต
ฟังก์ชันนี้ทำงานง่ายๆ คือ เก็บข้อมูลจำนวนเต็มขนาด 128 บิต โดยใช้ข้อมูลจำนวนเต็มขนาด 64 บิต 2 ตัวต่อกัน (เรียกเป็นครึ่งบน กับครึ่งล่างละกัน) เวลาจะบวกกัน ก็แค่ เอาครึ่งล่างบวกกัน เอาครึ่งบนบวกกัน แล้วถ้ามีทดจากครึ่งล่างก็ให้เอาไปบวกเพิ่มที่ครึ่งบนด้วย แค่นี้แหละ

#include <stdio.h>
#include <stdint.h>

typedef struct {
    int64_t hi;
    int64_t lo;
} int128_t;

int128_t add128(int128_t x, int128_t y) {
    int128_t z = {0,0};
    
    z.hi = x.hi + y.hi;
    z.lo = x.lo + y.lo;
    if (z.lo < x.lo) {
        z.hi++;
    }
    
    return z;
}

int main(int argc, const char * argv[])
{
    int128_t a = {0x0000000000000001, 0xffffffffffffffff};
    int128_t b = {0x0000000000000000, 0x0000000000000005};
    int128_t c;
    
    c = add128(a, b);
    
    printf("0x%016llx %016llx", c.hi, c.lo);
    
    return 0;
}

จากโปรแกรมนี้ จะได้ c = a+b โดยที่ทั้งหมดเป็นจำนวนเต็มขนาด 128 บิต ซึ่งเก็บในลักษณะ struct ประกอบด้วย hi กับ lo เป็นจำนวนเต็มขนาด 64 บิตทั้งคู่

จุดสำคัญของฟังก์ชันนี้ คือ การทดสอบว่าเกิดการทดเลขจากครึ่งล่างหรือไม่ โดยปกติ processor จะมี carry flag เอาไว้สำหรับเก็บค่าตัวทดหลังจากการบวกเลข แต่ภาษา C มีจุดอ่อนที่ไม่สามารถเรียกใช้ค่า carry flag ได้โดยตรง ถ้าจะทำแบบนั้นก็ต้องเขียน assembly ซึ่งดูไม่สะดวก ผมจึงใช้วิธีการตรวจสอบว่าเกิด overflow ขึ้นในการบวกเลขครึ่งล่างหรือไม่ ถ้าเกิด overflow ก็แสดงว่าจะต้องทดเลข หรือบวก 1 เข้าไปที่ผลบวกของครึ่งบน ตามลิงก์นี้ และต้องขอบคุณ @cutiening ที่ช่วยแสดงวิธี prove ว่า เมื่อ a+b แล้วเกิด overflow จะได้ว่าผลที่ได้ c < a และ c < b เสมอ ก็เลยได้ if statement ตามโปรแกรม เป็นอันเสร็จสิ้นการละเล่นแต่เพียงเท่านี้

ที่นี้บวกเลข 128 บิตได้แล้ว แต่ละแสดงผลลัพธ์ออกมาเป็นเลขฐานสิบได้ยังไง ก็ต้องเป็นคำถามต่อไป

16 August 2012

Virtual Method คืออะไร? (1)

Virtual method เป็นแนวคิดของ object-oriented programming ที่ไม่ค่อยเห็นกันเท่าไหร่ เพราะภาษาส่วนใหญ่ อย่างเช่น Java และ Python จะกำหนดให้ method ทุกอันเป็น virtual method ทั้งหมด คนที่เรียนใหม่ๆ จึงรับแนวคิดนี้ไปโดยไม่รู้ตัว ภาษาที่สามารถกำหนด metho d ได้ว่าเป็น virtual หรือไม่ ที่ผมพอรู้จักก็มี C++ และ C# พอดีวันก่อนผมโดนถามเกี่ยวกับเรื่องนี้ในภาษา C# ก็เลยขอเอามาเขียนเล่าไว้หน่อย เผื่อจะเป็นประโยชน์เวลาโดนถามอีก

Virtual method เกิดมาจากความคิดของ OOP ที่ต้องการขยายความสามารถของ class ที่สร้างไว้ก่อนแล้ว ด้วยวิธี inherit แล้ว override method เพื่อแก้ไขการทำงานบางส่วนของ class การใช้ virtual method ทำให้เราไม่ต้องตามไปแก้ไข method อื่นๆ ที่เรียกใช้ method ที่เราปรับปรุงทั้งหมด การระบุว่า method เป็น virtual method หมายความว่าให้เรียก method นั้นตาม object ที่สร้างขึ้นจริง ไม่ใช่เรียกตาม class ของตัวแปรที่สร้างขึ้น ลองดูตัวอย่างดีกว่า

ตัวอย่างแรกเป็น method แบบที่ไม่ใช่ virtual method

using System;

class A {
 public void print() {
  Console.WriteLine("This is A.");
 }
}

class B : A {
 public new void print() {
  Console.WriteLine("This is B.");
 }
}

class MyProgram {
 public static void Main() {
  A a1 = new A();
  a1.print();

  A a2 = new B();
  a2.print();

 }
}

โปรแกรมแรกนี้กำหนด Class A ซึ่งมี method ชื่อ print แล้วกำหนด Class B ให้เป็น subclass ของ A มี method ชื่อ print เช่นเดียวกัน (สังเกตว่าจะมี keyword ว่า new อยู่หน้า print ใน B อันนี้ C# เขาเรียกว่า method hiding คือการซ่อน method ของ superclass) เสร็จแล้วเรามี class MyProgram เอาไว้เป็น main program จะเห็นว่า ผมกำหนดตัวแปรสองตัว คือ a1 กับ a2 ตัวแปร a1 ชี้ไปที่ object ของ class A, ส่วน a2 ชื้ไปที่ object ของ class B (ปกติเราสามารถกำหนด object ของ subclass ให้กับตัวแปรของ superclass ได้อยู่แล้ว เพราะถือว่า subclass มีคุณสมบัติทุกอย่างของ superclass) เมื่อเรียกโปรแกรมนี้มาทำงาน จะได้

This is A.
This is A.

เหตุที่ผลลัพธ์เป็นอย่างนี้เพราะตัวแปร a1 และ a2 เป็นตัวแปรของ Class A เมื่อเรียก print ก็จะไปเรียก method แรกของ Class A มาทำงาน เราต้องคิดว่า object ของ Class B มีคุณสมบัติของ Class A รวมอยู่ด้วยแล้ว

ยังไม่ถึงเรื่อง virtual method เลย แต่วันนี้เอาไว้แค่นี้ก่อน วันหลังจะมาเขียนต่อ