Saturday, September 17, 2005

คนตาบอดที่ป้ายรถเมล์

วันก่อน ระหว่างที่รอรถเมล์กลับบ้าน
ผมเหลือบไปเห็นคนตาบอด 2 คน กำลังรอรถเมล์อยู่
เป็นผู้ชายคน กับผู้หญิงคน
ผู้หญิงลงไปยืนในถนน 1 ก้าว
แวบแรกที่เห็น ก็เกิด บทสนทนาในใจ
"เอ๊ะใช่คนตาบอดหรือเปล่า"
"แต่เขามีไม้เท้าทั้งคู่นี่ ต้องเป็นคนตาบอดแน่เลย"
แวบที่สอง ก็ตามมา
"เราควรจะเข้าไปช่วยเขาดีหรือเปล่า?"
"เขามีใครคอยช่วยอยู่หรือยัง?"
หลังจากปล่อยให้เกิดบทสนทนาในใจอยู่พักหนึ่ง
ผมก็เลยเดินเข้าไปช่วยพาขึ้นรถ
(สำหรับคนที่ไม่เคยพาคนตาบอดเดิน
ขอแนะนำให้ใช้วิธี ให้เขาจับแขนบริเวณข้อศอกเรา
วิธีนี้เพื่อนตาบอดคนหนึ่งของผม เขาแนะนำมา)

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

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

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

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

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

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

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

นึกถึงเรื่องการทดลองนี้แล้วนึกถึง
การทดลองที่ Reader digest เคยทำ
เขาทดลองวางกระเป๋าสตางค์ (ที่มีที่อยู่เขียนไว้ข้างใน) ทิ้งไว้ตามที่ต่างๆ
แล้วแอบดูว่าคนที่เก็บมีลักษณะท่าทางอย่างไร
จากนั้นก็คอยติดตามว่า มีการติดต่อหรือส่งคืนกระเป๋าสตางค์หรือไม่
Reader digest เขาทดลองกับหลายๆประเทศใน Asia
ผลที่ได้ค่อนข้างตรงกันอย่างหนึ่งก็คือ
ถ้าคนที่เก็บได้เป็นคนที่ฐานะไม่ค่อยดี
มันจะได้กระเป๋าพร้อมเงินคืน
ส่วนกรณีที่คนเก็บได้ดูดีมีฐานะ
ก็จะไม่ได้กระเป๋าคืน

ที่ติดใจก็คือ ที่เมืองไทย
มีกระเป๋าใบหนึ่งถูกทดลองทิ้งไว้ใน มหาวิทยาลัยจุฬาฯ
ผลเป็นไปตามที่คาด
กระเป๋านั้นไม่ได้ถูกพบเห็นอีกเลย
(คนเก็บได้เป็นนิสิต)

สำหรับตัวผมเอง
ผมคิดว่าทั้งเรื่องคนตาบอดหรือเรื่องกระเป๋าสตางค์
มีรากเหง้าตัวหนึ่งที่ตรงกันก็คือ "Ignorance"
..

จบดีกว่า ปัญหาพวกนี้คุยกับข้ามคืนก็ไม่จบ

Related link from Roti

Monday, September 12, 2005

Hierarchical Data in Database

ปกติผมจะเก็บ tree structure ใน db ด้วย schema แบบนี้

create table foo (
id long,
....
parent_id long
)


ซึ่งก็ ok แหล่ะ แต่การท่อง tree หรือหา path ของ tree ค่อนข้างกิน io ไปนิด
ซึ่งปกติที่ทำก็คือ load ทั้งหมดมา build เป็น graph structure แล้วเก็บ
มันไว้ใน cache

วันนี้ไปอ่านเจอที่ Gijs Van Tulder เขียนไว้ ในเรื่อง
Storing Hierarchical Data in a Database
ก็เลยได้เปิดหูเปิดตา ว่ามันมี technique อื่นที่ช่วยไม่ให้
การ select หา subtree หรือ path สามารถทำได้ใน select เดียว

ดยเขาจะสร้าง table ให้มี structure แบบนี้

create table foo (
id long,
..
parent_id long,
left_id long,
right_id long
)

ก็คือนอกจากจะมีการเก็บ parent id แล้ว
ก็ยังเก็บค่า left, right ไว้ด้วย
ซึ่งค่านี้ได้จากการ generate โดยการท่อง tree
แบบ Depth-First search

เวลาจะ select หา subtree ก็แค่

select * from foo where left_id between x and y

ค่า x และ y ก็คือ ค่า left_id, right_id ของ node ที่ต้องการหา subtree

...
มีที่น่าสนใจอีกเยอะเลย เช่นจะ select หา path ยังไร,
ผลลัพท์ที่ได้จะสร้างเป็น tree ได้อย่างไร,
การ add note เข้าไปใหม่ จะต้อง update ค่า left, right อย่างไร
ลองไปอ่านดูแล้วกันครับ

Note: อ่านแล้วก็อดนึกถึงคำพูดที่ว่า พวกเด็กที่เรียกไฟฟ้าเขียนโปรแกรมกันทื่อๆ
ก็ถูกของเขาแหล่ะ เพราะเราไม่ได้เรียนมาทาง computer โดยตรง
ก็เลยไม่ได้ผ่านหูผ่านตากับพวก algorithm แบบนี้

Related link from Roti

Commons CLI

วันนี้เขียนโปรแกรม utility เล็กๆ ที่สามารถ run จาก command line ได้
ก็เลยมองหา api ที่ช่วย parse argument
Commons CLI ของ jakarta เป็น api ที่ช่วยในการ parse argument
โดยมีวิธีการใช้ดังนี้

  • ระบุ Options ที่โปรแกรมเรารับได้
    Options opt = new Options();
    opt.addOption("d", true, "extract only specific date");
    opt.addOption("w", true, "extract only week that contain given date");
    opt.addOption("m", true, "extract for specific month,year");

    parameter ตัวที่ 2 ที่เรา pass ให้ method addOption ก็คือ การระบุว่า
    option ตัวนี้ต้องมี argument ตามมาด้วยหรือไม่
    อย่างในตัวอย่างที่เขียนนี้ ผู้ใช้สามารถเรียกใช้โปรแกรมได้ดังนี้
    program -d yymmdd -w yymmdd -y yymm infile outfile

    กรณีที่เราต้องการให้เลือกเฉพาะ option ใด option หนึ่งเท่านั้น
    ก็สามารถเขียนได้ดังนี้
    Options opt = new Options();
    OptionGroup grp = new OptionGroup();

    grp.addOption(new Option("d", true, "extract only specific date"));
    grp.addOption(new Option("w", true, "extract only week that contain given date")
    );
    grp.addOption(new Option("m", true, "extract for specific month,year"));

    opt.addOptionGroup(grp);

    ก็คือเราเปลี่ยนไปใช้ Optiongroup ในการ control ไม่ให้มีการเลือก
    มากกว่า 1 choice พร้อมๆกัน

  • ทำการ parse String[] args โดยระบุ parser ซึ่งเลือกได้ 3 แบบคือ BasicParser, GnuParser, PosixParser
    PosixParser parser = new PosixParser();
    CommandLine cmdline = parser.parse(opt, args);


  • เลือกใช้งาน argument หรือ option ผ่านทาง method getOptionValue
    หรือ getArgs()
    CommandLine cmdline = parser.parse(opt, args);

    File inFile = new File(cmdline.getArgs()[0]);

    if (cmdline.hasOption("d")) {
    Calendar[] ranges = parseDate(cmdline.getOptionValue("d"));
    ...
    }
    if (cmdline.hasOption("w")) {
    filter.setWeek(parseWeek(cmdline.getOptionValue("w")));
    }

Related link from Roti

NetBeans 5.0 Splashscreen

ดูดุดันขึ้นเยอะเลย



Related link from Roti

Hibernate Generic DAO + JDK5.0

อ่านเจอใน blog ของ Hibernate Link
เป็น idea ที่ดีมาก ใช้แต่ 1.4 จนลืมนึกถึง feature generic ของ 1.5 ไปเลย

ตัวอย่างที่เขาใช้
public interface GenericDAO<T, ID extends Serializable> {

T findById(ID id, boolean lock);

List<T> findAll();

List<T> findByExample(T exampleInstance);

T makePersistent(T entity);

void makeTransient(T entity);
}

public interface ItemDAO extends GenericDAO<Item, Long> {

public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";

Bid getMaxBid(Long itemId);
Bid getMinBid(Long itemId);

}

Related link from Roti

Sunday, September 11, 2005

Eclipse RCP & ClassLoader & Spring

เมื่อก่อนที่เคยเขียน RCP เคยพยายาม integrate
SpringFramework เข้ามาใช้ แต่ติดปัญหาว่า
เรื่องการ load class
โดย spring จะเรียกใช้ current ClassLoader โดยวิธี
Thread.currentThread().getContextClassLoader();

(Hibernate ก็ใช้วิธีนี้เหมือนกัน เข้าใจว่า framework ที่เกิดกับ
serverside นิยมใช้วิธีนี้กัน)
ซึ่งเมื่อนำมาใช้กับ Eclipse จะเกิดปัญหาทันที
เนื่องจาก classloader ที่ได้ มันจะเป็นคนละตัวกับ plugin ที่เราเขียน
ทำให้มองไม่เห็น class ที่เรา provide ให้

วิธีแก้ที่เห็นคนอื่นทำ ก็คือเขาจะ override ClassLoader ของ current thread
ชั่วคราว โดยทำ ณ ขณะที่ทำการ load ApplicationContext
public void start(BundleContext context) throws Exception {
super.start(context);

ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
applicationContext = new ClassPathXmlApplicationContext(SPRING_CONFIGS)
;
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
}


Note: คิดว่าวิธีนี้ยังไม่ support กรณีเราออกแบบให้มีการ merge
spring config file จากหลายๆ plugin เข้ามารวมที่ context เดียว

Related link from Roti

fvLogger, JavaScript Logging

อ่านเจอใน JavaScript Logging
เป็น api แนวเดียวกับ log4j แต่ใช้กับ javascript

วิธีการใช้
include logger.js กับ logger.css
<script type="text/javascript" src="logger.js"></script>
<link rel="stylesheet" type="text/css" href="logger.css" />

บริเวณที่ log information จะใช้ div แบบนี้
<div id="fvlogger">
<dl>
<dt>fvlogger</dt>
<dd class="all"><a href="#fvlogger" onclick="showAll();" title="show all" id="abcdef">all</a></dd>
<dd class="debug"><a href="#fvlogger" onclick="showDebug();" title="show debug" id="showDebug">debug</a></dd>
<dd class="info"><a href="#fvlogger" title="show info" id="showInfo">info</a></dd>
<dd class="warn"><a href="#fvlogger" onclick="showWarn();" title="show warnings" id="showWarn">warn</a></dd>
<dd class="error"><a href="#fvlogger" onclick="showError();" title="show errors" id="showError">error</a></dd>
<dd class="fatal"><a href="#fvlogger" onclick="showFatal();" title="show fatals" id="showFatal">fatal</a></dd>
<dd><a href="#fvlogger" onclick="eraseLog(true);" title="erase">erase</a></dd>
</dl>
</div>




ถ้าไม่ต้องการหรูมากนัก​ ก็ใส่เพียงเท่านี้ก็พอ
<div id="fvlogger"></div>




เวลา log ก็เรียก function debug, info, warn, error, fatal
function foo() {
debug("foo start");
....
}

Related link from Roti