PDA

View Full Version : Dictionary kiểu như Lạc Việt viết bằng VB



gold_son
25-02-2003, 17:29
Tớ thấy Từ điền viết bằng VB hay lắm đó . Có ai viết chưa vậy post lên diễn đàn đi

dtt_vn
25-02-2003, 23:36
đã có nhóm bàn rồi, nhưng hình như chưa thấy gì ráo trọi. cũng khá là khó. nhất là tvới tốc độ rùa bò của VB

lazy-programmer
26-02-2003, 11:52
Tôi muốn góp một tay làm đây, viết bằng VB.Net hay C# đi. Hiện tôi đã có phần database Anh-Việt, Việt-Anh nhưng nó bằng font ABC nên có lẽ phải chuyển sang Unicode. Bạn nào có hứng thú thì ta bàn tiếp.

attilathehun
26-02-2003, 16:50
không biết kiểu cấu trúc dữ liệu nào là tối ưu nhất cho chương trình từ điển nhỉ? và muốn chèn multimedia vào thì sử dụng cấu trúc dữ liệu như thế nào??

xbacala
26-02-2003, 17:01
Việc chuyển DB từ ABC hay VNI sang Unicode thì quá dễ !
Quan trọng nhất là cái search engine. không thể dùng search engine của database như SQL hay Access được vì nó không đủ nhanh và không tối ưu cho việc truy xuất theo kiểu dictionary.

tien bac
26-02-2003, 17:03
Chao lazy-programmer tớ cũng muốn viết từ điển bằng VB nhưng tớ thấy bạn có bộ Database tuyệt vời đấy chia sẻ cho tớ được không ?

attilathehun
26-02-2003, 17:07
ủa, tưởng viết phần mềm từ điển thì không sử dụng database mà tạo ra cấu trúc dữ liệu cho riêng mình chứ.
một database với 165000 record thì connect vào sao nổi

gold_son
26-02-2003, 17:10
Từ điển phải có cả giọng nói nữa làm cách nào để phải xử lý phần phát âm của từ mà mình chọn . Có bộ phần mềm nào có thể xử lý cách phát âm tốt nhất không

gold_son
26-02-2003, 17:14
vậy attilathehun có cách giải quyết vấn đề thế nào về cơ sở dữ liệu của từ điển vậy

attilathehun
26-02-2003, 17:23
To gold_son: đây là một kiểu cấu trúc dữ liệu của từ điển (không biết có tối ưu không nữa) mà tui lấy được từ tạp chí Tin học và nhà trường (thnt.com.vn)
Mời tham khảo!


<B> Kỹ thuật tạo phần mềm từ điển </B>


Hàng ngày khi làm việc, chúng ta thường tiếp xúc với từ điển trên máy tính để tra ngược, tra xuôi một cụm từ nào đó, chẳng hạn bạn sử dụng phần mềm Từ điển Lạc Việt. Bạn có bao giờ thắc mắc tại sao phần mềm có thể thực hiện tìm kiếm, tra cứu, hiển thị lên màn hình nhanh như vậy không?. Bài viết này hy vọng sẽ giúp các bạn tìm hiểu kĩ thuật ″phía sau hậu trường ″ của các chương trình từ điển.
Bài toán từ điển là bài toán hết sức thú vị. Đứng về mặt kĩ thuật có một số yêu cầu sau, khi xây dựng chương trình từ điển:
1. Cấu trúc dữ liệu trong từ điển phải hợp lý sao cho:
* Cài đặt toán dễ dàng.
* Thời gian chạy nhanh (Bao gồm thời gian tìm kiếm và xử lý dữ liệu).
2. Dữ liệu đặt trong File bảo đảm chế độ bảo mật.
3. Thuật toán đi kèm với cấu trúc dữ liệu phải hợp lý.
Trước hết File dữ liệu ta phải ghi ở dạng File kiểu bản ghi. Mỗi phần tử của File chứa dữ liệu cần thiết của một từ: Từ tiếng Anh, cách phiên âm, nghĩa của từ, cách đọc (nếu có). Mỗi từ tiếng Anh tương ứng với một bản ghi, chỉ số bản ghi đó trong File dữ liệu. Vì vậy từ điển là một chương có dữ liệu đầu vào khá lớn. Yêu cầu khi chạy chương trình, để đảm bảo hoạt động nhanh, tối thiểu chương trình phải nạp được từ tiếng Anh, đồng thời vị trí chứa nghĩa của nó đặt trong File. Khi đánh từ vào ô nhập từ, danh sách từ gợi ý phải hiện ra lập tức, đồng thời khi bấm ENTER thì nghĩa cũng phải hiện ra ngay. Điều đó có nghĩa là chương trình thực hiện tìm từ trên cấu trúc dữ liệu phải nhanh. Một cấu trúc dữ liệu khá điển hình và hợp lý với bài toán từ điển là: Cấu trúc cây từ điển. Cây từ điển là tập hợp các bản ghi liên kết với nhau. Mỗi bản ghi gọi là một nút trong cây. Để quản lý cây từ điển ta luôn luôn phải giữ địa chỉ của cây, tức là nút HEAD (Nút đầu tiên của cây). Mỗi nút cây từ điển gồm có 3 trường:
* Trường thông tin: Infor. Trường này chứa một ký tự kiểu CHAR
* Trường Next: chỉ đến nút Anh Em của nó.
* Trường Child: chỉ đến nút con của nó.

List=^Node;
Node=record
inf:char;
child,next:list;
end;
Một nút B gọi là con của nút A nếu: B = A.child. Tương tự nút B gọi là nút Anh Em của nút A nếu: B = A.next hoặc A = B.next. Từ đây ta mở rộng ra: Nếu B là con của A và C là Anh Em của B thì cả C và B đều là con của A. Nếu A là Anh Em của B và B là Anh Em của C thì A cũng là Anh Em của C.
Để tạo thành cây từ điển trong quá trình chạy chương trình, ta phải làm thao tác nạp các từ tiếng Anh vào cây. Về trường nghĩa của từ, vì nó rất lớn ta không thể nạp vào bộ nhớ chương trình được. Vì vậy ta phải để nghĩa của từ ở trong File. Khi nào tra đến từ nào ta sẽ đọc nghĩa của nó ở trong File. Điều này bắt buộc ta phải xen kèm vào trong cây từ điển, sau mỗi từ tiếng Anh chỉ số vị trí của từ tiếng Anh đó trong File. Tất nhiên chỉ số này phải được đổi thành chuỗi kí tự thập phân trước khi thực hiện xen từ. Khi đọc thì sẽ chuyển ngược chuỗi chỉ số này sang số thập phân. (Chú ý: nếu muốn bản tin chỉ số này ngắn để tiết kiệm bộ nhớ ta có thể chuyển từ số thập phân sang số dạng cơ số Hexa hoặc cơ số 32...). Giả sử cần thêm từ ″Dictionary″ vào cây. Ta làm theo quy tắc như sau:
a. Ta sẽ duyệt từ kí tự đầu đến kí tự cuối của từ. Gọi P là một biến con trỏ. Đầu tiên P trỏ tới Head.
h Ta tìm xem trong các con của P xem có nút nào có trường Infor là kí tự ″d″ hay chưa. Nếu đã có thì nhảy con trỏ P tới nút đó. Nếu chưa ta xen vào con của P một nút có trường Infor là″d″ sau đó chuyển con trỏ P tới nút vừa thêm.
h Lặp lại bước trên cho tới khi xen hết kí tự ″y″ vào cây.
b. Sau bước trên thì con trỏ P trỏ tới nút ″y″. Tiếp theo ta sẽ phải thêm chỉ số vị trí của bản ghi chứa từ này trong File dữ liệu vào nút con của P. Như vậy một vấn đề đặt ra là làm thế nào để biết được từ đâu là kết thúc từ tiếng Anh và bắt đầu là bản tin cho trường chỉ số. Một giải pháp là trước khi thêm con của P các bản ghi chỉ số ta chèn vào con của P một bản ghi có trường Infor mang kí tự đặc biệt ″*″ và chuyển con trỏ P tới đó. Việc chèn chuỗi chỉ số tương tự như việc chèn chuỗi từ ở phần (a). Sau bước này là kết thúc công việc chèn một từ tiếng Anh vào cây từ điển.
Như vậy muốn chèn một từ ta cần biết từ và chỉ số của bản ghi chứa từ đó. Hình dưới là minh hoạ một cây từ điển:




Cây từ điển trên chứa 4 từ và vị trí bản ghi chứa từ đó trong File: Dictionary (12), Dice (38), Car (1), Card (677). Như vậy để chứa 4 từ trên cây chỉ mất có rất ít số bản ghi so với dữ liệu của nó. Với cấu trúc này ta có thể nạp vào chương trình khi chạy tất cả các từ có trong một bộ từ điển lớn khoảng 100 nghìn từ mà không sợ bị tràn bộ nhớ. Hơn nữa thuật toán xây dựng các thao tác trên cây từ điển này sẽ hoạt động rất nhanh mà không phụ thuộc vào kích thước dữ liệu đầu vào.
Khi chương trình hoạt động, mối lần ta đánh vào một ký tự (Trong quá trình nhập từ để tra) chương trình sẽ phải duyệt cây từ điển để tìm ra các từ gần giống với chuỗi từ mà ta đang nhập nhất. Danh sách các từ này sẽ hiện ra giao diện chương trình. Quá trình duyệt bắt đầu từ nút Head dần dần xuống tới các nút con.
Đặc điểm của cây từ điển có rất nhiều nút lá, chiều cao của cây lại rất thấp (do số kí tự tạo ra từ không nhiều). Điều này rất thích hợp cho ta trong việc thường xuyên phải tìm kiếm trên cây. Thuật toán xây dựng trên cấu trúc dữ liệu này sẽ đơn giản và hoạt động rất nhanh. Vì Cấu Trúc Dữ Liệu là phần cơ bản của bài toán này, chúng ta sẽ tìm hiểu tiếp ở phần sau.
Tôi sẽ cung cấp cho các bạn Unit chứa các tiện ích về các thao tác cây trong chương trình :
Unit dictree;
Interface
uses Data,menu;
type list=^node;
node= record
inf:char;
child,next:list;
end;
EnglishWord= array[1..20]of string[15];
LineMean= array[1..30]of string[31];
PosDick= array[1..20]of longint;
component= record
word:string[15];
size:Byte;
mean:LineMean;
end;
procedure InserTree(s:st;n:Longint);
procedure ađisk(dic:component;Var Size:Longint);
procedure Inittree;
Procedure readtree(s:st; var result:EnglishWord; var Pos:PosDick;Var Size:Byte);
procedure readfile(x:Longint;var result:component);
procedure deleteword(s:st;n:longint);
Function FindWord(s:st):Boolean;
{---------------------------------------------------}
Implementation
{--------------------------------------------------}
procedure create(var head:list);
begin
new(head); Head^.child:=nil; Head^.Next:=nil;
end;
{-------------------------------------------------------}
procedure insertnextafter(var t:list;ch:char);
var q:list;
begin
new(q); q^.inf:=ch; q^.child:=nil; q^.next:=nil;
if t=nil then t:=q
else begin
q^.next:=t^.next;
t^.next:=q;
end;
end;
{--------------------------------------------------}
procedure insertnextbefor(var t:list;ch:char);
var s:char;
p:list;
begin
s:=t^.inf; t^.inf:=ch;
insertnextafter(t,s);
if t^.child<>nil then begin t^.next^.child:=t^.child; t^.child:=nil; end;
end;
{-----------------------------------------------------}
procedure insertchild(var t:list;ch:char);
var p:list;
begin
if t^.child=nil then
begin
new(p);
p^.inf:=ch; p^.child:=nil; p^.next:=nil;
t^.child:=p;t:=t^.child;
end else
begin
t:=t^.child;
while (t^.infnil) do t:=t^.next;
if t^.inf if t^.inf>ch then insertnextbefor(t,ch);
end;
end;
{--------------------------------------------------}
function serier(n:integer):st;
var s:st;
begin
str(n,s);
serier:=s;
end;
{------------------------------------------------------}
function number(s:string):integer;
var i,ch:integer;
begin
val (s,ch,i);
number:=ch;
end; {-----------------------------------------------------}
procedure ađisk (dic:component; Var Size: Longint);
Var i:byte;
begin
assign(f,'diction.stú);
{$i-}
reset(f);
if IOResult <> 0 then
Begin
Thong bao loi File Du lieu;
EndTask;
End;
Seek(f,FileSize(f));
write(f,dic);
if IOResult <> 0 then
Begin
Thong bao loi File Du lieu;
EndTask;
End;
Size:=FileSize(f)-1;
close(f);
end;
{-----------------------------------------------------}
procedure InserTree(s:st;n:Longint);
var i:integer;
p:list;
begin
p:=head;
for i:=1 to length(s) do insertchild(p,s[i]);
insertchild(p,'*');
s:=serier(n);
for i:=1 to length(s) do insertchild(p,s[i]);
end;
{-------------------------------------------------}
function find(var p:list;ch:char):boolean;
var sign:boolean;
t:list;
begin
find:=false;
t:=p;
if t^.child<>nil then
begin
t:=t^.child; sign:=(upcase(t^.inf)=upcase(ch));
if not sign then while (t^.next<>nil)and not(sign) do
begin
t:=t^.next;
sign:=(upcase(t^.inf)=upcase(ch));
end;
if sign then find:=true;
end;
if sign then p:=t;
end;
procedure Inittree;
var position:integer;
i,Error:Byte;
dic:component;
begin
assign(f,'diction.stú);{$I-}
reset(f);{$I+}
if IOResult <> 0 then
Begin
Thong bao loi File Du lieu;
EndTask;
End;
position:=0;
repeat {$I-}
read(f,dic);
if IOResult <> 0 then
Begin
Thong bao loi File Du lieu;
EndTask;
End;
insertree(dic.word,position);
inc(position);
until eof(f);
close(f);
end;
{------------------------------------------------------}
Procedure readtree(s:st; var result:EngLishWord; var Pos:PosDick;Var Size:Byte);
var i,count:integer;
sign:boolean;ch:st;p,t:list;
{------------------------------------------------------}
function findmean(t:list):integer;
var s:st;
begin
s:=''; t:=t^.child;
while t<>nil do
begin
s:=s+t^.inf;t:=t^.child;
end;
findmean:=number(s);
end;
{----------------------------------------------------}
begin
count:=0; i:=1;p:=head; sign:=false;

repeat

if find(p,s[i]) then inc(i) else sign:=true;

until sign or (i=length(s)+1);

s:=copy(s,1,i-1);
if p^.child<>nil then
begin
p:=p^.child;
if p^.inf='*' then
begin
count:=1; t:=p; result[count]:=s;
pos[count]:=findmean(t); p:=p^.next
end;
while p<>nil do
begin
t:=p; ch:='';

while (t<>nil)and (t^.inf<>'*')do begin ch:=ch+t^.inf; t:=t^.child; end;
if t^.inf='*' then
begin
inc(count); result[count]:=concat(s,ch);
pos[count]:=findmean(t);
end;
p:=p^.next;
end;
end;
Size:=count;end;
{-------------------------------------------------}
procedure deleteword(s:st; n:longint);
var i:integer;Error:Byte;p,t:list;sign:boolean;Dic:Com ponent;
begin
i:=1; p:=head;sign:=false;
repeat
if find(p,s[i]) then inc(i) else sign:=true;
until sign or (i=length(s)+1);
if i=(length(s)+1) then
if p^.child^.inf='*'then
begin
t:=p^.child^.child;
p^.child:=p^.child^.next;
dispose(t);
end;
assign(f,'diction.stú);{$I-}
reset(f);Seek(f,n-1);
Dic.Word:='';write(f,dic);
if IOResult <> 0 then
Begin
Thong bao loi File Du lieu;
EndTask;End;
close(f);
end;
Function FindWord(s:st):Boolean;
var i:integer;p,t:list;sign:boolean;
begin
i:=1; p:=head;sign:=false;FindWord:=False;
repeat
if find(p,s[i]) then inc(i) else sign:=true;
until sign or (i=length(s)+1);
if i=(length(s)+1) then
if p^.child^.inf='*'then
begin
FindWord:=True;
end;end;
{-------------------------------------------------}
procedure readfile(x:Longint;var result:component);
var dic:component;i,Error:Byte;
begin
assign(f,'diction.stú);{$I-}
reset(f);
if x<=filesize(f) then
begin
seek(f,x);{$I-}
read(f,result);
if IOResult <> 0 then
Begin
Thong bao loi File Du lieu;
EndTask;End;end;
close(f);
end;
{-------------------------------------------------}
begin
create(head);
Inittree;
end.

Nguyễn Thế Anh B−Lớp 11A3− Khối PTTH Chuyên Toán tin− DH.Vinh

lazy-programmer
27-02-2003, 10:43
CSDL của tôi dạng textfile, kg phải là Access hay SQL gì cả (kg phải lúc nào CSDL cũng phải là những cái ấy). Giải thuật trên có thể là tối ưu nhưng chúng ta phải tùy theo cấu trúc của CSDL mà tìm ra cách tra tối ưu nhất (kg quá phức tạp đâu). Còn về phần phát âm thì tôi chưa nghĩ tới, có lẽ phải copy ở đâu đó vậy.

Đây là trích phần đầu của từ điển Anh - Việt mà tôi có, bắt đầu bằng dấu ' (do toàn bằng font ABC nên để thấy được các bạn phải chuyển qua font ABC vậy):

@'cellist /'telist/
* danh tõ
- ng­êi ch¬i vi«l«ngxen, ng­êi ch¬i xel«

@'cello /'telou/ C¸ch viÕt kh¸c : ($'cello)$ /'telou/
* danh tõ
- (©m nh¹c) ®µn vi«l«ngxen, xel«

@'chutist /'u:tish/
* danh tõ
- ( (viÕt t¾t) cña $parachutist)$ (qu©n sù), (th«ng tôc) lÝnh nh¶y dï

@'d /-d/
* (th«ng tôc)
- (viÕt t¾t) cña $had, should, would

@'em /m/
* danh tõ
- (th«ng tôc) (viÕt t¾t) cña $them

@'gainst /geinst/
* giíi tõ
- (th¬ ca) (nh­) $against

@'ll /l/
* (viÕt t¾t) cña $will
- $I'll$ = $I will; he'll$ = $he will; that'll$ = $that will

@'s /s, z/
* (viÕt t¾t) cña $is, has, us, does
- (th«ng tôc) (nh­) $is
=$it 's raining+ trêi m­a
=$what 's the matter?+ c¸i g× ®Êy?
=$she 's gone+ c« ta ®· ®i khái
- (th«ng tôc) (nh­) $has
=$he 's done it+ anh Êy ®· lµm viÖc ®ã råi
- (th«ng tôc) (nh­) $us
=$let 's go+ nµo chóng ta ®i th«i
- (th«ng tôc) (nh­) $does
=$what 's he say about it?+ ý kiÕn cña anh Êy vÒ viÖc ®ã nh­ thÕ nµo?

@'shun /n/
* th¸n tõ
- ( (viÕt t¾t) cña $attention)$ nghiªm!

@'tis /tiz/
*
Như vậy các từ luôn bắt đầu bằng chữ @, tiếp đến là phiên âm (không rõ font chữ gì?).v.v... Theo tôi, để hiển thị kết quả, cách nhanh nhất là bê luôn cả cụm từ chữ @ này tới chữ @ sau, format và display.

Dĩ nhiên cần một giải thuật để sort các từ đứng trước và sau từ cần tìm lên một listbox/combobox.

Mong các bạn góp ý.

mel
27-02-2003, 12:04
Cái thuật toán này giống Bảng Băm (HashTable) quá, không biết phải không? Đọc sách thấy người ta nói dùng Bảng Băm để tạo CSDL cho tự điển nên nói như vậy.

attilathehun
27-02-2003, 14:35
tui nghĩ dùng text file chậm lắm, không được đâu.
hơn nữa lại không đảm bảo tính bảo mật của csdl, vì user có thể xem được hết và có thể sao chép lại rồi còn gì.

dtt_vn
27-02-2003, 17:49
chứ bồ cho người ta truy lục từ thì không bị xem hả?

phangtomat
27-02-2003, 23:34
Em viết từ điển GTVT (phục vụ cho ngành của em) rồi. Em viết bằng VB và chạy hơi chậm thật. Em chưa thử dùng với Win98,Me. Nhưng hình như nó không chạy được với Win98, hay Me đâu. Em sẽ post lên các bác coi thử nhé.

dtt_vn
27-02-2003, 23:38
tại sao lại không hạy với ME vả SE

phangtomat
28-02-2003, 16:31
Nó không chạy được với Win98 và Me vì em thấy khi chạy thì cái Listbox không hỗ trợ đến hàng nghìn từ được Add vào. Còn trong WinXP thì OK, listbox có thể Add hàng nghìn từ. Hình như do WinXP là 32 bit nên mới được.
Em post thử xem các bác chạy được không nhé. Hehe, em thử làm cái password kèm theo để bác nào thử tài ***** luôn thể.

phangtomat
28-02-2003, 16:35
co 5 file

hxen
28-02-2003, 16:44
phangtomat

hxen
28-02-2003, 16:46
who's phangtomat?

dtt_vn
28-02-2003, 22:41
bây giờ không post được đâu. với lại này chú, nếu mình nhớ không lầm thì từ W95 trở đi, người ta đã gọi cho window bằng hệ điều hành 32bits rồi mà ??? bạn nói wse và wme là không phải 32bs. không biết có chính xác không>?

phangtomat
28-02-2003, 23:07
Ơ, cú thật, tải xong rồi thì lại không thấy liên kết file đâu cả.
Em đã bảo là em không rõ nữa mà. Nhưng đúng là Listbox khi chạy dưới HĐH từ ME trở xuống thì không add nhiều được, nhưng với XP thì OK. Còn 2000 thì em chưa thử. Bác nào thử hộ em thì liên lạc với em: tmcgtvt@yahoo.com

lazy-programmer
01-03-2003, 10:57
Quả thực, sau khi nghiên cứu kỹ, tôi thấy truy tìm trực tiếp vào textfile này không hiệu quả, một giải pháp tôi cho rằng khả thi là re-indexing textfile này và lưu cấu trúc dạng cây của nó vào một file khác, hoặc thậm chí có thể re-format lại. Nhưng hiện lazyman tôi không có thời gian nghiên cứu thêm, mong các bạn góp ý.

Cảm ơn bạn attilathehun đã post lên một giải thuật rất tốt.

subzero
01-03-2003, 14:09
Nói chung, thì CSDL dùng Access làm là cũng được. Chỉ cần 2 bảng thôi. Một bảng chứa từ, một bảng chứa nghĩa. Khi search thì ở bảng từ nên tốc độ cao. Tôi cũng đã làm 1 số CT mà số lượng Record lên độ trăm nghìn thì thấy Access làm việc hơi chậm nhưng sau khi Compact & Repair mọi chuyện khác hẳn, nó hoạt động rất tuyệt.

attilathehun
01-03-2003, 20:30
vậy phải có lí do nào để LVMTD EVA không sử CSDL của Access chứ

subzero
01-03-2003, 22:22
Lý do chống ăn cắp dữ liệu nên họ mới phải tự tạo ra database cho riêng họ !

phangtomat
01-03-2003, 23:02
EM cũng nghĩ thế. Đúng đấy.

BlueStar
17-03-2003, 17:33
Cho tôi bộ CSDL của bạn đi .
Chương trình tôi tự viết để "nhà dùng" cũng được .
Mail cho tôi : Bluestar001@mail.com
Thanks !