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

  • Thread starter Thread starter SA_DQ
  • Ngày gửi Ngày gửi
Liên hệ QC

Người dùng đang xem chủ đề này

SA_DQ

/(hông là gì!
Thành viên danh dự
Tham gia
8/6/06
Bài viết
14,689
Được thích
23,043
Nghề nghiệp
U80
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​
 
Có bắt buộc phải format m/d/yyyy hôn cha?
 
Upvote 0
Lên Copilot hỏi thì đước trả lời là: Những năm nhuận của thế kỷ 21...
(= những năm có tháng Hai 29 ngày )
Thử viết bằng VBA nhưng không ra kết quả như hình trên...
Có lẽ Bác nên đổi lại là VBA nâng cao...
Thử bằng Power Query... và thu được kết quả sau.

let
// Generate a list of years from 2000 to 2099
yearList = {2000..2099},

// Filter the list to include only leap years
leapYears = List.Select(yearList, each Date.IsLeapYear(_)),

// Create a list of dates with February 29th from leap years
feb29Dates = List.Transform(leapYears, each #date(_, 2, 29)),
#"Converted to Table" = Table.FromList(feb29Dates, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Changed Type" = Table.TransformColumnTypes(#"Converted to Table",{{"Column1", type date}})
in
#"Changed Type"
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Với kết quả y như bảng trên? Quá sức với người mới bắt đầu VBA.
(b2) Xin các bạn xài 2 vòng lặp; Vòng ngoài là 7 & vòng trong là từ 2000 step 1 cho đến 2099
(b1) Trước đó khai báo 1 biến mảng (1 → 99, 1 → 7)
(b3) Vòng lặp bên trong ta tìm tham biến Dat - là ngày theo hàm DateSerial() để biết thứ của ngày 29/2 của năm đang khảo sát;
(b4) Từ số liệu về thứ (trong tuần) của Dat ta gán Dat vô đúng cột mà nó phải có vô mảng (. . . )
Chuyên khó nhất là tăng chỉ số dòng đúng cho cột cần gán Dat

Có bắt buộc phải format m/d/yyyy hôn cha?
/(hông buộc bác ạ!; Nó sao để vậy cũng được nha, mấy anh chị em (muốn sử dụng thời giờ rỗi trong mấy ngày Tết NĐ)!

/(ết quả có thể là vầy cũng OK:

2/29/2004​
2/29/2032​
2/29/2060​
2/29/2088​
2/29/2016​
2/29/2044​
2/29/2072​
2/29/2000​
2/29/2028​
2/29/2056​
2/29/2084​
2/29/2012​
2/29/2040​
2/29/2068​
2/29/2096​
2/29/2024​
2/29/2052​
2/29/2080​
2/29/2008​
2/29/2036​
2/29/2064​
2/29/2092​
2/29/2020​
2/29/2048​
2/29/2076​
 
Upvote 0
(b2) Xin các bạn xài 2 vòng lặp; Vòng ngoài là 7 & vòng trong là từ 2000 step 1 cho đến 2099
(b1) Trước đó khai báo 1 biến mảng (1 → 99, 1 → 7)
(b3) Vòng lặp bên trong ta tìm tham biến Dat - là ngày theo hàm DateSerial() để biết thứ của ngày 29/2 của năm đang khảo sát;
(b4) Từ số liệu về thứ (trong tuần) của Dat ta gán Dat vô đúng cột mà nó phải có vô mảng (. . . )
Chuyên khó nhất là tăng chỉ số dòng đúng cho cột cần gán Dat


/(hông buộc bác ạ!; Nó sao để vậy cũng được nha, mấy anh chị em (muốn sử dụng thời giờ rỗi trong mấy ngày Tết NĐ)!

/(ết quả có thể là vầy cũng OK:

2/29/2004​
2/29/2032​
2/29/2060​
2/29/2088​
2/29/2016​
2/29/2044​
2/29/2072​
2/29/2000​
2/29/2028​
2/29/2056​
2/29/2084​
2/29/2012​
2/29/2040​
2/29/2068​
2/29/2096​
2/29/2024​
2/29/2052​
2/29/2080​
2/29/2008​
2/29/2036​
2/29/2064​
2/29/2092​
2/29/2020​
2/29/2048​
2/29/2076​
Bài này có lẽ 1 vòng lặp cũng được
2000 là năm nhuận, 4 năm 1 lần nhuận, step 4 chắc cũng vẫn được
 
Upvote 0
Thế kỷ 21 bắt đầu ngày 01/01/2001 và chấm dứt 31/12/2100
 
Upvote 0
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​
Bác Sa ơi không biết có đúng ý bác không nhưng cháu thấy kết quả đúng bác có thưởng gì cho học sinh mới ăn tết không ạ.
Mã:
Sub abc()
    Dim i As Long, arr, kq, a As Integer, b As Integer
    arr = Array(3, 1, 6, 4, 2, 7, 5)
    ReDim kq(1 To 4, 1 To 7)
    For i = 1 To 25
        a = (i - 1) Mod 7
        b = (i - 1) \ 7 + 1
        kq(b, arr(a)) = "29/2/" & (i * 4 + 1996)
    Next i
    Range("A1").Resize(4, 7).Value = kq
End Sub
 
Upvote 0
Với kết quả y như bảng trên? Quá sức với người mới bắt đầu VBA.
Lão ta nhầm lẫn giữa tập VBA và tập phân tích giải thuật.

Bài này giải thuật dễ nhất là phân tích chu kỳ mẫu (pattern recognition)
1. Liệt kê những năm nhuần trong thế kỷ 21, tức là từ 2001 đến 2099.
2. Sắp xếp lại theo thứ tự của đề bài.

Bắt đầu từ A1, ta lấy năm đầu tiên, tức 2004.
A2 = A1 + 28 năm
...
A5 = A4 + 28 năm = 2116 (>=2100); đem trừ 100 và chuyển qua B1
B1 = 2016
tiếp tục cho đến khi gặp lại 2004 thì dừng.

Đây là mảng 2 chiều. Muốn một hay hai vòng lặp chả thành vấn đề. Hai vòng lặp thì dùng số cột số dòng. Một vòng thì dùng con toán chuyển đổi chỉ số mảng 1 chiều sang 2 chiều.
 
Upvote 0
Giải thuật đơn giản mới khó chứ code vòng lặp là chuyện căn bản. Lập luận như sau:
1. Lặp bao nhiêu
- Từ 2001 đến 2003 không có năm nhuận. Năm nhuận đầu tiên là 2004
- Cách 4 năm mới có 1 năm nhuận (mới có 29/02)
Vậy thì vòng lặp i 25 lần, từ 2004 đến 2100 (step 4)

2. Kích thước bảng kết quả
Cấu trúc kết quả là bảng 7 cột, suy ra số dòng tối đa là 4. Vậy thì khai báo mảng 4 dòng 7 cột. Tuy vậy tôi cứ khai 10 dòng 7 cột, lý do thì xem mục 4 bên dưới.

3. Biện pháp tìm vị trí cột cho 29/02: Dùng DateSerial(i ,2, 29), weekday của nó là bao nhiêu thì bỏ vào cột bấy nhiêu. Suy luận 1 chút cho thứ 2 là đầu tuần nhưng cột 2. Nếu chủ nhật là đầu tuần thì khỏi phân vân. Mod 7 và thương số nguyên chia 7 chỉ là ăn may, trừ khi biết chắc điều gì đó của năm 2000. Tuy nhiên năm 2000 không phải của thế kỷ 21 nên không được dựa vào năm này.

4. Vị trí cột nào đã điền thì phải điền sang dòng kế, quy luật 100% cho việc tăng dòng tự động (*) là không biết chắc nên không thả nổi. Làm sao biết chắc khi chỉ dựa vào phép toán chia 7? (**)

(*) Tăng dòng tự động là tin rằng dòng nào cũng đầy đủ 7 ô, trước khi qua dòng kế. Tôi không chắc.
(**) bài 9 đưa ra 1 chu kỳ 28 năm, nếu đúng thì phải tính toán mới ra 28, và phải chắc chắn rằng đúng cho mọi thế kỷ, mọi thiên niên kỷ. Tôi không chắc.
Và bởi vì không chắc mà lại lười tính toán, nên tôi cho VBA tính giùm.
 
Lần chỉnh sửa cuối:
Upvote 0
Dùng vòng for với bước nhảy 28
Mã:
Sub XYZ()
  Dim res(1 To 4, 1 To 7), i&, k&, j&
  For i = 4 To 28 * 25 Step 28
    j = i \ 100 + 1
    k = (i Mod 100)
    res(k \ 28 + 1, j) = "29/2/" & k + 2000
  Next i
  Range("A1").Resize(4, 7).Value = res
End Sub
 
Upvote 0
Bài 1 & bài 5: các năm nhuận liên tiếp được xếp cùng dòng hoặc cùng cột. code dưới viết theo bài 1
Mã:
Option Explicit

Sub zzz()
Dim Kq()
Dim i, j, k

k = 2099 \ 4
ReDim Kq(1 To k, 1 To 7)

i = 1
For k = 2000 To 2099 Step 4
    j = Weekday(DateSerial(k, 2, 29))
    Kq((i - 1) \ 7 + 1, j) = DateSerial(k, 2, 29)
    i = i + 1
Next k
Sheet1.Range("A6").Resize((i - 1) \ 7 + 1, 7) = Kq
End Sub
 
Upvote 0
Bác Sa ơi không biết có đúng ý bác không nhưng cháu thấy kết quả đúng bác có thưởng gì cho học sinh mới ăn tết không ạ.
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.

1707116047531.png
Dùng vòng for với bước nhảy 28
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.

1707115883212.png

Bài 1 & bài 5: các năm nhuận liên tiếp được xếp cùng dòng hoặc cùng cột. code dưới viết theo bài 1
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.
 
Lần chỉnh sửa cuối:
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.
Theo như số liệu bài 1 & 5, tính từ 2000 đến 2099, cứ 7 lần nhuận liên tiếp liền nhau sẽ có thứ trong tuần khác nhau, quy luật này chạy code thấy có vẻ đúng.
Sang số liệu khác, có thể quy luật này không còn đúng nữa. Việc này liên quan tới trình bày các kết quả tính trong bảng, để bác @SA_DQ cho ý kiến rồi sẽ xử lý tiếp vậy
 
Upvote 0
Các bạn cứ làm theo yêu cầu của đề bài; Chuyện còn lại người ra đề gánh với đời!

Cảm ơn tất cả các bạn đã, đang & sẽ viết bài trong mục này

Chúc các bạn vui xuân khỏe mạnh & hạnh phúc!
 
Upvote 0
Mới bắt đầu là phải thế lày các bác ạ,
1 là phải dùng hàm có sẵn của excel vì có biết lặp thế lào đâu.
2 là gặp lỗi phải dùng thần chú resume next.

Thêm: nhờ bài này mới biết năm 2096 đến 2104 là 8 năm mới xuất hiện năm nhuận. Ai mà cứ 4 là sai liền. --=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 = 2000 To 2100 Step 4
            day = CDate("2/29/" & j)
            If Application.Weekday(day, 1) = Cot Then
                Hang = Hang + 1
                ArrNgay(Hang, Cot) = day
                HangMax = Application.Max(Hang, HangMax)
            End If
        Next
    Next
    Range("A1").Resize(HangMax, 7).Value = ArrNgay
End Sub
 
Upvote 0
Giải thuật nhảy 28 năm là giải thuật theo nhìn trực diện vào mảng.

Nếu nhìn vào bên trong dữ liệu thì phân tích sẽ gồm thứ trong tuần:

Code theo phân tích này thì rất giản dị:
Sub t()
Dim a(1 To 4, 1 To 7)
Dim r(1 To 7) As Long ' chỉ định hàng hiện tại của mỗi cột
For yr = 2000 To 2100 - 1 Step 4 ' 2100 thuộc về thế kỷ 22
dt = DateSerial(yr, 2, 29) ' tất cả các năm chia chẵn 4 trong thế kỷ này đều nhuần
cl = Weekday(dt) ' thứ trong tuần tức là chỉ số cột
r(cl) = r(cl) + 1 ' chỉ cần tìm thêm dòng là xong
a(r(cl), cl) = dt
Next yr
[a1].Resize(4, 7) = a
End Sub
 
Upvote 0
Upvote 0
For yr = 2000 To 2100 - 1 Step 4 ' 2100 thuộc về thế kỷ 22
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?
Vì tôi chỉ có thể nói 2100 không có trong đề bài ở bài 1.
Thực ra bài 1 không rõ ràng:
- bảng 7 cột không có tiêu đề và mạnh ai nấy suy diễn ra thứ trong tuần.
- Các con số trong bảng phải suy diễn ra là ngày tháng (kiểu Mỹ)
- Phải suy diễn 1 chút nữa rằng các năm của thế kỷ 20 khi năm lớn nhất nhìn thấy là 2096, năm nhỏ nhất là 2004, (suy diễn thế kỷ là của bài 4, không phải tôi)

Và bởi vì đề bài ra không có 2100 (là 1 mốc quan trọng vì năm không nhuận), nên mợt số các bài giải cho kết quả là text, vẫn đúng với hình ảnh nhìn thấy ở bài 1, và diễn giải lại ở bài 5
 
Upvote 0
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
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
...
' bỏ qua năm '00, nhưng giữ lại năm '000
1. Bài 26 là 4 ví dụ minh họa chứ không phải test 4 lần. Các ví dụ cho thấy các tình huống yêu cầu:
- chọn năm bắt đầu và kết thúc bất kỳ
- Khoảng thời gian bất kỳ (nếu thêm 1 ví dụ từ năm sinh đến hiện tại ra 64 năm thì tốt)
- Vượt sang thế kỷ sau và vượt cả thiên niên kỷ

Năm x000 có phải nhuận hay không là người giải phải tự làm, chứ tôi không cho ví dụ, và tôi không cần test riêng trường hợp đó vì hàm VBA sẽ loại bỏ hoặc tiếp nhận mà không cần quan tâm.

2. Theo tôi nhớ thì không phải tròn ngàn sẽ nhuận, mà là tròn 400 năm sẽ nhuận: Năm chia hết cho 400 sẽ nhuận, năm 3000, 5000, 7000 là tròn ngàn nhưng không chia hết cho 400 nên không nhuận. Năm 4000, 6000, 8000 chia hết cho 400 nên nhuận
Có test đàng hoàng bằng VBA và cả hàm Date của worksheet. Ô tô đỏ là tròn ngàn nhưng không nhuận, Ô tô màu xanh là ô chia hết cho 400
Ô C2 chữ đỏ vì VBA đúng, hàm Date sai cho năm 1900

1707272029446.png
 
Lần chỉnh sửa cuối:
Upvote 0
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)

View attachment 298963

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


View attachment 298967

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

View attachment 298965

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

View attachment 298966
Thử code
Mã:
Sub XYZ()
  Dim res(), a&(1 To 7), fYear&, lYear&, i&, j&, t
 
  fYear = Range("C1").Value 'Nam dau
  lYear = Range("F1").Value 'Nam cuoi
  ReDim res(1 To (lYear - fYear + 1) \ 7 + 5, 1 To 7)
 
  For i = fYear To lYear Step 4
    t = i & "/2/29"
    If IsDate(t) Then
      j = Weekday(t, 2)
      a(j) = a(j) + 1
      res(a(j), j) = t
    Else
      i = i - 3
    End If
  Next i
  Range("A3").Resize(UBound(res), 7).Value = res
End Sub
 
Upvote 0
Thử code
Mã:
  ReDim res(1 To (lYear - fYear + 1) \ 7 + 5, 1 To 7)
End Sub
Redim số dòng hơi dư, chia 28 thì hợp lý hơn. Cộng 5 là phòng xa thì tốt rồi.
Dùng biến mảng 1 chiều 7 phần tử để lưu trữ số phần tử của từng cột là biện pháp hay nhất, tôi cũng dùng như vậy.
 
Upvote 0
2. Theo tôi nhớ thì không phải tròn ngàn sẽ nhuận, mà là tròn 400 năm sẽ nhuận: Năm chia hết cho 400 sẽ nhuận, năm 3000, 5000, 7000 là tròn ngàn nhưng không chia hết cho 400 nên không nhuận. Năm 4000, 6000, 8000 chia hết cho 400 nên nhuận
...
Xin lỗi, bi giờ tôi nhớ lại rồi. Đúng con toán ngoại lệ là 100 nhưng không phải 400. Bài toán tính năm nhuận là:
And(Mod(nam, 4)=0, Or(Mod(nam, 400)=0, Mod(nam, 100) <> 0))

Code trên phải sửa
If ((yr Mod 100) <> 0) Or ((yr Mod 1000) = 0) Then ' bỏ qua năm '00, nhưng giữ lại năm '000
sửa lại
If ((yr Mod 400)=0) Or ((yr Mod 100)<>0) Then ' bỏ 100, nhưng giữ lại 400

Chú:
Quote từ The Science of Leap Year, Bob Craddock, 27/02/2020
1707286324142.png
 
Upvote 0
Đáp án cho câu 2, không phải là tối ưu nhưng đơn giản:
- 1 vòng lặp
- Dùng DateSerial để khỏi bị lỗi, và kết quả luôn luôn là ngày tháng, kể cả DateSerial(2450, 13, 32) (Ngày 32 tháng 13)
- KIểm tra năm không phải năm nhuận bằng hàm Day <> 29 (hàm Month <>2 cũng được)
- Dùng hàm Ceiling để lấy năm chia hết cho 4 đầu tiên, không cần lấy năm nhuận cuối cùng
- Dùng mảng tạm để lưu trữ thứ tự dòng từng cột, mỗi cột thì số lượng dòng tăng riêng)
- Khai báo mảng kết quả với số dòng dư cho 1000 năm, nhưng dùng biến MaxRw để chỉ đưa xuống sheet số dòng có dữ liệu. Tính trước như bài 43 cũng hay nhưng vẫn phải cộng 1 số dòng dư phòng xa.

Không cần ghi chú người học căn bản cũng hiểu.

PHP:
Sub Nhuan()
    Dim ColArr(1 To 7), ResultArr(1 To 45, 1 To 7), i As Long, j As Long
    Dim dayN As Date, MaxRw As Long
    Sheet2.Range("A3:G50").ClearContents
    For i = Application.Ceiling(Sheet2.[C1], 4) To Sheet2.[F1] Step 4
        dayN = DateSerial(i, 2, 29)
        If day(dayN) = 29 Then '' Loai bo nam khong nhuan
            j = Weekday(dayN, vbMonday)
            ColArr(j) = ColArr(j) + 1
            If ColArr(j) > MaxRw Then MaxRw = ColArr(j)
            ResultArr(ColArr(j), j) = dayN
        End If
    Next
    Sheet2.[A3].Resize(MaxRw, 7).Value = ResultArr
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu không nhằm, đó là tam giác Pascal;
2ui tắc là Số ở hàng dưới bằng với tổng hai số ngay trên nó.
Tam giác Pascal là một trong những bài căn bản học lập trình. Học lập trình thì phải biết toán cao cấp. Điển hình là Đại số, Giải tích, và Số học.
Tuy nhiên, VBA là lập trình ứng dụng - không chỉ lập trình mà còn sử dụng các đối tượng/object của VBA và bảng tính. Vì vậy học VBA không cần phải học toán cao cấp. Chỉ cần hiểu rõ thế nào là đối tượng, thuộc tính và phương thức.

Trong bài trên, lập tam giác Pascal không khó. Cái khó ở đây là xếp lại cho chúng nằm thành hình tháp tức tam giác cân (lúc tính thì chúng xếp thành tam giác vuông). Thêm bài trên còn chảnh choẹ, merge cells tùm lum.

Hiện tại, cứ coi như là bài tập 3
Giải thuật:, lưu ý rằng tam giác Pascal là tam giác cân về trị. Tức các trị sẽ phản chiếu qua đường trung tuyến.
(giải thuật này không làm chuyện merge cells. Tuy nhiên, để dễ đọc, kết quả sẽ ghi 2 cells một trị giống như đề bài)
1. Lập mảng có số dòng là chiều cao tam giác, số cột là chiều cao * 2 - 1
2. Vòng lặp dòng từ 1 đến chiều cao
2.1. Xác định cột ghi đầu tiên là chiều cao - dòng + 1
2.2. Vòng lặp cột từ cột đầu tiên đến chiều cao step 2 (ghi 1 ô bỏ 1 ô)
2.2.1. Nếu cột đầu tiên thì trị là 1
2.2.2. Các cột khác, dùng con toán Pascal (tổng của 2 số trên nó)
2.2.3. Dùng phép phản chiếu để ghi các cột bên phải cột chiều cao (= tổng số cột - cột + 1)
3. Ghi mảng xuống sheet
4. Hết

Mã:
Sub PascalTriangle(Optional ByVal nHeight As Long = 1)
' code vẽ tam giác Pascal với chiều cao nHeight
' để cho tam giác đều, đẹp, dùng phép ghi một ô bỏ một ô
numCols = 2 * nHeight - 1 ' tổng số cột
ReDim a(1 To nHeight, 1 To numCols)
For rw = 1 To nHeight
  firstCol = nHeight - rw + 1 ' cột đầu tiên để ghi
  For col = firstCol To nHeight Step 2 ' ghi một ô, bỏ một ô
    If col = firstCol Then
      a(rw, col) = 1
    Else
      a(rw, col) = a(rw - 1, col - 1) + a(rw - 1, col + 1)
    End If
    a(rw, numCols - col + 1) = a(rw, col) ' ô phản chiếu
  Next col
Next rw
[a1].Resize(UBound(a, 1), UBound(a, 2)) = a
End Sub
 
Upvote 0
Đầu năm thử ra thêm BÀI TẬP 4
Đề bài: cho tháng và năm, tìm số ngày có trong tháng ấy.
Ví dụ tháng 4 có 30 ngày. Đáng lẽ chỉ tháng là đủ, nhưng kẹt tháng 2 cần dữ liệu năm.

1. Sơ cấp:
Dùng bất cứ cái gì để tính.
Ghi vào ô A1 (nếu biết thêm một vài mẹo để ghi thì cứ việc dùng)

2. Trung cấp:
2.1. Không được dùng hàm VBA hay hàm bảng tính, tức là chỉ dùng con toán thuần túy học từ lớp 5, 6 gì đó.
2.2. Không dùng lệnh IF
(có một vài cách, cách hiển nhiên nhất là dùng Select Case, nhưng cũng có lệnh rẽ nhánh điều kiện khác như On ??? Goto ???)

3. Cao cấp:
Bất cứ cách đặc biệt nào bạn nghĩ ra.
Ví dụ viết một function với JavaScript rồi gọi function ấy.
 
Upvote 0
PHP:
Function Ngay(ByVal Thg As Integer, ByVal Nam As Integer) As Integer
 Ngay = Switch(Thg = 1, 31, Thg = 3, 31, Thg = 5, 31, Thg = 7, 31, Thg = 8, 31, Thg = 10, 31, _
    Thg = 12, 31, Thg = 4, 30, Thg = 6, 30, Thg = 9, 30, Thg = 11, 30, Thg = 2, IIf(Nam / 4 = Nam \ 4, 29, 28))
End Function
:D :D :D
 
Upvote 0
Mã:
Function Ngay1(ByVal Thg As Integer, ByVal Nam As Integer) As Integer
 
      Dim Th(12) As Integer
      Dim Th2(4) As Integer

      Th(1) = 31
      Th(2) = 0
      Th(3) = 31
      Th(4) = 30
      Th(5) = 31
      Th(6) = 30
      Th(7) = 31
      Th(8) = 31
      Th(9) = 30
      Th(10) = 31
      Th(11) = 30
      Th(12) = 31
 
      Th2(0) = 29
      Th2(1) = 28
      Th2(2) = 28
      Th2(3) = 28
 
      Th(2) = Th2(((Nam / 4) - (Nam \ 4)) * 4)
 
      Ngay1 = Th(Thg)
 
End Function

copy công thức từ #50
kế thừa và phát huy ^^

:D :D :D:D :D :D

phân tích toán số từ #50
thị trường ngách ^^
1707709461948.png
 
Lần chỉnh sửa cuối:
Upvote 0
Không dùng hàm VBA, không dùng If và case .
t: Tháng
n: Năm
Mã:
Function ABC(t&, n&)
  ABC = 31 + (t = 4) + (t = 6) + (t = 9) + (t = 11)
  ABC = ABC + (t = 2) * (3 + (n = (n \ 4) * 4) + (n = (n \ 100) * 100) * (n <> (n \ 400) * 400))
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Tôi nghĩ là bài #50, #51, và #52 đều nhắm vào "Trung cấp".
Vì vậy tôi phê theo trình độ này, tức là cách thức code:

Bài #50:
Swicth là hàm để thay thế IF-THEN-ELSE và SELECT-CASE khi bạn cần viết code ngắn.
Càng nhiều tham số thì Switch càng mất hiệu quả. Bởi vì nó phải tính từng tham số.
Như bài trên dẫu tháng có phải là tháng hai hay không thì con toán tháng 2 vẫn phải tính.
Lệnh IF và Select nếu viết khéo thì gặp tháng <> 2 không phải làm tính.

Bài #51:
Tôi biết là bạn diễu, nhưng code như bài của bạn là loại code nguy hiểm.
Bạn tự mặc định UBound chỗ Dim mảng của bạn là 0. Điều này không luôn luôn đúng.
Gặp hệ thống định sẵn UBound là 1 thì code toi.
Muốn xài cocde này, đầu module phải có dòng "Option Base 0"

Bài #52:
Code này khai báo tham đàng hoàng nhưng để cho hàm mặc định là variant vô ích, chỉ kém hiệu quả thôi.
Function ABC(t&, n&)
Nên sửa lại là:
Function ABC&(t&, n&)
Và cũng giống như bài #50, không dùng hàm, cấu trúc dữ liệu của VBA, và rất ngắn gọn nhưng bị kém chỗ "tất cả các con toán đều phải tính".

Code không dùng IF, không Select, điển hình:
Function ToTiTe(byVal thang As Long, Optional Byval nam As Long = 0) As Long
On thang GoTo Thang1, Thang2, Thang3, Thang4, Thang5, Thang6, Thang7, Thang8, Thang9, Thang10, Thang11, Thang12
Exit Function
Thang1:
Thang3:
Thang5:
Thang7:
Thang8:
Thang10:
Thang12:
ToTiTe = 31
Exit Function
Thang4:
Thang6:
Thang9:
Thang11:
ToTiTe = 30
Exit Function
Thang2:
' code tính ngày ở đây
Exit Function
End Function

Code dài thòn, nhưng lệnh rẽ nhánh đến labels rất hiệu quả.
Nếu đặt ThangLon, ThangNho, Thang2 thì sẽ ngắn hơn, nhưng cái dòng On ... dễ nhầm lẫn hơn.
 
Upvote 0
Bài #51:
Tôi biết là bạn diễu, nhưng code như bài của bạn là loại code nguy hiểm.
Bạn tự mặc định UBound chỗ Dim mảng của bạn là 0. Điều này không luôn luôn đúng.
Theo tôi thấy thì code bạn này không màng đến Base 0 hay Base 1
Khai báo: Dim Th(12) As Integer
Nếu base 0 thì có 13 phần tử, nếu base 1 thì có 12 phần tử.
Dù vậy bạn ấy chỉ xài 12 phần tử từ 1 đến 12, phần tử 0 nếu có bị bỏ qua và không ảnh hưởng.
 
Upvote 0
Theo tôi thấy thì code bạn này không màng đến Base 0 hay Base 1
Khai báo: Dim Th(12) As Integer
Nếu base 0 thì có 13 phần tử, nếu base 1 thì có 12 phần tử.
Dù vậy bạn ấy chỉ xài 12 phần tử từ 1 đến 12, phần tử 0 nếu có bị bỏ qua và không ảnh hưởng.
Base 1 thì code chết tươi, bác ạ.
Ở nài #53, tôi nói là "Gặp hệ thống định sẵn UBound là 1 thì code toi."
Ở trình độ cdoe bậc trung thì tôi coi như đã biết qua dộ nguy hiểm của "mặc định", và biết tránh những tình huống ấy.

Mã:
      Th2(0) = 29 ' dòng này nè!
      Th2(1) = 28
      Th2(2) = 28
      Th2(3) = 28
 
      Th(2) = Th2(((Nam / 4) - (Nam \ 4)) * 4)
 
      Ngay1 = Th(Thg)
 
End Function
 
Upvote 0
Code bậc sơ đẳng:

Function ToTiTe(byVal thang As Long, Optional ByVal nam As Long = 1) As Long
ToTiTe = Day(DateSerial(nam, thang+1, 0))
End Function

Ghi thẳng vào ô, dùng hàm Evaluate và hàm bảng tính
 
Upvote 0
Bài #51:
Tôi biết là bạn diễu, nhưng code như bài của bạn là loại code nguy hiểm.
Bạn tự mặc định UBound chỗ Dim mảng của bạn là 0. Điều này không luôn luôn đúng.
Gặp hệ thống định sẵn UBound là 1 thì code toi.
Muốn xài cocde này, đầu module phải có dòng "Option Base 0"
lần đầu tiên e biết vụ Base 0 này luôn á ( e xin phép xưng e cho trẻ ^^ )
từ hồi sinh viên e đã gán thử giá trị cho phần tử thứ 0 ( bắt chước cách gán bên c dos ) , thấy không báo lỗi là xài cho hơn chục năm đi làm luôn, tính đến hiện tại chưa phải đền tiền vụ nào nên cứ thế xài thôi ^^


Theo tôi thấy thì code bạn này không màng đến Base 0 hay Base 1
Khai báo: Dim Th(12) As Integer
Nếu base 0 thì có 13 phần tử, nếu base 1 thì có 12 phần tử.
Dù vậy bạn ấy chỉ xài 12 phần tử từ 1 đến 12, phần tử 0 nếu có bị bỏ qua và không ảnh hưởng.

nói chung e mặc định trước giờ khai báo mảng trong vba sẽ được thêm 1 biến,
ví dụ : abc(12) -> sẽ được 13 biến tính từ 0 ^^

lúc đầu tính để như vầy, nhưng muốn tỏ ra nguy hiểm tí nên gán từ 0 á ^^
Th2(1) = 29
Th2(2) = 28
Th2(3) = 28
Th2(4) = 28

Th(2) = Th2(((Nam / 4) - (Nam \ 4)) * 4 + 1)

con toán \ lần đầu e mới biết luôn, trước giờ cứ phải viết như vầy Int(Nam / 4) @@
 
Lần chỉnh sửa cuối:
Upvote 0
PHP:
Function DaysOfMonth(ByVal Thg As Integer, ByVal Nam As Integer) As Integer
 DaysOfMonth = Choose(Thg, 31, IIf(Nam Mod 4 = 0, 29, 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
End Function
 
Upvote 0
chỉ là thêm điều kiện thôi 400, 100 thôi mà !


Mã:
Function Ngay1(ByVal Thg As Integer, ByVal Nam As Integer) As Integer
 
      Dim i As Integer
      Dim Th(12) As Integer
      Dim Th2(4) As Integer
      Dim Nam100(100) As Integer
      Dim Nam400(400) As Integer

      Th(1) = 31
      Th(2) = 0
      Th(3) = 31
      Th(4) = 30
      Th(5) = 31
      Th(6) = 30
      Th(7) = 31
      Th(8) = 31
      Th(9) = 30
      Th(10) = 31
      Th(11) = 30
      Th(12) = 31
 
      Th2(1) = 29
      Th2(2) = 28
      Th2(3) = 28
      Th2(4) = 28
 
      Th(2) = Th2(((Nam / 4) - (Nam \ 4)) * 4 + 1)
 
      '-------------100
      For i = 1 To 100
          Nam100(i) = 0
      Next i
      Nam100(1) = 1
 
      Th(2) = Th(2) - Nam100(((Nam / 100) - (Nam \ 100)) * 100 + 1)
 
 
      '-------------400
      For i = 1 To 400
          Nam400(i) = 0
      Next i
      Nam400(1) = 1
 
      Th(2) = Th(2) + Nam400(((Nam / 400) - (Nam \ 400)) * 400 + 1)
 
      '---------------
 
      Ngay1 = Th(Thg)
 
 
 
End Function


e xin phép chỉnh code bác 1 xíu !
PHP:
Function DaysOfMonth(ByVal Thg As Integer, ByVal Nam As Integer) As Integer
  Dim m2 As Integer
  m2 = IIf(Nam Mod 4 = 0, 29, 28)
  m2 = m2 - IIf(Nam Mod 100 = 0, 1, 0)
  m2 = m2 + IIf(Nam Mod 400 = 0, 1, 0)
  
  DaysOfMonth = Choose(Thg, 31, m2, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Trình độ chẳng ra gì: (tôi muốn gọi "cao cấp" lắm nhưng tránh người ta phê mình tự phụ, thành ra loãng code)

Bài này là bài tập kinh điển trong lập trình C.
int SoNgay(int thang, int nam)
{
switch (thang)
{
case 2:
return ((thang%400=0||thang%4=0&&thang%100!=0)? 28 : 29);
case 4: case 6: case 9: case 11: return 30;
default: return 31;
}
}
C có đặc điểm là có thể đi tắt, không cần tính các điều kiện khác nếu nó cho rằng đến đó đủ xác định rồi. Vì vậy code trên rất hiệu quả, đi thẳng đến các label và drop trhu nếu cần.
Tháng 2: năm chia chẵn cho 400 là nó biết rồi, không tính chỗ còn lại.
Nếu không chia chẵn 400 thì do Or (||) nó buộc tính tiếp, nếu chia chẵn 4 thì do && nó lại phải tính tiếp con toán chia chẵn 100. Vì số không chia chẵn 4 có tỷ lệ áp đảo 3/1 nên tôi cho nó tính trước.

Code JavaScript cũng gần giống C
function SoNgay {
switch (thang) {
case 2:
return ((thang%400=0||thang%4=0&&thang%100!=0)? 28 : 29);
case 4: case 6: case 9: case 11: return 30;
default: return 31;
}
}
Bi giờ chỉ cần viết code VBA gọi hàm này qua MSScriptControl.ScriptControl
 
Lần chỉnh sửa cuối:
Upvote 0
Base 1 thì code chết tươi, bác ạ.
Tôi sai ở khai báo thứ hai (vì sao khai báo và sử dụng 2 cách thì chẳng biết). Còn khai báo thứ nhất của 12 tháng thì sẽ không lỗi thật.
Ngoài ra, 0 và 1 là LBound chứ không phải UBound
 
Upvote 0
Hình như đề bài cấm dùng If, cho dùng Case. Code sơ cấp:
Mã:
Select Case Th
    Case 1, 3, 5, 7, 8, 10, 12
        Ngay = 31
    Case 4, 6, 9, 11
        Ngay = 30
    Case 2
        'Tính cho tháng 2
    Case Else
         Ngay = "zô ziên"
End Select
 
Upvote 0
Hình như đề bài cấm dùng If, cho dùng Case. Code sơ cấp:
Mã:
Select Case Th
    Case 1, 3, 5, 7, 8, 10, 12
        Ngay = 31
    Case 4, 6, 9, 11
        Ngay = 30
    Case 2
        'Tính cho tháng 2
    Case Else
         Ngay = "zô ziên"
End Select
Bậc này là trung cấp, bác ơi. Bác sẽ ngạc nhiên khi tôi nói với bác rằng trên GPE này còn nhiều người chưa nắm vững sự khác biệt giữa Select và IF (*)
Bài này bậc sơ cấp là dùng hàm VBA, điển hình là code bài #56
Ở bậc trung cấp, tôi cố tình ra điều kiện "không IF" là để test khả năng sử dụng các lệnh rẽ nhánh có điều kiện.

Chú thích (*): Select thay thế IF là một kỹ thuật code bị VBA xem thường vì nó không thuần túy là lệnh "rẽ đến labels" như các ngôn ngữ hiện đại khác.
Một trong những lợi ích khác mà sách vở tiếng Việt không đề cập tới là tuy IF hiệu quả hơn, nhưng khi gặp đúng chỗ, Select có thể chỉ phải tính có số đầu 1 lần trong khi IF phải tính nhiều.
IF (a+b) = 1 OR (a+b) = 10 Then ' ' (a+1) tính 2 lần, so sánh 2 lần
Select (a+b) ' (a+1) tính 1 lần
Case 1, 10 ' tuy vẫn so sánh 2 lần

Chú thích 2: lệnh rẽ đến labels là lệnh rất hiệu quả (lênh switch trong JavaScript). Bài #61 tuy ngoại đạo (dùng ngôn ngữ khác) nhưng tôi đưa vào để ví dụ cho thấy sự hiệu quả của nó.
Bàu #53 trông rườm rà nhưng đó là cách diễn giải gần sát nhất theo VBA.
 
Upvote 0
Bậc này là trung cấp, bác ơi.
Tôi chỉ biết rằng Select Case đọc code ở #64 hiểu ngay lập tức không cần ghi chú. Case Else là nâng cao 1 chút khi dự phòng người dùng dùng sai loại dữ liệu cho hàm, chứ không phải Case Else cho tháng 2 (Case 2 mà đổi thành Case Else nghĩa là mặc định người dùng thông minh không điền 13 trở lên hoặc text)
Lẽ ra nên có 1 điều kiên "nam" > = 1900 nhưng lại cơ bản quá.
 
Upvote 0
Tôi chỉ biết rằng Select Case đọc code ở #64 hiểu ngay lập tức không cần ghi chú. ...
IF-Then-ElseIf cũng là loại dễ hiểu.
Chỗ tôi nhấn mạnh ở vài #65 là "khi nào thì "Select case" hơn hẳn If:
- Khi mà trị Select chỉ cần tính 1 lần, và nếu dùng If-Then-ElseIf thì phải tính nhiều lần.
Khi nào thì IF hơn hẳn:
- If là lệnh căn bản của chính mã máy cho nên nhanh hơn các kiểu rẽ nhánh có điều kiện khác. Vì vậy, lúc không chắc thì If là tốt nhất.

Lẽ ra nên có 1 điều kiên "nam" > = 1900 nhưng lại cơ bản quá.
Chính IBM đã bỏ năm 1900 khi hợp đồng cho Micro-Soft viết mã DOS cho PC thì tội gì ta phải tính?

Chú: ngày xưa, MicroSoft là hai từ nối, và tiêu chuẩn ngày xưa thì hai từ nối phải có gạch ở giữa. Sau này có sự đồng ý không thành văn của các tập đoàn là không dùng "-" nữa.
 
Upvote 0
Chính IBM đã bỏ năm 1900 khi hợp đồng cho Micro-Soft viết mã DOS cho PC thì tội gì ta phải tính?
Ý tôi là bắt lỗi người dùng, giả sử người dùng hàm viết công thức =Ngay(2, 520) thì trả kết quả là "Em hỏng biết" chẳng hạn. Hoặc điền tham số <nam> bằng 1 text
 
Upvote 0
Đầu Xuân năm mới thì: em xin chúc các bác ngon lành cành đào y năm cũ!!!

_)(#; _)(#; _)(#; _)(#; _)(#; _)(#; _)(#; _)(#; _)(#;


Còn các code trên thì: """"""""THẬT LÀ BÁ ĐẠO""""""""

_)(#; _)(#; _)(#;_)(#;
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom