ไม่ได้เขียนอะไรใหม่ๆ ที่นี่มาเกือบครึ่งปี เพราะงานยุ่ง แล้วก็ยังเสพติด twitter และ facebook งอมแงมอีกต่างหาก วันนี้มีเวลาว่าง (จริงๆ ก็ไม่ว่างหรอก แต่ขี้เกียจทำงานที่ควรจะทำ) เลยเอา Go Programming Language ที่พัฒนาโดย Google มาลองเล่นดู เนื่องจากผมใช้ Mac OS X ซึ่งเป็นแพลตฟอร์มที่สนับสนุนอยู่แล้ว ก็เลยติดตั้งไม่ยาก แค่มี XCode อยู่ แล้วลง mercurial เพิ่ม ก็สามารถโหลดซอร์สโค้ดคอมไพเลอร์ของ Go มาคอมไพล์เองได้เลย ใช้เวลาคอมไพล์ตัวคอมไพเลอร์สั้นมาก แป๊บเดียวเสร็จ ประทับใจพอสมควร
จากนั้นก็เลยลองเขียนโปรแกรมแบบมั่วๆ (เพราะยังงงกับ syntax อยู่) เพื่อแก้ปัญหา N-Queens แบบง่ายๆ (ใช้ backtracking search ด้วย recursive ดื้อๆ เอานี่แหละ) โดยใช้ Go:
package main
import "fmt"
var N int = 10
func nqueens(board []int, filled int) int {
var attack bool = false;
if filled == N-1 {
for i:=0; i<N; i++ {
fmt.Printf("%d\t", board[i])
}
fmt.Printf("\n");
return 1
}
filled += 1;
for i:=0; i<N; i++ {
board[filled] = i;
attack = false;
for j:=0; j<filled; j++ {
dy := board[j]-board[filled];
dx := j - filled;
if (board[j] == board[filled]) ||
(dy/dx == 1 || dy/dx == -1) && (dy%dx == 0) {
attack = true;
break
}
}
if !attack {
nqueens(board, filled)
}
}
return 0
}
func main() {
var board []int = make([]int, N);
nqueens(board, -1);
}
ด้วยความอยากรู้ ก็เลยลองเอาโปรแกรมนี้เอามาแปลงเป็นภาษา C แบบตรงๆ บรรทัดต่อบรรทัด
#include<stdio.h>
#include<stdlib.h>
const int N=10;
int nqueens(int *board, int filled) {
int i,j,attack,dy,dx;
if (filled == N-1) {
for(i=0; i<N; i++)
printf("%d\t", board[i]);
printf("\n");
return 1;
}
filled++;
for(i=0; i<N; i++) {
board[filled] = i;
attack = 0;
for(j=0; j<filled; j++) {
dy = board[j]-board[filled];
dx = j - filled;
if ((board[j] == board[filled]) ||
((dy/dx == 1 || dy/dx == -1) && (dy%dx == 0))){
attack = 1;
break;
}
}
if (!attack)
nqueens(board, filled);
}
return 0;
}
main() {
int *board = (int *)malloc(sizeof(int)*N);
nqueens(board, -1);
}
เสร็จแล้วก็ลองเขียน Python อีกโปรแกรมหนึ่ง
def nqueens(board, filled):
global N
if filled == N-1:
for i in board:
print "%d\t" % board[i],
print
return True
filled+=1
for i in xrange(0, N):
board[filled] = i
attack = False
for j in xrange(0, filled):
dy = board[j]-board[filled]
dx = j - filled
if board[j] == board[filled] or ((dy/dx == 1 or dy/dx == -1) and dy%dx==0):
attack = True
break
if not attack:
nqueens(board, filled)
return 0
N=10
board = [0]*N
nqueens(board, -1)
ทดลองรัน (ด้วย N=10) แล้วจับเวลาง่ายๆ ด้วย time ก็จะได้ผลเป็น
|
Go |
C |
Python |
| real |
0m0.165s |
0m0.041s |
0m1.048s |
| user |
0m0.116s |
0m0.029s |
0m1.028s |
| sys |
0m0.040s |
0m0.002s |
0m0.015s |
ประสิทธิภาพแบบคร่าวๆ ก็ถือว่าใช้ได้ทีเดียว ไม่ได้แย่กว่า C นัก แต่ไม่รู้ว่าผมชินกับภาษา C มากเกินไปหรือเปล่า ทำให้ผมรู้สึกแปลกๆ งงๆ กับ syntax ของ Go มันดูไม่ค่อยสวยงามยังไงไม่รู้ บอกไม่ถูก เหมือนแค่เอา C มาตัดบางส่วนออก เพราะกลัวจะพิมพ์เยอะเกินไป มันเลยดูขัดๆ อีกอย่าง Go ก็ไม่ได้เขียนสั้นๆ ง่ายๆ ได้เหมือน Python ผมว่าโครงสร้าง syntax ของ C กับ Python สวยงามกว่าเยอะ แต่ผมก็ยังไม่ลอง concurrent programming ที่เป็นจุดขายของ Go เหมือนกัน เอาไว้หนีงานมาลองใหม่คราวหน้าละกัน
programming
C, go, programming, python
เมื่อวานนี้อ่านข่าวบน /.jp แล้วเจอบทสัมภาษณ์ประธานบริษัท NTT Data ซึ่งเป็นบริษัท IT ในกลุ่ม NTT
若い時にプログラムを書こう、必ず人生の豊かさにつながる
– 山下徹 NTTデータ社長
แปลเป็นไทยได้ความว่า
เขียนโปรแกรมกันไปเถอะในช่วงที่อายุยังน้อย, สิ่งเหล่านี้จะเชื่อมโยงกับความรุ่มรวยในชีวิตอย่างแน่นอน
– โทรุ ยะมะชิตะ ประธานบริษัทเอ็นทีทีดาต้า
ถ้าอ่านบทสัมภาษณ์ฉบับเต็มแล้ว จะมีประเด็นต่างๆ อยู่หลายประเด็น แต่ในฐานะคนสอนหนังสือเด็กปริญญาตรี ผมสนใจประเด็นการเขียนโปรแกรมเป็นหลัก เพราะปัจจุบันผมรู้สึกว่าตัวเองยังไม่สามารถทำให้เด็กๆ ที่สอนอยู่ หันมาชอบ สนใจและตั้งใจที่จะเขียนโปรแกรมคอมพิวเตอร์ได้ แม้ว่าเด็กๆ เหล่านั้นจะเรียนในสาขาที่โลกภายนอกคาดหวังว่า เมื่อจบแล้วจะต้องมีทักษะในการเขียนโปรแกรมที่เพียงพอ
จริงอยู่ว่าเด็กส่วนใหญ่จะพอเขียนโปรแกรมได้ อาจจะใช้วิธีตัดแปะเอาบ้าง หาทางเอาตัวรอดกันไปได้ แต่ส่วนใหญ่มักจะรู้สึกว่าการเขียนโปรแกรมเป็นเรื่องยาก เป็นเรื่องน่าเบื่อ เป็นงานคนชั้นล่าง (เหมือนผู้ใช้แรงงานในสังคมไอที) โดยเฉพาะค่านิยมอันหลังนี้ เป็นสิ่งที่ผมไม่เห็นด้วยที่สุด เพราะผมเชื่อว่าเราไม่สามารถเป็น system analyst เป็น project manager ที่ดี หรือเป็นตำแหน่งต่างๆ ที่บางบริษัทบอกว่าเป็นเหมือนยอดปิระมิดได้ โดยไม่มีพื้นฐาน ทักษะการเขียนโปรแกรม หรือไม่รู้ว่าถ้าออกแบบไปอย่างนี้แล้วคนที่เอาไป implement เขาจะทำยังไง
ผมตั้งใจว่าต่อไปนี้ผมพยายามลบค่านิยมนี้ออกจากเด็กๆ ที่เข้ามาเรียนในภาค แม้ว่ามันจะเป็นเรื่องยาก เพราะแนวความคิดว่างานบริหารจัดการเป็นงานสบายเป็นเรื่องที่ฝังรากลึกไปแล้ว รวมถึงการเสนอทางเลือกที่ดูเหมือนดีกว่า ว่าการเรียนวิธีการบริหารจัดการโดยตรงก็สามารถเข้าใจวิธีการ และเทคนิคต่างๆ ได้ สามารถเริ่มงานโดยเป็นผู้บริหารได้เลย ไม่จำเป็นต้องทำงานต๊อกต๋อยเหมือนพวกที่เรียนเขียนโปรแกรม เขาทำแค่ออกแบบ เสนอแนวความคิด ที่เหลือก็ปล่อยให้ใครก็ไม่รู้ทำก็หมดเรื่อง
จริงอยู่ ที่เด็กที่จบจากภาคผมทุกคนไม่จำเป็นต้องไปทำงานเขียนโปรแกรม มีงานที่น่าสนใจอีกเยอะ แต่ผมก็เชื่ออย่างประโยคข้างบนว่า ทักษะการเขียนโปรแกรมจะชักนำไปสู่ความรุ่มรวยในชีวิตการทำงาน ทำให้เรามีทักษะที่เพียงพอในการทำสิ่งต่างๆ สำเร็จไปด้วยดี
ปัญหาอย่างหนึ่งสำหรับตัวผมเอง ก็คือ ผมรู้สึกว่าผมยังไม่รู้ ไม่มีวิธีดีๆ ที่จะสอนให้เด็กๆ เข้าใจความสนุกสนาน แล้วความน่าสนใจของการเขียนโปรแกรมได้ คงต้องพยายามกันต่อไป
อ้างอิง: http://slashdot.jp/developers/09/06/05/066228.shtml
thought
programming, teaching
หลังจากถูกถล่มด้วยงานต่างๆ ทำให้ไม่ได้มาเขียนสองเดือนกว่าๆ พอดีช่วงนี้ช่วยจัดงาน PAKDD2009 อยู่ และมีงานที่เกี่ยวข้องที่ต้องใช้สคริปต์ เลยเอามาจดไว้ที่นี่หน่อยกันลืม
เรื่องของเรื่องก็คือผมต้องการส่งอีเมลไปยังคนหลายๆ คน โดยมีเนื้อหาที่แตกต่างกันเล็กน้อย ผมเลยเริ่มจากเปิด OpenOffice.org Spreadsheet ขึ้นมาเขียนข้อมูลต่างๆ ที่ต้องการแปะลงไปในจดหมาย แล้วเก็บเป็นไฟล์ CSV (Comma-Separated Value) ที่เป็นสำหรับเก็บข้อมูลแบบง่ายที่สุด แต่ละบรรทัดแทนข้อมูลแต่ละเรคอร์ด และใช้เครื่องหมาย “,” คั่นระหว่างข้อมูลแต่ละฟิลด์ จากนั้นก็เขียนสคริปต์ Python เพื่ออ่านข้อมูลจากไฟล์ CSV ที่ทำไว้ เอาไปแปะในเทมเพลตที่เตรียมไว้ โดย Python จะมีคลาสสำหรับอ่านเขียนไฟล์ CSV เป็นคลาสมาตรฐานอยู่แล้ว ก็เลยสามารถทำงานได้สะดวกมาก
วิธีใช้คลาส csv เพื่ออ่านข้อมูล จะทำอย่างนี้
import csv
data = csv.reader(open('myfile.csv'), delimiter=',', quotechar='"')
for row in data:
print row[0]
csv.reader เป็นคลาสสำหรับจัดการอ่านข้อมูลแบบ CSV รับอาร์กิวเมนต์เป็นไฟล์ เครื่องหมายที่ใช้คั่นระหว่างฟิลด์ และเครื่องหมายที่ใช้เป็น quote ครอบข้อมูลแต่ละฟิลด์ เมื่อสร้างออพเจคต์ของ csv.reader ขึ้นมาแล้ว ก็สามารถวนรอบเพื่ออ่านไฟล์ขึ้นมาทีละเรคอร์ดได้เลย โดยข้อมูลที่อ่านขึ้นมาจะเก็บไว้ในรูปอาเรย์ อย่างกรณีนี้ row[0] ก็คือข้อมูลในฟิลด์แรกสุด
หลังจากนี้เราก็ต้องเตรียมเทมเพลต ซึ่งก็คือกำหนดตัวแปรสตริงเท่านั้นเอง เช่น
template = """
Dear {r[0]},
I would like to send you the following information:
{r[1]}
Best Regards,
Cholwich
"""
จะเห็นว่าสตริงนี้มีสัญลักษณ์พิเศษ ระบุข้อมูลที่เราต้องการจะเอาไปแทรก {r[0]} หมายถึงสมาชิกตัวแรกของตัวแปร r ที่ส่งมาเป็นพารามิเตอร์ อันนี้เป็นฟีเจอร์ String Formatting ของ Python อยู่แล้ว เมื่อกำหนดเทมเพลตเสร็จ สั่งให้ Python แทรกข้อมูลโดย
output = template.format(r=row)
จะได้ผลลัพธ์ที่ผสานข้อมูลเข้าไปแล้ว เป็นสตริงเก็บไว้ที่ตัวแปรชื่อ output จากนั้นผมก็สามารถเอาไปส่งเมล์ หรือเอาไปเขียนลงไฟล์เก็บไว้ได้ พอเอาทั้งหมดมารวมกันก็จะได้
import csv
template = """
Dear {r[0]},
I would like to send you the following information:
{r[1]}
Best Regards,
Cholwich.
"""
data = csv.reader(open('myfile.csv'), delimiter=',', quotechar='"')
for row in data:
output = template.format(r=row)
f = open(row[2], 'w')
f.write(output)
f.close()
สุดท้ายก็จะได้สคริปต์ง่ายๆ สำหรับทำ mail merge ที่ได้ output เป็นไฟล์แยกกัน โดยระบุชื่อไฟล์ไว้ที่ row[2]
อ้างอิง:
- PEP 3103: Advanced String Formatting
- Python csv — CSV File Reading and Writing
programming
programming, python
ด้วยความที่อยากให้นักศึกษามีส่วนร่วมในการเรียนมากขึ้น เทอมนี้เลยพยายามเรียกนักศึกษาให้ช่วยตอบคำถาม หรือออกมาทำอะไรเล่นหน้าห้อง อย่างน้อยจะได้หลับกันน้อยลง หรือพยายามทำแบบฝึกหัดที่ให้ในห้องเรียนบ้าง แต่จะให้เลือกชื่อก็จำชื่อนักศึกษาทั้งหมดไม่ได้ แถมอาจจะเรียกบางคนซ้ำ หรือไม่ได้เรียกบางคนเลย เมื่อวานพอมีเวลาว่างอยู่บ้าง เลยนั่งเขียนโปรแกรม Java เล็กๆ ขึ้นมาหนึ่งตัว เอาไว้สำหรับสุ่มชื่อนักศึกษาในชั้นเรียน ตอนแรกก็กะจะทำเป็นโปรแกรมง่ายๆ คือแค่เปิดไฟล์อ่านชื่อนักศึกษามา แล้วก็แค่สุ่ม แต่ถ้าทำอย่างนั้นก็มีโอกาสที่บางคนจะโดนซ้ำ หรือสุ่มไม่ทั่วถึง
เพื่อเผื่อแผ่ให้ทุกคนถูกเรียกกันอย่างทั่วถึง เลยปรับการสุ่มนิดหน่อย โดยเก็บจำนวนครั้งที่นักศึกษาแต่ละคนถูกเรียกไว้ ถ้าโดนเรียกแล้วหนึ่งครั้ง ก็จะกำหนดโอกาสที่จะถูกสุ่มขึ้นมาอีกให้เป็น 1/2 ของเพื่อนที่ยังไม่เคยโดนเรียก ถ้าโดนสองครั้งก็จะลดไปอีกให้เหลือ 1/4 ของเพื่อนๆ คือให้เป็น 1/(2^n) เมื่อ n เป็นจำนวนครั้งที่ถูกเรียก เสร็จแล้วก็มานึกอีกว่าบางคนอาจจะโดนเรียกแล้วไม่อยู่ อย่ากระนั้นเลยจะต้องเก็บข้อมูลไว้หน่อยว่าคนนี้โดดเรียน เลยทำปุ่มให้กดได้ด้วยว่าโดดไปแล้วกี่ครั้ง (ไม่รู้เก็บไว้ทำไมเหมือนกัน)
สุดท้ายได้โปรแกรมออกมาหน้าตาแบบนี้

ถ้าใครสนใจลองโหลดไปเล่นได้ที่นี่ วิธีใช้ก็คือเตรียม text file ใส่ชื่อนักศึกษาบรรทัดละคน เขียน ID ก่อน แล้วค่อยตามด้วยชื่อ คั่นด้วย TAB อาจจะใส่จำนวนครั้งที่เรียกนักศึกษาไปแล้วด้วยก็ได้ คั่นด้วย TAB เหมือนกัน
ส่วนโค้ด ถ้าใครอยากได้ก็ขอมาล่ะกันครับ มันค่อนข้างเละเทะ ไม่อยากเผย อายครับ
ถ้ามีเวลาบ้าอีก ก็อาจจะเพิ่มฟีเจอร์อื่นๆ ไปด้วย
programming, teaching
programming, teaching
ช่วงนี้ได้อ่านหนังสือ “The Ruby Programming Language” ทำให้มีโอกาสได้รู้เทคนิค หรือฟีเจอร์ของภาษา Ruby หลายอย่าง แม้ว่าจะใช้ Ruby มาตั้งแต่สมัยอยู่ญี่ปุ่น แต่ยังไม่เคยมีโอกาสอ่านหนังสือจริงๆ จังๆ ส่วนใหญ่ก็ใช้ไปแบบงูๆ ปลาๆ เหตุผลเดียวที่ใช้ก็คือรู้สึกว่า syntax ค่อนข้างคงที่ไม่กำกวม แถมยังเป็น OOP ทำให้ง่ายต่อการทำงานบางอย่าง วันนี้เพิ่งอ่านเจอว่าเราสามารถเขียน method ที่รับอาร์กิวเมนต์จำนวนไม่คงที่ได้ โดยใช้วิธีการกำหนดพารามิเตอร์เพิ่มหนึ่งตัว นอกเหนือจากพารามิเตอร์ปกติ และให้ใส่เครื่องหมาย * ไว้ข้างหน้าพารามิเตอร์ตัวนั้น เช่น
def min(first, *rest)
min=first
rest.each {|x| min=x if x<min}
return min
end
method นี้จะหาค่าต่ำที่สุดของอาร์กิวเมนต์ทั้งหมดที่รับมา เช่น
โดย first เป็นพารามิเตอร์หลัก หมายความว่าจะต้องรับข้อมูลมาอย่างน้อยหนึ่งตัว ส่วน rest เป็นพารามิเตอร์สำหรับค่าอื่นๆ ที่เหลือ ในที่นี้เมื่ออ้างถึง rest จะเป็นอะเรย์เก็บค่าต่างๆ ที่ได้รับมา ดังนั้นจึงสามารถใช้ rest.each ในการอ้างถึงสมาชิกแต่ละตัว แล้วเปรียบเทียบหาค่าต่ำที่สุดได้
ลองดูฟีเจอร์นี้ของ Ruby แล้ว ก็รู้สึกว่าไม่ต่างจากรูปแบบลิสต์ [head|tail] ของ Prolog คือลิสต์ถูกแบ่งเป็นสองส่วน ได้แก่ ส่วนหัว กับส่วนที่เหลือ (ตรงนี้จะต่างจากภาษา C ที่ใช้วิธีกำหนดทั้งหมดไว้ในอะเรย์ แล้วระบุจำนวนอาร์กิวเมนต์มาให้) เลยอยากจะลองเขียน method reverse สำหรับสลับลำดับข้อมูลในลิสต์เรียงกลับจากลำดับเดิม โดยใช้วิธี recursive แบบ Prolog ดู
reverse([],[]).
reverse([H|T], R) :- reverse(T,TR), append(T,[H],R).
def reverse(first, *rest)
if rest.empty?
return [first]
else
return reverse(*rest) << first
end
end
ลองเทียบกันดูในแง่ความหมาย จะเห็นว่่าเหมือนกันเป๊ะๆ แถมยังใช้เครื่องหมายคล้ายๆ กันอีก จุดที่แตกต่างที่สุดก็เห็นจะอยู่ที่ลักษณะของ reverse ใน Ruby เป็นฟังก์ชันมีการส่งค่ากลับ ส่วน reverse ของ Prolog เป็น predicate แสดงความสัมพันธ์ระหว่าง list สองอัน
เป็นอันสิ้นสุดการทดลองของวันนี้แต่เพียงเท่านี้
programming
programming, prolog, ruby
Recent Comments