Category Archives: OOP

ตัดคำไทยด้วย C#

WARNING: Highly technical blog ahead

กาลครั้งหนึ่งนานมาแล้ว … อาจารย์สมชายแห่ง CP เคยเอารัฐธรรมนูญฉบับเก่ากับฉบับใหม่มาเทียบความถี่ในการปรากฎของคำ ตอนนั้นผมอึ้งมากที่รู้ว่า Java มันตัดคำไทยได้ด้วย แบบ built-in มาไม่ต้องหา library อะไรมาเพิ่มเลย >_<  ถ้าผมเข้าใจไม่ผิด Java ใช้ ICU ของ IBM ที่เป็น open source แต่ตัวที่อยู่ใน class library ของ Java จะเป็นเวอร์ชันเก่ากว่าหน่อย

เจ้า ICU ที่ว่านี่นอกจากตัดคำได้ (Boundary Analysis) แล้วมันยังทำอย่างอื่นที่เกี่ยวกับกับ localization & internationalization ได้อีกมากมายก่ายกองครับ ลองเข้าไปดูกันเอง IBM ทำไว้สองชุดคือ ICU4C และ ICU4J สำหรับ C, C++ และ Java (ไม่มี .NET แป่วว) ถ้าใครทันสมัยใช้ Firefox ที่ยังตัดคำไทยไม่ค่อยจะคล่องอาจจะได้ยินชื่อนี้บ่อย เพราะมีคนไทยฮาร์ดคอร์มากมายเอาโค้ด Firefox มาแก้ใส่ ICU ให้ตัดคำแล้ว build ใหม่ … แค่ฟังก็อยากจะอ้วกออกมาเป็น pointer กับไฟล์ .h แล้วว =__=’

ด้วยความที่ 3 วันนี้ผมเมาจัดหรือยังไงไม่ทราบ เลยใช้เวลาว่างอันมีอยู่น้อยนิดไปพยายามทำ binding ให้เรียกใช้ ICU จาก C# (และ .NET) ได้ครับ! ก่อนทำก็หา best practice โดยการไปถามที่แหล่งประจำเล็กน้อย ได้คำตอบมาแค่อันเดียวน่าเศร้าใจ TvT

ผมเข้าใจว่าการทำ binding มันจะต่างจาก wrapper ตรงที่ เราต้องนำเสนอ interface เดียว (หรือคล้ายๆ) กับที่ underlying C++ classes มันใช้อยู่ให้กับผู้ใช้ ส่วนการทำ wrapper มันเหมือนกับว่าเราไปสร้างอะไรซักอย่างหุ้ม C++ classes เหล่านั้นไว้ และให้ผู้ใช้เรียกใช้งานผ่าน interface ของเรา (โดยไม่รู้ว่าข้างในมีอะไรอยู่บ้าง – Facade Pattern อ่านว่า “ฟา-ซ้าด”) ซึ่งการทำ wrapper นี่มันง่ายกว่าโขเลยน่ะ

หลังจากลองถูๆไถๆ ใช้พลังกับ C++/CLI เหมือนที่เคยใช้ในซีเนียร์โปรเจค ก็ออกมาเป็นรูปเป็นร่าง อยู่ที่ ICU4NET สามารถลองดาวน์โหลดไปใช้ได้ ถึงชื่อจะบอกว่า ICU4NET แต่จริงๆแล้วตอนนี้มันมี class ที่ใช้งานได้อยู่ class เดียวคือ BreakIterator =_=” ตั้งใจไว้ว่าจะจัดการกับ class ในกลุ่ม boundary analysis ให้หมด

เอาโค้ดตัวอย่างให้ดูเล็กน้อย อันแรกเป็นแบบดั้งเดิม ลักษณะเดียวกับที่เขียนใน ICU4J และ ICU4C

private List<string> WordBreak(string text)
{
var sb = new StringBuilder();
var col = new List<string>();

using (BreakIterator bi = BreakIterator.CreateWordInstance(Locale.GetUS()))
{
bi.SetText(text);
int start = bi.First(), end = bi.Next();
while (end != BreakIterator.DONE)
{
col.Add(text.Substring(start, end - start));
start = end; end = bi.Next();
}
}

return col;
}

ส่วนอันนี้ผมเขียน Extension Method เพิ่มให้มัน return เป็น IEnumerable ได้ พวกขา 3.5 จะได้เล่นซนแบบนี้ได้ :’)

using (BreakIterator bi = BreakIterator.CreateWordInstance(Locale.GetUS()))
{
bi.SetText(uxText.Text);

// requires ICU4NETExtension to use Enumerate extension method
MessageBox.Show(string.Join(Environment.NewLine, bi.Enumerate()
.GroupBy(w => w)
.OrderBy(x => x.Count())
.Reverse()
.Select(x => x.Key + " : " + x.Count())
.Take(10)
.ToArray()));
}

ตอนแรกตั้งใจว่า ส่วนที่น่าจะเอาไปเล่นได้หลักๆคงเป็นพวก ASP.NET Web App แหละ แต่นั่งคิดๆดูแล้วมันมีส่วนประกอบที่เป็น library native ของ ICU หลายอันอยู่ ถ้าเข้าใจไม่ผิดคงมีปัญหากับเรื่อง trust level ของ IIS พอสมควร …

ผลพลอยได้จากการผลาญเวลา (แบบไร้เหตุผล เอามันส์ล้วนๆ) ครั้งนี้คือ ได้ศึกษา C# เพิ่มอีกนิดหน่อย แล้วก็ลองใช้ C++/CLI อีกนิสสสนึง หลังๆมานี่อยู่ที่ทำงานได้ใช้แต่ C++ จนรู้สึกตัวเองตามโลก C# ไม่ทัน มาอ่านโค้ดของ @chakrit (ขา 3.5 ตัวพ่อ) ทีนี่นั่งงงอยู่หลายนาที –..-‘ เออออ … น้ำหลักลดด้วยนะ ^ ^

จากประสบการณ์ ด้วยหัวข้อยบลอกประมาณนี้ ผมเชื่อว่าคงมี นิสิต/นักศึกษา ที่กำลังปั่นงานของอาจารย์แล้ว search มาเจอ และอยากนำไปใช้ได้โดยเร็ว … ผมขอร้องว่าก่อนจะทิ้งคอมเม็นต์ไว้หรือเมล์มาถาม technical issue กับผม ช่วยศึกษาเรื่องพื้นฐาน C# กับเรื่องพวกวิธี reference รวมถึงอ่าน Readme ก่อน แล้วค่อยถามมานะครับ …

ผมตั้งเป้าไว้ว่าจะมีคนเอาไปใช้ประโยชน์ได้ 2 คนขึ้นไป ใครเอาไปใช้ทำอะไรบอกด้วยๆ :’)

ICU4NET – ICU Binding for .NET

WordBreak

เรียนรู้ไพธอน

python-logo

เพิ่งเริ่มเห็นว่าเป็นภาษาที่เป็นประโยชน์หลายอย่างเมื่อไม่นานมานี้ครับ ยกตัวอย่างเช่น

  • Google App Engine ก็ใช้ Python ได้
  • .NET ก็มี IronPython ให้ใช้
  • บน Linux และ Unix ก็มักจะมีตัวแปลภาษา Python ติดตั้ง ใช้เป็นคงช่วยงานพวก automation ได้ระดับนึง โดยไม่ต้องไปศึกษาพวก shell script ที่ดูยากกว่า
  • ไลบรารีเจ๋งๆหลายอย่างของ C++ ที่อาจจะไม่มี “ตัวหุ้ม” (Wrapper) สำหรับภาษาอื่น แต่ก็มักจะมีของ Python ให้เห็นอยู่เนืองๆ เช่น pyOgre, pyQT, …

ก่อนหน้านี้ซักระยะนึง เคยอ่าน Dive into Python ครับ เค้าบอกว่าเป็น Python สำหรับโปรแกรมเมอร์มีกระสบการณ์ เข้าไปอ่านก็อ่านรู้เรื่องนะ แต่มันใช้พลังมากไปหน่อย ก็เลยเลิกอ่าน …

ความพยายามครั้งใหม่เลยลองไปหาหนังสือที่ง่ายกว่ามา คือ Byte of Python อันนี้เป็นสำหรับมือใหม่จริงๆ อธิบายค่อนข้างละเอียด คนเขียนโปรแกรมเป็นแล้วก็พอเอาไปอ่านผ่านๆเร็วๆได้ อ่านจบจะเริ่มเข้าใจ syntax ของ Python มากขึ้น แล้วเขียนโปรแกรมง่ายๆได้ หลังจากจบเล่มนี้ก็เริ่มมีความมั่นใจมากขึ้นที่จะกลับไปอ่าน Dive into Python

หลังจากที่เริ่มเข้าใจ syntax ของภาษา และการเอา module ภายนอกมาใช้ ก็เลยเขียนโปรแกรมง่ายๆเก็บไว้ดู เป็นโปรแกรม TermTweet โปรแกรมเดียวกับที่ใช้เปรียบเทียบ C++ และ C# เมื่อวันก่อน มาเขียนใหม่ ผลลัพธ์ … ต้องบอกว่าแอบอายแทนซีชาร์ปลูกรักเล็กน้อย

>_<

PyTermTweet

import re
import urllib2
from xml.dom.minidom import parse,getDOMImplementation

if __name__ == '__main__':
target = 'http://twitter.com/statuses/user_timeline/6380022.rss'
res_file = urllib2.urlopen(target)
dom = parse(res_file)

elements = dom.getElementsByTagName('item')
for elem in elements:
text = elem.childNodes[1].firstChild.data
url = re.search(r'(http://[^\s]*)', text)
print text
if url != None:
print "link: ", url.group(1)
print '--------------------------------'
 

สรุปข้อดีที่พบเห็น ดังต่อไปนี้

  • การใช้การจัดย่อหน้าแทน block ทำให้ไม่ต้องใช้ { และ } ทำให้โค้ดอ่านง่ายขึ้น ล่ะมั้ง
  • เขียนๆไปแล้วรู้สึกตลอดเวลา ว่า Python ออกแบบมาให้พิมพ์สั้น และมีความหมายที่สุด เช่น else if กลายเป็น elif หรือ Null/Nil กลายเป็น None หรือ regex กลายเป็น re หรือ การตัด do-while ออกไป และอีกมากมาย
  • โครงสร้างข้อมูลที่ใช้กันบ่อยๆในการเขียนโปรแกรม เช่น list หรือ dictionary มีให้ใช้ทันที และใช้ง่ายมาก (dict = {‘A’:4.0 , ‘A+’: 3.5}) ต่างจากภาษาพวก .NET หรือ Java
  • ด้วยความที่เป็น Dynamic Typing ทำให้ไม่ต้องระบุประเภทของตัวแปร โดยตัวแปลภาษามันจะเข้าใจได้เองว่าแต่ละตัวเป็น Type อะไร โค้ดเลยค่อนข้างกระชับ อย่างที่เห็นด้านบน

ณ วันเดียวกัน ไอกร ก็บ่นมาว่า ภาษามันไม่มี interface ให้ใช้ ก็เลยไปอ่านเจอเรื่อง Duck Typing สรุปก็คือว่า Python มันไม่มี Constraint ในรูปแบบของการ Inheritance หรือการ implement interface

สมมติว่ามี method นึงรับพารามิเตอร์เข้ามา แล้วเรียกใช้ method Fly() กับ Sleep() อะไรก็ตามที่ implement สอง method นี้ก็ pass ไปให้ได้ทั้งนั้น มันเลยเป็นที่มาของชื่อ Duck Typing

“ถ้าเห็นนกที่เดินได้เหมือนเป็ด ว่ายน้ำได้เหมือนเป็ด
ร้องได้เหมือนเป็ด นกตัวนั้นเป็นเป็ด”

ข้อเสียของ Duck Typing ตามที่มีการวิพากษวิจารณ์กัน คือ มันทำให้การหลีกเลี่ยงข้อผิดพลาดที่เกิดจากการใช้ Type ผิดนั้น ต้องใช้ “แรง” ในการ Test เยอะขึ้นพอสมควร แล้วการเขียนหรือพัฒนาต่อโดยไม่ทำให้เกิดข้อผิดพลาดด้าน Type เพิ่มขึ้นในอนาคตนั้น ก็ต้องใช้ความเข้าใจในตัวโค้ดที่ทำงานด้วยในระดับหนึ่ง รวมถึงต้องการ Documentation ที่ชัดเจนและละเอียด .. ตรงกับที่ไอกรบ่นเมื่อวานเป๊ะๆ ว่ามันต้องไปไล่อ่าน Class เพิ่มอีก 4 – 5 Classes

เรียนรู้กันต่อไปครับ 🙂

การทำ Serialization และ Deserialization ใน C#

เป็นหัวข้อน่ารู้อีกอย่างนึงครับ สำหรับเรื่องของ Serialization และ Deserialization

ตามนิยาม (ในวิกิพีเดีย) เค้าบอกไว้ว่า

Serilization คือกระบวนการแปลงวัตถุให้กลายเป็นสายข้อมูลในรูปแบบบิต ทำให้สามารถเก็บรักษาวัตถุดังกล่าวไว้บนสื่อเก็บข้อมูล (เช่น HDD) หรือจะเอาไปส่งผ่าน Network ก็ได้

Deserilization คือกระบวนการย้อนกลับของ Serialization คือการแปลงจากสายข้อมูลในรูปแบบบิตให้กลับมาเป็นวัตถุของเรานั่นเอง

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

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

วิธีการทำใน C# มีขั้นตอนดังต่อไปนี้ ยกตัวอย่างจากคลาสที่เขียนในวันนี้

  1. เพิ่ม Attribute Serializable เข้าไปที่หัว Class ก่อน
  2. ให้ Class implements ISerializable ต้องเขียน method GetObjectData เพิ่ม
  3. ทำ Deserialization Constructor ที่มีรูปแบบตามที่กำหนด
  4. เพิ่มลอจิกส่วนที่เป็นการทำ Serialize และ Deserialize

ดูโค้ดดีกว่า

ส่วนของ ToBinaryFile และ FromBinaryFile เป็นส่วนของข้อ 4 นะครับ

เวลาจะทำ Serialize ก็เรียก ToBinaryFile("filename.osl") และเวลาจะ Deserialize ก็เรียก Word.FromBinaryFile("filename.osl")

[code:c#]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace WordParser
{
    [Serializable()]
    class Word : ISerializable
    {
        public string Value { get; set; }
        public string Description { get; set; }
        public int Popularity { get; set; }

        public Word()
        {

        }
       
        public Word(SerializationInfo info, StreamingContext context)
        {
            Value = (string)info.GetValue("Value", typeof(string));
            Popularity = (int)info.GetValue("Popularity", typeof(int));
            Description = (string)info.GetValue("Description", typeof(string));
        }

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Value", Value);
            info.AddValue("Popularity", Popularity);
            info.AddValue("Description", Description);
        }

        #endregion

        public static Word FromBinaryFile(string file)
        {
            using (Stream stream = File.Open(file, FileMode.Open))
            {
                BinaryFormatter bformatter = new BinaryFormatter();
                Word ret = (Word)bformatter.Deserialize(stream);
                return ret;
            }

           
        }

        public void ToBinaryFile(string file)
        {
            using (Stream stream = File.Open(file, FileMode.Create))
            {
                BinaryFormatter bformatter = new BinaryFormatter();
                bformatter.Serialize(stream, this);
            }

        }

    }
}

[/code]

บั๊กที่เกิดบ้างไม่เกิดบ้าง

เป็นโปรเจคที่ทำอยู่ที่ฝึกงานครับ

แบ่งงานกับเพื่อนร่วมทีมเขียน เพื่อนร่วมทีมเขียน Data Access Layer ก็คือคลาสที่ห่อหุ้มการทำงานกับฐานข้อมูลทั้งหมดนั่นแหละ (ฐานข้อมูลที่ใช้คือ IBM DB2 ครับ ชื่อเก่า Cloudscape)  ส่วนผมเขียนส่วนที่เป็น Business Logic แล้วก็ทำ Presentation Layer (เป็น Web-based)

จริงๆแล้วมีวิธีมากมายที่จะสร้างเจ้าตัว DAL ขึ้นมาได้ ทั้งแบบอัตโนมัติ และไม่อัติโนมัติ แต่วิธีที่เราใช้กันเป็นวิธีที่คลาสสิกที่สุด คือการประกอบคำสั่ง SQL ขึ้นมาเอง ส่วนที่มีปัญหาก็คือเรื่องของ DateTime นี่แหละ

วิธีการที่ใช้ประกอบค่า DateTime ขึ้นมา คือเอา วัน เดือน ปี ชั่วโมง นาที วินาที มาต่อกัน เช่น 19-06-2008 15:43:12 เป็นต้น แต่ปรากฎว่าเพื่อนผมลืมไปว่า ถ้าเวลาตอนนั้นค่าของหลักวินาทีเป็นเลขหลักเดียว จะได้ String ผิดๆออกมา เช่น 19-06-2008 15:43:6 (จริงๆแล้วควรเป็น 19-06-2008 15:43:06) ตอนเอางานมารวมกันก็เลยมีปัญหาดังกล่าวเกิดขึ้นบ้างไม่เกิดขึ้นบ้าง เล่นเอางงอยู่นาน ถ้าประมาณคร่าวๆ โอกาสที่จะเกิดข้อผิดพลาดระหว่างการทำงานก็เป็น 1/6 ล่ะครับ

ก็ให้ข้อสังเกตไว้ครับ ว่าพวกข้อผิดพลาดที่เกิดบ้างไม่เกิดบ้าง เป็นไปได้สูงว่าจะเกี่ยวข้องกับเรื่องของเวลา (เช่นเดียวกับการ Random) เห็นเพื่อนบ่นเรื่องบั๊กที่พอจะแก้ก็หายไปแล้วไว้ใน Twitter ก็เลยอยากจดเรื่องนี้ไว้ซักหน่อย