Bài tập VBA cho người mới bắt đầu

Liên hệ QC

SA_DQ

/(hông là gì!
Thành viên danh dự
Tham gia
8/6/06
Bài viết
14,320
Được thích
22,361
Nghề nghiệp
Nuôi ba ba & trùn quế
Các bạn viết cho 1 macro để ta có được kết quả như bảng sau:

2/29/2004​
2/29/2016​
2/29/2000​
2/29/2012​
2/29/2024​
2/29/2008​
2/29/2020​
2/29/2032​
2/29/2044​
2/29/2028​
2/29/2040​
2/29/2052​
2/29/2036​
2/29/2048​
2/29/2060​
2/29/2072​
2/29/2056​
2/29/2068​
2/29/2080​
2/29/2064​
2/29/2076​
2/29/2088​
2/29/2084​
2/29/2096​
2/29/2092​
 
Phải cảm ơn cái resume next vì nhờ nó mới biết 2100 không có ngày 29, thế là cdate nó chuyển về 29/2/2096 nên thêm 1 lặp, lỗi mà lại hay bác ạ.
Hay cái con tiều. Làm gì có chuyện cdate chuyển "29/2/2100" thành ngày tháng 29/2/2096.
cdate bị lỗi Type Mismatch nên cóc chuyển gì cả, và biến day còn giữ giá trị cũ, nó cứ thế mà phang giá trị cũ đó vào mảng và phang xuống sheet.
 
Lần chỉnh sửa cuối:
Upvote 0
Hay cái con tiều. Làm gì có chuyện cdate chuyển "29/2/2100" thành ngày tháng 29/2/2096.
cdate bị lỗi Type Mismatch nên cóc chuyển gì cả, và biến day còn giữ giá trị cũ, nó cứ thế mà phang giá trị cũ đó vào mảng và phang xuống sheet.
Hic, thế mà em cứ tưởng cdate nó biết xử lý tình huống. :wallbash: :wallbash: :wallbash:
 
Upvote 0
2100 thuộc về thế kỷ 22, vậy theo anh năm 100 thuộc thế kỷ thứ 2, và thế kỷ thứ nhất chỉ có 99 năm? Nghĩa là tôi sai năm sáu chục năm nay?
...
Việc năm 2000 thuộc về thế kỷ nào, kỷ nguyên nào vẫn là vấn đề còn tranh cãi. Tương tự, đặt 2100 vào thế kỷ nào cũng tùy thuộc ý chủ quan.
Ở đây, tôi chủ quan theo nhu cầu tiện lợi. Gọi vậy cho tiện.
Rất có thể, bài này thì tôi cho 2100 thuộc về thế kỷ 22. Nhưng bài khác, do nhu cầu tiện lợi, tôi lại đặt nó vào thế kỷ 21.
 
Upvote 0
Tương tự, đặt 2100 vào thế kỷ nào cũng tùy thuộc ý chủ quan.
Chân lý chỉ có 1. Toán học chỉ có hoặc đúng hoặc sai. Định nghĩa thế kỷ là của toàn cầu mà anh cho "ý chủ quan" vào :p :p :p
Hic, thế mà em cứ tưởng cdate nó biết xử lý tình huống.
Học mà tưởng với mơ thì cả đời không tiến bộ.
 
Upvote 0
Code theo phân tích ở bài #9 (nhảy 28 năm, không cần biết thứ trong tuần)

Sub tt()
Dim a(1 To 4, 1 To 7)
yr = 2004
Do
If yr >= 2100 Then
yr = yr - 100
If rw < UBound(a) Then If CalcPos(rw, cl) Then Exit Do ' nhảy qua cột khác
End If
If CalcPos(rw, cl) Then Exit Do
a(rw, cl) = DateSerial(yr, 2, 29)
yr = yr + 28
Loop
[a6].Resize(4, 7) = a
End Sub

Function CalcPos(ByRef rw, ByRef cl) As Long
' tính vị trí (dòng cột) kế tiếp để ghi mảng 4x7
rw = rw + 1
If rw > 4 Then rw = 1
If rw = 1 Then cl = cl + 1
If cl > 7 Then CalcPos = -1 ' quá tải mảng
End Function
 
Upvote 0
Bài tập 2
Đề bài rõ ràng hơn bài 1 :p :p :p
Thực hiện công việc sau:
Cho ô C1 chứa năm bắt đầu và F1 chứa năm kết thúc mong muốn. Liệt kê những ngày 29/02 của những năm nhuận trong khoảng thời gian bắt đầu/ kết thúc trên vào bảng theo thứ trong tuần, đầu tuần là thứ hai ( viết tắt Mon ở A2) như hình bên dưới:
Kết quả phải là ngày dạng chuẩn và tự động canh phải.
Cho phép chọn năm bắt đầu và năm kết thúc bất kỳ (không lệ thuộc vào đầu cuối thế kỷ, không giới hạn 100 năm)
Với phân tích giải thuật ở bài #10, sẽ thấy số dòng không tự động tăng trong 1 số trường hợp, và 1 dòng không phải lúc nào cũng điền đầy rồi mới qua dòng kế.

Minh họa: Chọn từ 2001 đến 2100 (100 năm)

1707148858106.png

Cũng 100 năm, bắt đầu 2512


1707149946367.png

Chọn từ 2005 đến 2154 (150 năm)

1707148950505.png

Chọn từ 3340 đến 3539 (200 năm)

1707149031846.png
 
Lần chỉnh sửa cuối:
Upvote 0
Sửa trên dưới 30s. --=0 --=0 --=0

Mã:
Option Explicit

Sub zzz()
    Dim day As Date
    Dim j&, Hang&, Cot&, HangMax&
    Dim ArrNgay(1 To 1000, 1 To 7) As Variant
    On Error Resume Next
    For Cot = 1 To 7
        Hang = 0

        For j = Application.Ceiling([C1].Value, 4) To Application.Floor([F1].Value, 4) Step 4'SUA THANH HAM CELING

            day = CDate("2/29/" & j)
            If day = DateSerial(j, 2, 29) Then
            If Application.Weekday(day, 2) = Cot Then
                Hang = Hang + 1
                ArrNgay(Hang, Cot) = day
                HangMax = Application.Max(Hang, HangMax)
            End If
            End If
        Next
    Next
    Range("A3").Resize(HangMax, 7).Value = ArrNgay
    Range("A2").Resize(1, 7).Value = Array("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN")
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Upvote 0
Tóm lược về code của tôi đới với đề bài 1 (bài #17 và #25)
Bài này thách thức độ hiểu về Array ở hạng trung.
Bài #17 cho thấy cách tính chỉ số theo dạng dòng sang cột.
Bài #25 cho thấy cách tính chỉ số theo dạng cột sang dòng.

Riêng bài #25 cho thấy cách sử dụng hàm con.
Hàm con này tính vị trí kế tiếp trong mảng.
1. vì Function chỉ có thể trả về một trị cho nên phải dùng ByRef cho các tham số cột, dòng.
2. vì các con số cần thiết tính toán đã được trả về theo tham số cho nên hàm còn Function còn dư được trị trả về.
Code bài này cố ý lợi dụng trị trả về để cho biết có cần đi tiếp hay không. Nếu trị trả về <> 0 thì mảng đã đầy, không cần đi tiếp.
Mẹo này ít thấy trogn VBA nhưng khá thông dụng trong code C và các ngôn ngữ thuộc dòng họ ấy (kể cả JavaScript, Google App Script, ...)

Về đề bài 2:
Để xem có giải thuật nào cần code bậc cao về Array hay không. Nếu có thì tôi sẽ viết code. Nếu không thì thôi nhường.
 
Upvote 0
Kết quả là text. Nếu tính từ 2100 đến 2199 thì xuất hiện 29/02/2100 trong khi đó năm 2100 không nhuận. Thứ tự trong tuần không còn đúng nữa: Hai cột đầu tiên là thứ sáu và thứ bảy.

View attachment 298960

Nếu tính từ 2100 đến 2199 thì cũng xuất hiện 29/02/2100, và thứ tự trong tuần không đúng nữa: Hai cột đầu tiên là thứ sáu và thứ bảy.

View attachment 298959


Nếu tính từ 2100 đến 2199 thì cũng xuất hiện 29/02/2100, do dùng DateSerial nên chuyển thành 01/03/2100, ở cột 2, sau đó bị ghi đè bởi 29/02/2112.
Khà khà khà! Yêu cầu như thế nào thì làm vừa đủ là tốt rùi, yêu cầu khác thì phải làm khác.
 
Upvote 0
Một trong 2 thứ thôi. Trong đó DateSerial ra dạng ngày chuẩn và không lỗi MisMatch.
Đã biết DateSerial 29/2 biến thành 01/03 thì kiểm tra Day() có phải 29 hay không là được.

Ghi chú: Không ai mà lại đặt tên biến trùng với tên hàm như day
Thử với 1 vòng lặp xem. Và test cho kỹ chứ đừng để người khác nhìn thoáng qua code thấy sai/ lỗi mà mình thì không thấy. Vụ floor là 1 thí dụ.
 
Upvote 0
Một trong 2 thứ thôi. Trong đó DateSerial ra dạng ngày chuẩn và không lỗi MisMatch.
Đã biết DateSerial 29/2 biến thành 01/03 thì kiểm tra Day() có phải 29 hay không là được.
Người viết code khong biết rằng hàm CDate chỉ dùng để chuyển ngày dạng khác số (text,...) thành ngày.
Vì là phép ép kiểu cho nên nó có những giới hạn và những luật mặc định của nó.
Người viết code ỷ trượng vào câu thần chú "vượt qua lỗi" của mình. Điển hình là loại viết code chạy sai mà không biết là kết quả sai. On error chỉ vượt qua lỗi VBA tính không được chứ đâu có cho biết chỗ nào kết quả sai.

Chú: không biết cả luật "trăm không ngàn nhuận" thì có vẻ hơi yếu môn khoa học thường thức (học về những chuyện hằng ngày). Cần học lại

Ghi chú: Không ai mà lại đặt tên biến trùng với tên hàm như day
Không hẳn vậy. những biến, hàm có sẵn trong VBA thì chỉ tránh khai báo lại ở dạng toàn cục thôi.
Với dạng biến/hằng/hàm nội, đôi khi người ta cố tình sử dụng luật "phạm vi định danh" để làm gì đó.
Phạm vi định danh (namespace): biến nội có phạm vi toàn function/sub. Nếu nó trùng tên với một biến/hàm nào đó của VBA thì nó sẽ che biến/hàm này, Function/Sub ấy sẽ không thể truy cập.
Trường hợp muốn truy cập thì bắt buộc phải dùng tiến tố VBA.
Ví dụ

Bên trong hàm này, vbRed được khai báo lại. Cho nên có thể gán trị nó tùy thích
1707207082442.png

Bên trong hàm này, vì không khai báo cho nên vbRed là hằng của VBA
1707207377701.png

Vì không bị che cho nên trong hàm này, vbRed và VBA.vbRed là một.
1707207477573.png
 
Upvote 0
Người viết code khong biết ...
Người viết code ỷ trượng vào câu thần chú "vượt qua lỗi" của mình.
Tôi chán chả muốn nói nữa rồi, nó lì quá, đã nn mà còn lì.
Chú: không biết cả luật "trăm không ngàn nhuận" thì có vẻ hơi yếu môn khoa học thường thức (học về những chuyện hằng ngày). Cần học lại
Hơi yếu: anh lại nói giảm nhẹ quá rồi.
Kiến thức năm nhuận tôi học từ lớp 7. Khoa học thường thức là kiến thức của lớp 5 cho đến lớp 9.
 
Lần chỉnh sửa cuối:
Upvote 0
Dịch không được bác ạ.

Mà với người chuyên không biết code như em thì ra được kết quả là hết hồn rồi. Em thích tối ưu, nhưng nếu không có khả năng tối ưu thì chấp nhận thôi. --=0 --=0 --=0

luật "trăm không ngàn nhuận"
Cái quy tắc này là sao bác nhỉ? Em mới nghe luôn ấy. Bác giải đáp giúp em, khổ, chi mà phải cứng nhắc thế nhỉ?
 
Upvote 0
Bài tập 2:
4 test ví dụ bài #26 không có cái nào chứa năm '000 :unknw:
Test như vậy là chưa đủ :p

Giải: code lập mảng thì chả khác mấy với bài #17. Coi như dùng lại được.
Còn lại, theo giải thuật tôi thì thêm code tính năm nhuận đầu tiên, và code loại đi các năm '00s, giữ lại các năm '000s.

Sub MangNamNhuan(ByVal yrSt As Long, ByVal yrEn As Long, wrPos As Range)
ReDim mang(1 To yrEn - yrSt + 1, 1 To 7)
Dim r(1 To 7) As Long
For yr = yrSt To yrSt + 3 ' tìm năm nhuận
If Day(DateSerial(yr, 1, 60)) = 29 Then Exit For
Next yr
For yr = yr To yrEn Step 4
If ((yr Mod 100) <> 0) Or ((yr Mod 1000) = 0) Then ' bỏ qua năm '00, nhưng giữ lại năm '000
dt = DateSerial(yr, 2, 29)
cl = Weekday(dt, vbMonday)
r(cl) = r(cl) + 1
mang(r(cl), cl) = dt
End If
Next yr
wrPos.Resize(Application.Max(r), 7) = mang
End Sub

Sử dụng:
MangNamNhuan 2001, 2100, [a3]
hoặc
MangNamNhuan [c1], [f1], [a3]
 
Upvote 0
Bài tập 2
Đề bài rõ ràng hơn bài 1 :p :p :p
Thực hiện công việc sau: . . . . .
Rõ ràng hơn cái gì chứ?
Chỉ có bổ sung & phát triển lên thôi;

Rõ ý trong đề bài là:
(1) Người ta muốn có 1 bảng số liệu như mẫu;
(2) Yêu cầu viết 1 macro để có bảng số liệu đó;
./.
 
Upvote 0
Rõ ràng hơn cái gì chứ?
Chỉ có bổ sung & phát triển lên thôi;

Rõ ý trong đề bài là:
(1) Người ta muốn có 1 bảng số liệu như mẫu;
(2) Yêu cầu viết 1 macro để có bảng số liệu đó;
Đồng ý với anh là bài 2 mở rộng từ bài 1. Nhưng bài 1 thì rõ ràng là không rõ ràng:
- Không có câu nào nói là tìm năm nhuận
- Không có câu nào nói là tìm trong các năm từ đây đến đấy. Tới bài 5 mới nói đến sơ sơ
- Không giải thích "số liệu" nào phải bỏ vào cột nào
- người ta điền text cũng đúng,
- Nếu điền ngày thì phải điền ngày kiểu Mẽo mới đúng
 
Upvote 0
trên mạng có cái hình này cũng hay,
mà nhập tay lâu wá, có cách nào làm nhanh ko nhỉ ?? @@
1707270078706.png

1707270096802.png
 
Upvote 0
Upvote 0
Web KT
Back
Top Bottom