msgbartop
Just another blog of mine
msgbarbottom

09 Jul 10 กรอกคะแนน

ผมรู้สึกว่าการกรอกคะแนนนักศึกษาเป็นเรื่องยุ่งยาก โดยเฉพาะคะแนนการบ้านกับควิซ ที่เก็บมาแบบไม่ได้เรียงตามลำดับรหัสนักศึกษา ไม่รู้ว่าอาจารย์คนอื่นเขาทำกันยังไง (โดยเฉพาะวิชาที่สอนกัน 200-300 คน) เดิมผมมักจะเอากระดาษมาเรียงตามรหัสนักศึกษา แล้วค่อยไล่กรอกลง spreadsheet แต่มันก็ยุ่งยาก ขี้เกียจอีก

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

วันก่อนก็เลยเขียนโปรแกรมเล็กๆ เอาไว้กรอกคะแนนควิซโดยเฉพาะ ฟีเจอร์หลักๆ ที่ต้องการก็คือ อยากใส่แค่รหัสนักศึกษาบางส่วน (3-4 ตัวก็พอ) ถ้าไม่ซ้ำก็กรอกคะแนนไปได้เลย ถ้าซ้ำก็แสดงตัวเลือกให้หน่อย แล้วค่อยกรอกคะแนน ตอนแรกก็อยากจะทำเป็น GUI สวยงาม มี textbox ให้กรอกรหัส แล้วจะแสดงตัวเลือกเหมือนพวก autocomplete อะไรทำนองนั้น แต่ก็ดูยุ่งยาก ขี้เกียจอีก (จะเห็นว่าขี้เกียจกันทุกขั้นตอน) แต่สุดท้ายเลยได้โปรแกรม python แบบ command-line มาอันหนึ่ง รับข้อมูลเป็น text file มีแต่รหัสนักศึกษา ใช้ regular expression สำหรับเทียบรหัสกรอกเข้ามา แล้วก็ให้เลือกได้ ถ้ามีรหัสตรงหลายอัน จากนั้นก็กรอกคะแนนลงไปได้ เรียบร้อย ใช้ได้ดี ตามข้างล่างนี้ (แต่ไม่รับประกันความผิดพลาดถ้าจะเอาไปใช้ เพราะขี้เกียจเช็ค input error บางกรณีอีก) โปรดลองใช้กันได้ตามอัตภาพ :D
(more…)

Tags: , , ,

22 Nov 09 ลองเล่น Go

ไม่ได้เขียนอะไรใหม่ๆ ที่นี่มาเกือบครึ่งปี เพราะงานยุ่ง แล้วก็ยังเสพติด 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 เหมือนกัน เอาไว้หนีงานมาลองใหม่คราวหน้าละกัน

Tags: , , ,

22 Apr 09 Mail Merge ด้วย Python

หลังจากถูกถล่มด้วยงานต่างๆ ทำให้ไม่ได้มาเขียนสองเดือนกว่าๆ พอดีช่วงนี้ช่วยจัดงาน 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]

อ้างอิง:

  1. PEP 3103: Advanced String Formatting
  2. Python csv — CSV File Reading and Writing

Tags: ,