Các câu đố, bài tập nhằm ôn tập & bổ sung kiến thức căn bản VBA

Liên hệ QC

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,569
Được thích
16,626
Giới tính
Nam
Với tinh thần chơi mà học, học mà chơi, nên tôi đã mở ra topic này, hy vọng các thành viên tham gia, nhất là các thành viên mới biết về VBA.

Sau đây là câu hỏi đầu tiên:

Câu hỏi 1: Bằng phương pháp nào nhanh nhất để tìm ra ô nào trong một cột chứa một điều kiện.

Tôi có 1 file Excel 2007, với cột A, từ A1 đến A1048576 đều có giá trị.

Bằng phương pháp nào nhanh nhất (dùng mảng, dùng For Each v.v...) để tìm ra ô nào trong cột A chứa chữ "Nghia", đồng thời với ô ở cột B tương ứng nhập giá trị "OK" vào đó?

Ví dụ tìm thấy trong ô A2 có giá trị là "Nghia" thì ô B2 nhập vào "OK".

Hiện tại, đáp án nhanh nhất mà tôi có được đã gửi mail riêng (nhằm ghi lại thời gian gửi, để tránh nói ăn gian).

Để tiện việc theo dõi các câu đố, các bài tập tôi đã tạo ra topic này các bạn click vào đây:

Các link của topic "Các câu đố, bài tập nhằm ôn tập & bổ sung kiến thức căn bản VBA"
 

File đính kèm

  • DoVuiCanBan.rar
    1.3 MB · Đọc: 618
Lần chỉnh sửa cuối:
Rỗi không các bạn, ngày nghỉ mà:

TÌM CÁCH VIẾT HOA DÒNG TIÊU ĐỀ BÁO CÁO.

Chúng ta có 1 cơ sở dữ liệu (CSDL) cấp fát bảo hộ lao động (BHLĐ) của 1 tỉnh như lượt trích dưới đây:

B | C | D | E | F | G | | L | M | N | O | P | Q Ngày | MaBH | Ten BH | MaNV | TenNV | MaDVi |…| Ma | Ten BHLĐ | ĐVT | Niên hạn ||
1/18/09|wLF|Áo quần lễ fục|NTK00|Ng. T.T. Kha|AL|…|GDa|Giầy |đôi|2||
2/2/09|zLF|Mũ lễ fục|NTK00|Ng. T.T. Kha|AL|…|MuK|Mũ kê pi |cái|2||
2/23/09|wLF|Áo quần lễ fục|NKT00|Ng. K.Tuyến|PC|…|BTD|QA thu đông |bộ|2||
3/3/09|zLF|Mũ lễ fục|NKT00|Ng. K.Tuyến|PC|…|MuM|Mũ mềm |cái|2||
.|.|.|.|.|.|…|.|.|.|.|.|.
5/10/12|BTD|QA thu đông|NKD00|Ng. K.Diễm|TS|…| MaNV | HoTen | MĐV || MaH | Huyện
6/14/12|TLD|Thắc lưng|NKD00|Ng. K. Diễm|TS|…|NKT01|Ng. K Tuyến |PC||PC|Phù Cát
.|.|.|.|.|.|…|.|.|.|.|.|.

CSDL này liệt kê ngày cấp gần chục loại trang bị BHLĐ hàng năm cho 200 nhân viên kiểm lâm.
Để theo dõi việc cấp fát BHLĐ đến từng cá nhân, ta cần lập “SỔ CẤP PHÁT BHLĐ” cho từng cá nhân trong tỉnh.
Sổ đó có dạng trình bày như sau:

3. . . . . . . . SỔ LĨNH BHLĐ CỦA Trần Bảo Trân.
4. . . . . . . . Đơn vị: Hoài An.

6 |TT|Ngày|MaBH|Tên BHLĐ|Ghi chú
7 |1|4/8/09|wLF|Áo quần lễ f ục|
8 |2|4/9/09|zLF|Mũ Kê pi lễ fục|
9 |3|3/4/13|MuK|Mũ Kê pi|
10 |4|3/4/13|Gda|Giầy da|
..|…|….|….|
15 |9|3/21/13|ADM|Áo mưa|

Để trích lọc & tạo sổ theo dõi thực lãnh BHLĐ từ trang “DuLieu” sang trang “SoBHLD” cho từng cán bộ nhân viên coi như đã làm xong;


Đề bài tập sẽ là:

Tại dòng 3 ta thấy chưa thỏa đán cho lắm;
Các bạn làm sao để dòng này có dạng:

3. . . . . . . . SỔ LĨNH BHLĐ CỦA TRẦN BẢO TRÂN.
(Xin các bạn xem thêm trong file)
 

File đính kèm

  • gpeBaiTap.rar
    12 KB · Đọc: 14
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Đố các bạn kết quả MsgBox sẽ trả về là bao nhiêu?

Các bạn tự đoán trước kết quả và chạy code sau nhé!

Mã:
Sub Test()
    Dim a As Long, b As Long
    a = 1.5
    b = 1.5
[COLOR=#ff0000][B]    MsgBox a + b[/B][/COLOR]
End Sub

Các bạn rút ra điều gì khi sử dụng biến Long?
 
Upvote 0
Đố các bạn kết quả MsgBox sẽ trả về là bao nhiêu?

Các bạn tự đoán trước kết quả và chạy code sau nhé!

Mã:
Sub Test()
    Dim a As Long, b As Long
    a = 1.5
    b = 1.5
[COLOR=#ff0000][B]    MsgBox a + b[/B][/COLOR]
End Sub

Các bạn rút ra điều gì khi sử dụng biến Long?
Khi anh làm quen với việc tạo CSDL thì anh sẽ biết cái này, kiểu biến cũng gần giống như kiểu dữ liệu ở các trường trong CSDL. Tùy vào ý đồ cũng như việc tính toán, lưu trữ mà ta chọn kiểu dữ liệu khác nhau.

VD: Theo bài của anh ở trên là số thập phân thì phải chọn kiểu dữ liệu dạng double hoặc single. Nếu như chọn kiểu Long nó sẽ mặc định làm tròn số nếu số thập phân >=5 thì sẽ làm tròn lên, ngược lại sẽ làm tròn xuống (Giống như hàm Round(So,0) ở Excel). Áp dụng vào bài anh thì a=1.5=>2, b=1.5=>2 Như vậy a+b sẽ cho ra là 4 là điều dể hiểu.
 
Upvote 0
Khi anh làm quen với việc tạo CSDL thì anh sẽ biết cái này, kiểu biến cũng gần giống như kiểu dữ liệu ở các trường trong CSDL. Tùy vào ý đồ cũng như việc tính toán, lưu trữ mà ta chọn kiểu dữ liệu khác nhau.

VD: Theo bài của anh ở trên là số thập phân thì phải chọn kiểu dữ liệu dạng double hoặc single. Nếu như chọn kiểu Long nó sẽ mặc định làm tròn số nếu số thập phân >=5 thì sẽ làm tròn lên, ngược lại sẽ làm tròn xuống (Giống như hàm Round(So,0) ở Excel). Áp dụng vào bài anh thì a=1.5=>2, b=1.5=>2 Như vậy a+b sẽ cho ra là 4 là điều dể hiểu.

Bài học rút ra ở đây:

1) Đừng nghĩ biến Long là một số nguyên mà đánh đồng nó với giá trị có hàm INT. Vì thế nếu muốn nó là số nguyên không làm tròn thì thêm hàm INT vào giá trị trước khi biến đó được định giá trị.

2) Khi tính toán, nếu giá trị có số thập phân thì nên chọn biến có kiểu dữ liệu là Double hoặc Single, hoặc Currency

Và nếu lựa chọn giữa 3 biến Double/ Single/ Currency, ta lại làm tiếp bài test sau:

Mã:
Sub Test()
    Dim c As Currency
    Dim d As Double
    Dim s As Single
    
    c = 1 / 3
    d = 1 / 3
    s = 1 / 3
    
    MsgBox "d = c: " & (d = c) & _
    vbLf & vbLf & "d: " & d & _
    vbLf & vbLf & "c: " & c
    
    MsgBox "c = s: " & (c = s) & _
    vbLf & vbLf & "c: " & c & _
    vbLf & vbLf & "s: " & s


    MsgBox "d = s: " & (d = s) & _
    vbLf & vbLf & "d: " & d & _
    vbLf & vbLf & "s: " & s
End Sub

Kết quả d = c = s, nhưng hiển thị lại khác nhau. Các bạn rút ra được điều gì?
 
Lần chỉnh sửa cuối:
Upvote 0
Trong hầu hết các ngôn ngữ lập trình đều có một luật gọi là luật ép kiểu. Luật này được trình dịch (compiler) áp dụng triệt để khi có sự không thống nhất giũa các dữ liệu.

a là kiểu dữ liệu được khai báo là Long. Compiler luôn luôn biết nó là Long
1.5 là kiểu dữ liệu được mặc định là Float hoặc Double, tuỳ theo mặc định môi trường.

Vì đây là hai kiểu khác nhau nên khi sử dụng toán tử gán (=), compiler bắt buộc phải lôi luật ép kiểu ra.
Theo quy định của VBA, lúc ép kiểu sang Integer hay Long, Float và Double sẽ bị làm tròn.

(*) Ghi chú: mỗi ngôn ngữ có một cách ép kiểu khác nhau, lúc ép kiểu cần phải cẩn thận. Tuy VBA làm tròn, nhưng ngôn ngữ khác (T-SQL chẳng hạn) có thể sẽ chặt bỏ phần thập phân (.9999999 cũng bị chặt bỏ như thường)

(**) Ghi chú 2: có ngôn ngữ không ép kiểu của biểu thức mà ngược lại, tự động đổi kiểu dữ liệu của biến.
 
Upvote 0
Luật sử dụng biến Float/Double:

Người lập trình chân chính không bao giờ so sánh trực tiếp 2 biến float với nhau qua toán tử "=".

Để so sánh, người ta xét xem chúng có gần bằng nhau hay không. Theo luật lập trình, nếu 2 số cách nhau ít hơn một khoảng ép si lon rất nhỏ nào đó thì coi như bằng nhau.

if (abs(float1 - float2) < 1E-10) then { 2 số coi như bằng nhau }
 
Upvote 0
Trong hầu hết các ngôn ngữ lập trình đều có một luật gọi là luật ép kiểu. Luật này được trình dịch (compiler) áp dụng triệt để khi có sự không thống nhất giũa các dữ liệu.

a là kiểu dữ liệu được khai báo là Long. Compiler luôn luôn biết nó là Long
1.5 là kiểu dữ liệu được mặc định là Float hoặc Double, tuỳ theo mặc định môi trường.

Vì đây là hai kiểu khác nhau nên khi sử dụng toán tử gán (=), compiler bắt buộc phải lôi luật ép kiểu ra.
Theo quy định của VBA, lúc ép kiểu sang Integer hay Long, Float và Double sẽ bị làm tròn.

(*) Ghi chú: mỗi ngôn ngữ có một cách ép kiểu khác nhau, lúc ép kiểu cần phải cẩn thận. Tuy VBA làm tròn, nhưng ngôn ngữ khác (T-SQL chẳng hạn) có thể sẽ chặt bỏ phần thập phân (.9999999 cũng bị chặt bỏ như thường)

(**) Ghi chú 2: có ngôn ngữ không ép kiểu của biểu thức mà ngược lại, tự động đổi kiểu dữ liệu của biến.

Cám ơn bạn đã bổ sung kiến thức từ VBA sang các ngôn ngữ khác. Tuy nhiên chúng ta đang bàn về VBA và bài tôi gửi lên không phải đánh đố, mà nhằm cho các bạn mới học hoặc đã biết VBA từ lâu nhưng chưa chú ý đến vấn đề này.
 
Upvote 0
Cám ơn bạn đã bổ sung kiến thức từ VBA sang các ngôn ngữ khác. Tuy nhiên chúng ta đang bàn về VBA và bài tôi gửi lên không phải đánh đố, mà nhằm cho các bạn mới học hoặc đã biết VBA từ lâu nhưng chưa chú ý đến vấn đề này.

Tôi không có ý lôi kéo đề tài sang chỗ khác. Vì có bạn nói tới CSDL cho nên tôi nhắc nhở rằng không phải ở đâu cũng có phép ép kiểu giống nhau.

Riêng trong VBA thì ví dụ của bạn chưa hoàn hảo. Bạn cần đưa thêm ra chi tiết để thấy VBA dùng phép Round của nó khi ép kiểu. Nói cách khác, nếu bạn gán
a = 1.5 hay a = 2.5 thì dều thấy cuối cùng a = 2.
 
Upvote 0
Tôi không có ý lôi kéo đề tài sang chỗ khác. Vì có bạn nói tới CSDL cho nên tôi nhắc nhở rằng không phải ở đâu cũng có phép ép kiểu giống nhau.

Riêng trong VBA thì ví dụ của bạn chưa hoàn hảo. Bạn cần đưa thêm ra chi tiết để thấy VBA dùng phép Round của nó khi ép kiểu. Nói cách khác, nếu bạn gán
a = 1.5 hay a = 2.5 thì dều thấy cuối cùng a = 2.
Như vậy vẫn chưa hoàn hảo hihihihi, thử test thủ tục dưới đây:

Mã:
Sub Test()
    Dim a As Long, b As Byte
    For b = 1 To 10
        a = b + 0.5
        MsgBox "b" & b & ": " & a
    Next
End Sub

Ta lại thấy điều gì nhỉ? Với biến b, Số lẻ thì sao và số chẳn thì sao?
 
Lần chỉnh sửa cuối:
Upvote 0
Trong hầu hết các ngôn ngữ lập trình đều có một luật gọi là luật ép kiểu. Luật này được trình dịch (compiler) áp dụng triệt để khi có sự không thống nhất giũa các dữ liệu.

a là kiểu dữ liệu được khai báo là Long. Compiler luôn luôn biết nó là Long
1.5 là kiểu dữ liệu được mặc định là Float hoặc Double, tuỳ theo mặc định môi trường.

Vì đây là hai kiểu khác nhau nên khi sử dụng toán tử gán (=), compiler bắt buộc phải lôi luật ép kiểu ra.
Theo quy định của VBA, lúc ép kiểu sang Integer hay Long, Float và Double sẽ bị làm tròn.

(*) Ghi chú: mỗi ngôn ngữ có một cách ép kiểu khác nhau, lúc ép kiểu cần phải cẩn thận. Tuy VBA làm tròn, nhưng ngôn ngữ khác (T-SQL chẳng hạn) có thể sẽ chặt bỏ phần thập phân (.9999999 cũng bị chặt bỏ như thường)

(**) Ghi chú 2: có ngôn ngữ không ép kiểu của biểu thức mà ngược lại, tự động đổi kiểu dữ liệu của biến.

Thông tin đỏ đỏ theo tôi không có giá trị. Không phải là sai. Vì trong mỗi ngôn ngữ có thể sẽ khác nên luôn luôn phải kiểm tra xem trong ngôn ngữ mà mình đang dùng nó thế nào.
Delphi rất khắc nghiệt trong chuyện dùng kiểu. Nếu a được khai báo là BYTE, Integer, hay DWORD thì không thể viết:
Mã:
a := 4 / 2
được vì compiler sẽ thông báo lỗi. Vì đối với Delphi thì 4 / 2 không là 2 mà là 2.0, tức giá trị Extended. Chỉ viết được như trên khi a được khai báo là Single, Double hoặc Extended.
Phép gán là được phép khi 2 kiểu dữ liệu là "phù hợp". Ví dụ Integer, Byte, Dword là phù hợp với nhau (cùng là những giá trị "nguyên") hoặc Single, Double, Extended.
Hoặc chẳng hạn PBYTE, PInteger, PDWORD là những pointer, tức giá trị "nguyên", nên nếu a là giá trị Byte, Integer, Dword còn b được khai báo là PDWORD (kiểu pointer) thì có thể viết

Mã:
b := [COLOR=#ff0000]PDWORD[/COLOR](a); (type casting)


Nhưng không thể viết
Mã:
b := a;

Mà người lập trình phải "ép" (PDWORD(...)), Delphi không "ép" gì cả

Tất nhiên nếu a là Integer và b là Double thì không được phép a := b; nhưng lại được phép b := a; Cái này nó cũng kiểu như: "Số tự nhiên luôn là số thực, nhưng số thực chưa chắc là số tự nhiên".

Trong Delphi nếu 2 kiểu dữ liệu không "phù hợp" với nhau thì compiler "không cho qua", compiler không lôi "luật ép kiểu" ra, cũng không "tự động đổi kiểu dữ liệu của biến"

Vậy thì đã chuyển sang ngôn ngữ khác thì phải tìm hiểu.
 
Lần chỉnh sửa cuối:
Upvote 0
Trong hầu hết các ngôn ngữ lập trình đều có một luật gọi là luật ép kiểu. Luật này được trình dịch (compiler) áp dụng triệt để khi có sự không thống nhất giũa các dữ liệu.

...

Tôi đã cẩn thận dùng từ "hầu hết" để phân biệt với "tất cả". FORTRAN cũng đâu có ép kiểu.

Trình dịch không tự động ép kiểu thì nó từ chối ngay từ đầu, không phải quan tâm lắm.
Gặp những lúc trình dịch ép kiểu mới quan trọng. Nếu không quen cách thức ép kiểu thì có thể nhận kết quả sai mà không biết.
 
Upvote 0
Với tinh thần chơi mà học, học mà chơi, nên tôi đã mở ra topic này, hy vọng các thành viên tham gia, nhất là các thành viên mới biết về VBA.

Sau đây là câu hỏi đầu tiên:

Câu hỏi 1: Bằng phương pháp nào nhanh nhất để tìm ra ô nào trong một cột chứa một điều kiện.

Tôi có 1 file Excel 2007, với cột A, từ A1 đến A1048576 đều có giá trị.

Bằng phương pháp nào nhanh nhất (dùng mảng, dùng For Each v.v...) để tìm ra ô nào trong cột A chứa chữ "Nghia", đồng thời với ô ở cột B tương ứng nhập giá trị "OK" vào đó?

Ví dụ tìm thấy trong ô A2 có giá trị là "Nghia" thì ô B2 nhập vào "OK".

Hiện tại, đáp án nhanh nhất mà tôi có được đã gửi mail riêng (nhằm ghi lại thời gian gửi, để tránh nói ăn gian).

Để tiện việc theo dõi các câu đố, các bài tập tôi đã tạo ra topic này các bạn click vào đây:

Các link của topic "Các câu đố, bài tập nhằm ôn tập & bổ sung kiến thức căn bản VBA"
Em mới mua cuốn sách lập trình VBA của thầy Hướng và em đang tập tành viết code, em có thầy chủ đề này của anh Nghĩa và em giải câu đố thứ nhất của anh. Em không xem cách giải của bạn nào cả. Mục đích chính là để tự mình suy nghĩ tìm tỏi. Em có viết 2 đoạn code như sau:
Đoạn 1:
Public Sub tim_tu()
Dim i As Long, rng As Range, cll As Range
Set rng = Sheet1.Range("A1:B65536")
For Each cll In rng
If cll.Value = "Nghia" Then cll.Offset(0, 1) = "Ok"
Next cll
End Sub
Đoạn 2:
Public Sub timtu()
Dim arr(), i As Long
arr = Worksheets("sheet1").Range("A1:B65536").Value
For i = 1 To UBound(arr, 1)
If arr(i, 1) = "Nghia" Then
arr(i, 2) = "Ok"
End If
Next i
End Sub
Ở code 1 em chạy kết quả chính xác, còn ở code 2 em không hiểu sao code chạy không báo lỗi gì nhưng nó không ra kết quả là "Ok" bên cạnh chữ "Nghia" ạ?
Mong anh chị giải thích giúp ạ!
 

File đính kèm

  • caudo1.rar
    272.5 KB · Đọc: 14
Lần chỉnh sửa cuối:
Upvote 0
Code thứ nhất bạn đọc và ghi trực tiếp trên range/cells của bảng tính cho nên bạn thấy kết quả.
Code thứ hai bạn trich xuất dữ liệu vào một mảng. Sau đó bạn ghi trên mảng mảng này. Mảng triong bài này là một vùng trên bộ nhớ. Bạn đâu có chi nó lại trên range thì làm sao mà thấy được.

Khuyến khích:
Bạn tự học mày mò là đáng khích lệ. Nhưng sau mỗi lần tự giải một cái gì đó thì cũng nên tìm hiểu thêm xem người khác giải ra sao để học thêm giải thuật (lưu ý tôi dùng "sau")
 
Upvote 0
Code thứ nhất bạn đọc và ghi trực tiếp trên range/cells của bảng tính cho nên bạn thấy kết quả.
Code thứ hai bạn trich xuất dữ liệu vào một mảng. Sau đó bạn ghi trên mảng mảng này. Mảng triong bài này là một vùng trên bộ nhớ. Bạn đâu có chi nó lại trên range thì làm sao mà thấy được.

Khuyến khích:
Bạn tự học mày mò là đáng khích lệ. Nhưng sau mỗi lần tự giải một cái gì đó thì cũng nên tìm hiểu thêm xem người khác giải ra sao để học thêm giải thuật (lưu ý tôi dùng "sau")
Em rất cảm ơn anh đã giải thích. Tại em cũng chưa hiểu rõ cách sử dụng biến mảng nên mới vậy ạ.
Anh (chị) có thể chỉ giúp em cách ghi lại giá trị của biến mảng vào Range để em hiểu rõ được không ạ?
 
Lần chỉnh sửa cuối:
Upvote 0
sao topic này lâu quá không có cao nhân nào hỏi thế nhỉ
 
Upvote 0
Ồ có người bới lên mình mới biết chị Điều hành viên còn có cả tài viết code nữa cơ đấy . hâm mộ chị Điều hành viên quá đi . --=0--=0--=0
 
Upvote 0
vâng, mặc dù là những câu đố "căn bản", nhưng tôi tin là các thành viên đã, đang, sẽ học vba trong đó có tôi, sẽ chờ một đáp án bằng dao hoặc ado để giải quyết vấn đề ở câu đố 1 nhằm mục đích để học hỏi, để mở rộng thêm tầm mắt của mình.

Dù là tốc độ đã xét rồi, nhưng sáng nay tôi cũng ngẫm nghĩ và có một giải pháp cho bài đó cũng khá nhanh (có lẽ không bằng kiểu xử lý mảng rồi gán trực tiếp xuống chuỗi dùng cho trường hợp dữ liệu như trong file đính kèm), nhưng đây cũng là một phương pháp hữu hiệu nhưng ít thành viên nhắc đến, đó là dùng find method.

Dùng find method tôi đã thấy người ta dùng rất nhiều, nhưng tôi chưa đọc được bài nào viết trên diễn đàn nói về dùng find next. Vậy nhân đây tôi xin giới thiệu cách viết này cho các bạn cùng tham khảo, góp ý.

Mã:
private sub findnext()
    dim c as long, i as long, j as long, r as long, _
        frng as range, lrng as range
    with sheet1.range("a1:a1048576")
        set lrng = .cells(1, 1)
        for i = 1 to .rows.count
            set frng = .find(what:="nghia", after:=lrng, lookin:=xlformulas, lookat:=xlwhole)
            if frng is nothing then exit for
            if frng.row = 1 then exit for
            .findnext(after:=lrng).offset(, 1) = "ok"
            set lrng = frng
        next
    end with
end sub

các bạn lưu ý: Với cách tìm kiếm giá trị thì lookin:=xlformulas luôn luôn nhanh hơn lookin:=xlvalues.

--------------------------------------------

do code trên tôi làm một cách vội vã, chưa kiểm tra kỹ nên sẽ phát sinh vòng lặp chạy liên tục nếu ô đầu tiên không thỏa điều kiện, cho nên tôi đã sửa lại ở bài này:

http://www.giaiphapexcel.com/forum/showthread.php?86400-Đố-vui-căn-bản-về-vba&p=538878#post538878

chân thành xin lỗi các bạn!
Lâu lắm hôm nay rãnh mới đọc được bài này
hình như code của bạn hiền dùng find +for next vẫn còn chậm 1 nửa so với find + do loop
--=0
 
Upvote 0
Web KT
Back
Top Bottom