技術討論區 > 程式討論版

請教如何改寫perl程式,以加快執行速度

頁: << < (2/3) > >>

damon:

你的演算法有問題吧,這樣一筆一筆比對是最笨的方法,有幾筆就要判斷幾次
你需要的不是語法,是演算法的概念吧
尤其是怎麼減少判斷的時間的部份,好好想想怎麼樣比較快吧
例如如果第一個字是匯的話就丟到比對匯字的部份
或是用第二個字來比對,或是以最常出錯的字來比對,先比對最容易出錯的部份再來比對不容易出錯的部份,這都是你的演算法可以思考的方向
如果你有辦法利用統計資料把哪些字容易打錯,哪些字不容易打錯
你的演算法可以做成中文的拼字檢查了

被騎上班的老:

演算法的確有很大的關係。

我的建議是將 pattern  存成資料庫然後排序,資料庫一 load 到記憶體就不必重新讀入,另外有排序的搜尋絕對要比從頭到尾的比對快。
目前你的程式一行一次比對,就要重新將變數 load 到記憶體,比對然後執行, I/O 的時間就拖長

其次,只需要執行需要的轉換。意思是說其實你轉換一次的時候,如果比對不成功。 7000 行全部白跑了,比對成功的話 也有 6999 行白跑了。

所以應該先檢查是否在 pattern 比對資料庫中,否的話就 return 。是的話就執行比對的程式。

對阿,寫到這裡突然想到,學校裡面上過,資料跟程式應該分開啊! 因為資料會異動,根本不應該放在程式裡面啊。資料放在資料庫,你的程式大小就會減少, cycle time 也就跟著減少了啊。

加油加油! 你的經驗會是我們一個重大的課題。

chienwen:

網路上有一些好用的字典資料庫, 例如:

http://www.freebsd.org/cgi/pds.cgi?ports/chinese/dictd-database
http://stardict.sourceforge.net/Dictionaries_zh_TW.php

其中, 有一些正體中文的文件, 是翻譯自簡體中文.
但我發現有一些文字轉換之後, 會有錯誤.
我想把這些字典資料庫重新轉換, 更正這些錯誤.
因此, 才有這次的問題.

我是用 perl 的套件做正簡中文轉換:

--- 代碼: ---use Encode ;
use Encode::HanConvert ;
--- 程式碼結尾 ---


我發件這個套件, 遇到某些字, 會轉換錯誤, 一共有 880 個中文字.
錯誤的原因是: 有些簡體中文字, 會對應兩個以上的正體中文字.

我用這 880 個中文字, 查詢教育部的辭典, 一共查出約 7000 個辭彙,
這 7000 個辭彙, 就是這個問題的比對項目.

我依據學長姊的指導, 將程式碼稍加修改如下:

--- 代碼: ---package input ;

sub check_chinese_spell {
my $s = join( ", " , @_ ) ;

if ($s =~ /\xB6\xD7/) { # 匯彙
$s =~ s/\xB6\xD7\xB3\xF8/\xB7\x4A\xB3\xF8/g ; # 匯報 - 彙報
$s =~ s/\xB6\xD7\xBD\x73/\xB7\x4A\xBD\x73/g ; # 匯編 - 彙編
$s =~ s/\xB6\xD7\xA5\x5A/\xB7\x4A\xA5\x5A/g ; # 匯刊 - 彙刊
$s =~ s/\xB6\xD7\xB6\xB0/\xB7\x4A\xB6\xB0/g ; # 匯集 - 彙集
# ...略...
} ;
# ...略...
return $s ;
} ;

1 ;
--- 程式碼結尾 ---

程式的速度大為提升, 重原先的 6~7 小時, 降到只剩 55 分鐘.
 (轉換同一個 70 萬筆的字典資料庫)

至於, 學長姊提到用資料庫排序, 再 load 到記憶體的方法,
目前我還沒有什麼概念, ... 繼續研究中 ...

這個 FreeBSD port 的 source 是用 perl 去轉換的.
我為了轉換的問題, 才用心去學 perl.
(也才體會出 perl 在文件處理上的功能這麼強)
一直以為自己語法沒學好, 猛往語法鑽,
"需要的不是語法,是演算法的概念吧"
原來演算法也是這麼重要!!

被騎上班的老:

恭喜你,也感謝你的回報。其實我們有時候只是理論者,不是實作者。理論需要實作者的驗證,讓我們覺得讀的書有用。

其實這也是個良性循環,有人貢獻方法,有人貢獻實驗結論,然後大家都可以從中間學到經驗。這就是 study-area 的精神最佳應證。

這個討論真是經典,可否建請板主置頂成為這個板的好榜樣。

被騎上班的老:

data.txt


--- 代碼: ---\xB6\xD7\xB3\xF8        \xB7\x4A\xB3\xF8
\xB6\xD7\xBD\x73        \xB7\x4A\xBD\x73
\xB6\xD7\xA5\x5A        \xB7\x4A\xA5\x5A
\xB6\xD7\xB6\xB0        \xB7\x4A\xB6\xB0
\xB4\xE5\xBF\xB3        \xB9\x43\xBF\xB3
\xB4\xE5\xBE\xC7        \xB9\x43\xBE\xC7
\xAA\xED\xB5\xFD        \xAA\xED\xC3\xD2
\xBF\xEB\xB5\xFD        \xBF\xEB\xC3\xD2
\xC5\x47\xB5\xFD        \xC5\x47\xC3\xD2
--- 程式碼結尾 ---

以 tab 分離,前面的是 key 也就是需要轉換的 pattern 後面是 value 也就是需要轉換成的文字。

a.pl

將文字檔轉換成一個 dbfile (data.db) 也就是說將程式與資料分開。你可以將資料存在另外一個文字檔,一方面增加資料的時候不必去修改程式另一方面可以縮小程式。

轉換文字檔成資料庫(data.db)只需要作一次,如果 data.txt 沒有異動不需要重做這個轉換。此外這個資料庫是一個排序過的資料庫,搜尋時間應該會縮短。


--- 代碼: ---#!/usr/bin/perl

#use warnings;
#use strict;
use DB_File;

$DB_BTREE->{'compare'} = sub {
        my ($key1, $key2) = @_;
        "\L$key1" cmp "\L$key2";
};

# Declaration
our (%hash, $key, $value);
my $DB_FILE = "data.db";
my $DATAFILE = "data.txt";
my %hash;
my $line;

# delete $DB_FILE
unlink $DB_FILE ;
#
open(DATA, $DATAFILE);
tie(%hash, "DB_File", $DB_FILE, O_RDWR|O_CREAT, 0666, $DB_BTREE)
  or die "Cannot open DB_FILE $DB_FILE : $!\n";
print "Before Sort \n\n";
while($line = <DATA>)
{
        chomp($line);
        ($key, $value) = split(/\t/, $line);
        print $key, " -> ", $value, "\n";
        $hash{$key} = $value;
}



print "\nAfter Sort\n\n";
while(my($key, $value) = each %hash)
{
        print  $key, " -> ", $value, "\n";
}

# close file
untie % hash;
close DATA;
--- 程式碼結尾 ---



從 dbfile 比對 c.pl


--- 代碼: ---#!/usr/bin/perl

use warnings;
use strict;
use DB_File;
our (%hash, $key, $value);

my $DB_FILE = "data.db";

tie(%hash, "DB_File", $DB_FILE, O_RDWR, 0666, $DB_BTREE)
        or die "Cannot open file '$DB_FILE'  $!\n";

$key = $ARGV[0];
print $key,"\n";

$value = $hash{$key};

print $value, "\n";
untie %hash;
--- 程式碼結尾 ---



例如  ./c.pl "\xB6\xD7\xBD\x73" 會回應


--- 代碼: ---\xB6\xD7\xBD\x73
\xB7\x4A\xBD\x73
--- 程式碼結尾 ---



這是一個資料庫查詢的範例,使用時要比較快的話要把螢幕輸出,如 print 之類的指令關掉。

所以你的程式在比對的時候可以改成去查詢資料庫,如果有傳回值就可以執行轉換這個傳回值。要是沒有就可以 return,這樣的話就可以只比對一次就可以。

詳情可以找 perl DB_File 的文章

頁: << < (2/3) > >>

前往完整版本