แนะนำการใช้ Perl เพื่อหาผลการทดลอง

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

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

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

ก่อนที่จะใช้ Perl script ผมแนะนำให้

  1. เขียนโปรแกรมให้รับค่าพารามิเตอร์จาก command line ให้ได้ก่อน ถ้าเป็นภาษา C/C++ ก็ใช้พวก argc, argv และ getopt (ลองดูโค้ดตัวอย่างได้ที่  ตัวอย่าง getopt ครับ
  2. ตอนที่แสดงผลจากโปรแกรมนั้น ให้เราแสดงออกมาทาง stdout หรือจะเขียนลงไฟล์ก็ได้ครับ
  3. output ที่ออกมา เราควรจะออกแบบสักหน่อย เพื่อให้โปรแกรมต่อไปสามารถรับจาก command line ได้

ต่อไปนี้ก็จะเป็นโค้ดตัวอย่างที่ผมใช้บ่อยๆ ครับ

การรับพารามิเตอร์จาก command line ใช้ดังนี้

my $myVariable = $ARGV[0];

เริ่มจากเลข 0 ครับ ถ้ามี 3 ค่า จะเป็นดังนี้

my $myVariable1 = $ARGV[0];
my $myVariable2 = $ARGV[1];
my $myVariable3 = $ARGV[2];

ส่วนคำสั่ง Perl สำหรับรันโปรแกรมที่เราเขียนขึ้นเองต่างหากก็ตามนี้

system( "myProgram $param01 $param02" );

เปิดโฟลเดอร์เพื่อแสดงชื่อไฟล์ทั้งหมด (รวมทั้งชื่อโฟลเดอร์ด้วย ฉะนั้นแนะนำว่าให้ชื่อไฟล์มี extension ไว้ แล้วแก้คำสั่ง grep เพื่อกรองเอาแต่ไฟล์ครับ)

opendir( DIR, $folder_name ) or die "couldn't open $folder_name\n";
# ไม่เอา . ไม่เอา .. และไม่เอา .svn (เผื่อว่าเราใช้ svn)
my @files = grep { $_ ne '.' && $_ ne '..' && $_ ne '.svn' } readdir DIR;
for $file ( sort @files ) {
  print $file . "\n";
}

ถ้าต้องการที่จะเรียงไฟล์ตามตัวอักษรไม่ใช่ตามแบบ string ก็ใช้ตามนี้ครับ

for $file ( sort { $a <=> $b } @files ) { ... }

ส่วนฟังก์ชั่นตัด extension ของไฟล์

sub without_ext {
  my ( $file ) = @_;
  return substr( $file, 0, rindex( $file, '.' ) );
}

วิธีใช้ก็แค่โยนชื่อไฟล์เข้าไปแบบนี้ without_ext ( $file )

และฟังก์ชั่นตัดเอามาแต่ extension ของไฟล์

sub ext_only {
  my ( $file ) = @_;
  return substr( $file, rindex( $file, '.' ) + 1 );
}

วิธีใช้ก็แค่โยนชื่อไฟล์เข้าไปแบบนี้ ext_only( $file ) เช่นกัน

การเปิดไฟล์สำหรับอ่านและการอ่านไฟล์

open( FILE, $filename ) or die( "Cannot open file" );
@data = <FILE>;
foreach $text_line ( @data ) {
  print $text_line . "\n";
}

การเปิดไฟล์สำหรับเขียนลงไฟล์ก็แค่เพิ่ม ">" ลงไปก่อนหน้าชื่อไฟล์ครับ ดังนี้

open( FILE, ">$filename" ) or die( "Cannot open file" );
print FILE "test\n";

การอ่านข้อมูลจาก stdout

# ต้องใส่ | ข้างหลังด้วย แปลว่าให้อ่านจาก pipe
open( OUTPUT, "myProgram $param01 $param02 |" );
while( <OUTPUT> ) {
  # แบบนี้จะตัด input โดยใช้การขึ้นบรรทัดใหม่เป็นตัวกำหนด ถ้าต้องการตัดโดยใช้ tab ก็เปลี่ยนจาก "\n" เป็น "\t"
  @output = split( "\n", $_ );
  foreach $val ( @output ) {
    print $val . "\t";
  }
}

สามารถหาข้อมูลเพิ่มเติมเกี่ยวกับ open() ได้ ที่นี่ ครับ

หวังว่าจะมีประโยชน์สำหรับหลายๆ ท่านนะครับ 🙂