msgbartop
Just another blog of mine
msgbarbottom

17 Jan 11 จับลอกการบ้านแบบพื้นๆ

เทอมนี้ผมได้รับมอบหมายให้สอนวิชา Introduction to Computers and Programming ซึ่งเป็นวิชาพื้นฐานสำหรับนักศึกษาชั้นปีที่ 1 ทุกโปรแกรม มีนักศึกษาลงทะเบียนจำนวนมาก อย่างเทอมนี้ก็มีเกือบ 300 คน เนื้อหาหลักก็คือสอนการเขียนโปรแกรมภาษา C กับอัลกอริทึมพื้นฐาน (อย่างเช่น searching และ sorting) แต่การเขียนโปรแกรมเป็นทักษะที่ต้องฝึกฝนบ่อยๆ จึงทำเข้าใจและทำได้ วิชาก็เลยมีทั้งบรรยาย ปฏิบัติการ และการบ้าน (ปีนี้ดีหน่อยที่สามารถหาคนมาช่วยตรวจการบ้านได้)

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

เมื่อจะตรวจแบบพื้นๆ ผมก็แค่ใช้ hash function มาสร้าง message digest จากไฟล์ที่นักศึกษาส่งมา ถ้าไฟล์ไหนมี message digest เหมือนกัน ก็แปลว่าลอกกันมาทั้งไฟล์ จะไปได้หักคะแนนถูก การหาค่า message digest ของไฟล์การบ้านทั้งหมด ก็ใช้คำสั่ง find ของ unix

$ cd assignment1
$ find . -name "*" -not -type d -exec shasum '{}' \; | sort > digest_list.txt

คำสั่ง find ใช้หาชื่อไฟล์ตามเงื่อนไขที่กำหนด ในที่นี้คือเอาทุกไฟล์ ยกเว้นไดเรคทอรี เมื่อได้ไฟล์แล้ว เราก็สามารถสั่งให้รันโปรแกรม shasum โดย ‘{}’ จะถูกแทนด้วยชื่อไฟล์ที่ค้นเจอแต่ละไฟล์ จากนั้นก็เอาไปเรียงลำดับเพื่อให้ไฟล์ที่มี digest เหมือนกันอยู่ติดกัน เก็บลงไฟล์​ digest_list.txt ที่เหลือก็แค่เขียนโปรแกรมจับกลุ่มว่าไฟล์ที่เหมือนกันมีใครบ้าง จัดเป็นกลุ่มก็เรียบร้อย

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: , , ,

07 Jun 09 เขียนโปรแกรมกันเถอะ

เมื่อวานนี้อ่านข่าวบน /.jp แล้วเจอบทสัมภาษณ์ประธานบริษัท NTT Data ซึ่งเป็นบริษัท IT ในกลุ่ม NTT

若い時にプログラムを書こう、必ず人生の豊かさにつながる
– 山下徹 NTTデータ社長

แปลเป็นไทยได้ความว่า

เขียนโปรแกรมกันไปเถอะในช่วงที่อายุยังน้อย, สิ่งเหล่านี้จะเชื่อมโยงกับความรุ่มรวยในชีวิตอย่างแน่นอน
– โทรุ ยะมะชิตะ ประธานบริษัทเอ็นทีทีดาต้า

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

จริงอยู่ว่าเด็กส่วนใหญ่จะพอเขียนโปรแกรมได้ อาจจะใช้วิธีตัดแปะเอาบ้าง หาทางเอาตัวรอดกันไปได้ แต่ส่วนใหญ่มักจะรู้สึกว่าการเขียนโปรแกรมเป็นเรื่องยาก เป็นเรื่องน่าเบื่อ เป็นงานคนชั้นล่าง (เหมือนผู้ใช้แรงงานในสังคมไอที) โดยเฉพาะค่านิยมอันหลังนี้ เป็นสิ่งที่ผมไม่เห็นด้วยที่สุด เพราะผมเชื่อว่าเราไม่สามารถเป็น system analyst เป็น project manager ที่ดี หรือเป็นตำแหน่งต่างๆ ที่บางบริษัทบอกว่าเป็นเหมือนยอดปิระมิดได้ โดยไม่มีพื้นฐาน ทักษะการเขียนโปรแกรม หรือไม่รู้ว่าถ้าออกแบบไปอย่างนี้แล้วคนที่เอาไป implement เขาจะทำยังไง

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

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

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

อ้างอิง: http://slashdot.jp/developers/09/06/05/066228.shtml

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: ,

02 Dec 08 Student Randomizer

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

เพื่อเผื่อแผ่ให้ทุกคนถูกเรียกกันอย่างทั่วถึง เลยปรับการสุ่มนิดหน่อย โดยเก็บจำนวนครั้งที่นักศึกษาแต่ละคนถูกเรียกไว้ ถ้าโดนเรียกแล้วหนึ่งครั้ง ก็จะกำหนดโอกาสที่จะถูกสุ่มขึ้นมาอีกให้เป็น 1/2 ของเพื่อนที่ยังไม่เคยโดนเรียก ถ้าโดนสองครั้งก็จะลดไปอีกให้เหลือ 1/4 ของเพื่อนๆ คือให้เป็น 1/(2^n) เมื่อ n เป็นจำนวนครั้งที่ถูกเรียก เสร็จแล้วก็มานึกอีกว่าบางคนอาจจะโดนเรียกแล้วไม่อยู่ อย่ากระนั้นเลยจะต้องเก็บข้อมูลไว้หน่อยว่าคนนี้โดดเรียน เลยทำปุ่มให้กดได้ด้วยว่าโดดไปแล้วกี่ครั้ง (ไม่รู้เก็บไว้ทำไมเหมือนกัน)

สุดท้ายได้โปรแกรมออกมาหน้าตาแบบนี้

ถ้าใครสนใจลองโหลดไปเล่นได้ที่นี่ วิธีใช้ก็คือเตรียม text file ใส่ชื่อนักศึกษาบรรทัดละคน เขียน ID ก่อน แล้วค่อยตามด้วยชื่อ คั่นด้วย TAB อาจจะใส่จำนวนครั้งที่เรียกนักศึกษาไปแล้วด้วยก็ได้ คั่นด้วย TAB เหมือนกัน

ส่วนโค้ด ถ้าใครอยากได้ก็ขอมาล่ะกันครับ มันค่อนข้างเละเทะ ไม่อยากเผย อายครับ :D ถ้ามีเวลาบ้าอีก ก็อาจจะเพิ่มฟีเจอร์อื่นๆ ไปด้วย

Tags: ,