Bài tập VBA về cơ bản ByVal, ByRef; Bài tập hàm con (1 người xem)

Liên hệ QC

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

Hoàng Nhật Phương

Thành viên gắn bó
Tham gia
5/11/15
Bài viết
1,895
Được thích
1,219
Tôi không xem sub LocKetQua và SumTongCong đâu nhé. Nhưng trong LocKetQua ít nhất phải có ByVal sFile As String
Con chào Bác Siwtom,
Đầu năm con kính chúc Bác cùng gia đình Vạn sự như ý, An khang thịnh vượng ạ.
Ở bài này con vẫn còn mơ hồ một chút nữa giưã ByVal/ByRef , Bác chỉ chon hiểu thêm với ạ:
Nếu code:
Mã:
    ...
           Dim sKey As String
...
            For ik = 0 To dFile.Count - 1
                sKey = dFile.Keys()(ik)
                Call LocKetQua(sKey, Book, shtM12, lr, shtMau)
            Next ik
    ...
Sub LocKetQua(sFile As String,...)
Như vậy sFile không có ByVal ở đầu (theo con hiểu mặc định sẽ là ByRef nếu không khai báo ByVal/ByRef ở đầu) code không báo lỗi.
Nhưng khi chuyển như sau:
Mã:
    ...
 Dim sKey 
...
            For Each sKey In dFile.Keys
                Call LocKetQua(sKey, Book, shtM12, lr, shtMau)
            Next sKey
    ...
Code báo lỗi: ByRef argument type mismatch
Sub LocKetQua(ByVal sFile As String,...) sFile phải khai báo ByVal ở đầu như Bác chỉ ở trên.

Phải chăng là khi sử dụng sKey không còn là kiểu String ( giống với sFile As String) nữa mà do thay sang kiểu Variant, nếu đúng như vậy: ByVal sFile As String sẽ vẫn giữ được giá trị truyền vào nhưng khác với ByRef là nó sẽ đổi được kiểu khai báo của tham số truyền vào từ Variant sang String phải không ạ?
 
Lần chỉnh sửa cuối:
Con chào Bác Siwtom,
Đầu năm con kính chúc Bác cùng gia đình Vạn sự như ý, An khang thịnh vượng ạ.
Ở bài này con vẫn còn mơ hồ một chút nữa giưã ByVal/ByRef , Bác chỉ chon hiểu thêm với ạ:
Nếu code:
Mã:
    ...
           Dim sKey As String
...
            For ik = 0 To dFile.Count - 1
                sKey = dFile.Keys()(ik)
                Call LocKetQua(sKey, Book, shtM12, lr, shtMau)
            Next ik
    ...
Sub LocKetQua(sFile As String,...)
Như vậy sFile không có ByVal ở đầu (theo con hiểu mặc định sẽ là ByRef nếu không khai báo ByVal/ByRef ở đầu) code không báo lỗi.
Nhưng khi chuyển như sau:
Mã:
    ...
Dim sKey
...
            For Each sKey In dFile.Keys
                Call LocKetQua(sKey, Book, shtM12, lr, shtMau)
            Next sKey
    ...
Code báo lỗi: ByRef argument type mismatch
Sub LocKetQua(ByVal sFile As String,...) sFile phải khai báo ByVal ở đầu như Bác chỉ ở trên.

Phải chăng là khi sử dụng sKey không còn là kiểu String ( giống với sFile As String) nữa mà do thay sang kiểu Variant, nếu đúng như vậy: ByVal sFile As String sẽ vẫn giữ được giá trị truyền vào nhưng khác với ByRef là nó sẽ đổi được kiểu khai báo của tham số truyền vào từ Variant sang String phải không ạ?
Lỗi được thông báo rõ ràng mà. Nếu là Sub LocKetQua(sFile As String ... thì trong code gọi sub LocKetQua biến sFile phải được khai báo với kiểu y hệt như trong Sub LocKetQua - kiểu String. Tức phải có
Mã:
Dim sKey As String
...
Call LocKetQua(sKey, Book, shtM12, lr, shtMau)

Không thể là Dim sKey ' As Variant được.

Nếu vẫn có Sub LocKetQua(sFile As String .. mà bắt buộc phải có Dim sKey (Variant) do dùng For Each: For Each sKey (sKey phải là Variant hoặc Object) thì phải gọi như sau
Mã:
Dim sKey
...
Call LocKetQua(CStr(sKey), Book, shtM12, lr, shtMau)

Nếu là Sub LocKetQua(ByVal sFile As String ... thì sau đó sKey thôi chứ không phải xoay xở CStr(sKey)
 
Lần chỉnh sửa cuối:
Upvote 0
Lỗi được thông báo rõ ràng mà. Nếu là Sub LocKetQua(sFile As String ... thì trong code gọi sub LocKetQua biến sFile phải được khai báo với kiểu y hệt như trong Sub LocKetQua - kiểu String. Tức phải có


Không thể là Dim sKey ' As Variant được.
Dạ cảm ơn Bác nhiều ạ, như vậy con hiểu rồi cả ByVal/ByRef đều là truyền nhưng:
ByRef : Truyền tham chiếu (bao gồm cả kiểu của dữ liệu & giá trị)
ByVal : Chỉ truyền giá trị, không truyền kiểu dữ liệu (giá trị)
 
Upvote 0
Dạ cảm ơn Bác nhiều ạ, như vậy con hiểu rồi cả ByVal/ByRef đều là truyền nhưng:
ByRef : Truyền tham chiếu (bao gồm cả kiểu của dữ liệu & giá trị)
ByVal : Chỉ truyền giá trị, không truyền kiểu dữ liệu (giá trị)
Không hẳn là sai về ý nghĩa Trừ cái phần tôi gạch ngang.
Nhưng từ ý nghĩa đến mục đích còn xa lắm.
Tìm hiểu mục đích tại sao có chuyện bài-rép, bài-vao ?

Chú:
ByVal: hàm lập một biến nội và gán trị của tham cho biến ấy. Sau khi thoát hàm, biến nội này sẽ tự diệt.
ByRef: hàm sử dụng tham như biến ngoại.
 
Upvote 0
Không hẳn là sai về ý nghĩa Trừ cái phần tôi gạch ngang.
Nhưng từ ý nghĩa đến mục đích còn xa lắm.
Tìm hiểu mục đích tại sao có chuyện bài-rép, bài-vao ?
Con chào Bác @VetMini
Cảm ơn Bác đã quan tâm ạ, với ByVal có thể mục đích dạng như là ép kiểu để tăng tốc code đó phải không Bác?
Ví dụ khi kiểu tham số x đang là Variant tại sub mẹ và muốn truyền đến một sub con nào đó,sub con này phải khai báo ByVal chuyển từ Variant thành Long hoặc String gì đó để tăng tốc...
Còn mục đích của ByRef thì con chưa biết vì mặc định nếu không khai báo ở đầu là kiểu truyền thì con hiểu đó ByRef thôi ạ có thể khai báo cũng là tăng thêm tốc độ hơn chăng...
Bác chỉ dẫn thêm cho con với ạ, nếu có ví dụ minh họa thì con sẽ dễ hiểu hơn ạ.
Nhân dịp năm mới con kính chúc Bác năm mới sức khỏe tốt,công tác tốt ạ.
 
Upvote 0
Con chào Bác @VetMini
Cảm ơn Bác đã quan tâm ạ, với ByVal có thể mục đích dạng như là ép kiểu để tăng tốc code đó phải không Bác?
Hướng dẫn của bác @batman1 về kiểu biến không phải là mục đích của byVal, càng không phải để tăng tốc, mà vì bị xung đột kiểu biến khi truyền vào. Tương tự như nhét con heo vào nồi luộc gà không vừa, thì phải chặt bớt 2 chân hoặc 4 chân để vừa nồi.
Còn ý nghĩa byVal hay byRef thì đọc cái chú ở bài 72. Toàn là đọc không kỹ rồi đoán mò
 
Upvote 0
Chạy thử code abc để phân biệt byVal và byRef về giá trị. Còn về kiểu biến thì chế code tự kiểm tra
PHP:
Sub abc()
Dim TestVal
TestVal = 5
MsgBox "TestVal = " & TestVal & Chr(10) & "(Gia tri goc)"
TestValue TestVal
MsgBox "TestVal = " & TestVal & Chr(10) & "Nay thi byVal tu diet!"
TestValue2 TestVal
MsgBox "TestVal = " & TestVal & Chr(10) & "Nay thi byRef ... !"
End Sub
    '____________________'
Sub TestValue(ByVal Num)
Num = Num * 2
MsgBox "Num = " & Num
End Sub
    '__________________'
Sub TestValue2(ByRef Num)
Num = Num * 3
MsgBox "Num = " & Num
End Sub
 
Upvote 0
Đọc kỹ cái phần "chú" ở bài #72. Và hỏi lại chỗ chưa hiểu.
Hướng dẫn của bác @batman1 về kiểu biến không phải là mục đích của byVal, càng không phải để tăng tốc, mà vì bị xung đột kiểu biến khi truyền vào. Tương tự như nhét con heo vào nồi luộc gà không vừa, thì phải chặt bớt 2 chân hoặc 4 chân để vừa nồi.
Còn ý nghĩa byVal hay byRef thì đọc cái chú ở bài 72. Toàn là đọc không kỹ rồi đoán mò
Chạy thử code abc để phân biệt byVal và byRef về giá trị. Còn về kiểu biến thì chế code tự kiểm tra
PHP:
Sub abc()
Dim TestVal
TestVal = 5
MsgBox "TestVal = " & TestVal & Chr(10) & "(Gia tri goc)"
TestValue TestVal
MsgBox "TestVal = " & TestVal & Chr(10) & "Nay thi byVal tu diet!"
TestValue2 TestVal
MsgBox "TestVal = " & TestVal & Chr(10) & "Nay thi byRef ... !"
End Sub
    '____________________'
Sub TestValue(ByVal Num)
Num = Num * 2
MsgBox "Num = " & Num
End Sub
    '__________________'
Sub TestValue2(ByRef Num)
Num = Num * 3
MsgBox "Num = " & Num
End Sub
Xin cảm ơn Bác @VetMini và Chú @ptm0412 nhiều ạ,con đã hiểu cách sử dụng byVal và byRef rồi.
 
Upvote 0
Hiểu rồi thì thử viết hai hàm con kinh điển xem:

1. Hàm RootFx (function hay sub tuỳ chọn), giải phương trình bậc 1, ax + b = 0 (double)

2. Hàm Swap (sub), hoán đổi trị hai số a và b (double)
 
Upvote 0
Phải nói tới heo và gà mới chịu hiểu
Sao Chú Mỹ lúc nào cũng cứ vùi dập con thê thảm thể nhỉ (ơ nhưng mà cũng đúng mà, vón con đã như vậy rồi, Chú có muốn nhồi nhét nữa con cũng không hấp thụ được --=0)
Bài 72# mục 'chú' của Bác @VetMini là sửa đổi bổ sung sau con chưa để ý, bài 73 con trích dẫn không có phần đấy. :yahoo:
Đầu năm tình hình dịch bệnh đang phức tạp Chú Mỹ hạn chế đi lượn nhé hehe, nhân dịp năm mới con chúc Chú Vạn sự như ý ạ.
Hiểu rồi thì thử viết hai hàm con kinh điển xem:

1. Hàm RootFx (function hay sub tuỳ chọn), giải phương trình bậc 1, ax + b = 0 (double)

2. Hàm Swap (sub), hoán đổi trị hai số a và b (double)
Ôi Bác ơi cái sub nào liên quan đến trích lọc hoặc làm việc với dữ liệu gì đó được không Bác.
Toán con đã dốt rồi, chữ Thầy trả Thầy đã lâu rồi nữa.. mò code không xong con lại đi lại mò thêm toán nữa thì cực quá Bác ơi.
Mà 'hàm con kinh điển' chắc chỉ có các siêu cao thủ mới viết được, con công lực kém ạ :D
 
Upvote 0
- Sao Chú Mỹ lúc nào cũng cứ vùi dập con thê thảm thể nhỉ
- mò code không xong con lại đi lại mò thêm toán nữa thì cực quá Bác ơi.
- Mà 'hàm con kinh điển' chắc chỉ có các siêu cao thủ mới viết được, con công lực kém ạ :D
- Chết tiệt mà lỵ
- Đó là toán lớp 5, giải toán thậm chí không phải toán mà là rèn tư duy logic. Không có tư duy mà toàn nhảy bổ vào công cụ lạ để trích lọc với xử lý dữ liệu, rồi lúng ta lúng túng
- Hàm con kinh điển này là quá căn bản. Bài 1 chiều thành hai chiều và ngược lại, dòng trước cột sau và ngược lại, thay đổi kích thước m x n thành r x s, ... cũng là để rèn tư duy tốt thì không làm.
 
Upvote 0
- Chết tiệt mà lỵ
- Đó là toán lớp 5, giải toán thậm chí không phải toán mà là rèn tư duy logic. Không có tư duy mà toàn nhảy bổ vào công cụ lạ để trích lọc với xử lý dữ liệu, rồi lúng ta lúng túng
- Hàm con kinh điển này là quá căn bản. Bài 1 chiều thành hai chiều và ngược lại, dòng trước cột sau và ngược lại, thay đổi kích thước m x n thành r x s, ... cũng là để rèn tư duy tốt thì không làm.
Dạ được rồi Chú Mỹ , lúc nào con làm được con sẽ up kết quả, không làm được con cũng sẽ la lên ở đây ạ.
 
Lần chỉnh sửa cuối:
Upvote 0
Dạ được rồi lúc nào con làm được con sẽ up kết quả, không làm được con cũng sẽ la lên ở đây.
Có 1 bài tương tự swap trong 1 chủ đề vẽ biểu đồ: Vẽ đồ thị 2 serie 2 trục Y. Vấn đề của người này là muốn đổi vị trí 2 trục từ trái qua phải và ngược lại,
Tôi trả lời thế này mà người ta làm được:
Giả sử tay trái cầm dao, tay phải cầm nĩa và muốn đổi ngược lại thì có 3 cách:
- Đưa dao từ tay trái qua tay phải (tay phải cầm 2 thứ), rồi tay trái nhón lấy cái nĩa
-Đưa nĩa từ tay phải qua tay trái (tay trái cầm 2 thứ), rồi tay phải nhón lấy con dao
- Vất cả 2 xuống bàn rồi lượm lên lại.
Không làm được thì khỏi ăn cỗ

TB
Swap 2 giá trị còn có thêm 2 cách nữa
 
Upvote 0
Hiểu rồi thì thử viết hai hàm con kinh điển xem:

1. Hàm RootFx (function hay sub tuỳ chọn), giải phương trình bậc 1, ax + b = 0 (double)
Bác góp ý thêm cho con ạ:
Mã:
Option Explicit

'GiaiPhuongTrinh:     ax + b = 0
Function GiaiPhuongTrinh_BacNhat(ByRef a As Double, ByRef b As Double) As Double
    Dim x As Double
    If a = 0 And b <> 0 Then
        MsgBox "Phuong trinh vo nghiem"
        Exit Function
    End If
    If a = 0 And b = 0 Then
        MsgBox "Phuong trinh vo so nghiem"
        Exit Function
    End If
    If b <> 0 Then
        x = -b / a
        GiaiPhuongTrinh_BacNhat = x
    End If
End Function
Bỏ biến X:
Mã:
Option Explicit

'GiaiPhuongTrinh:     ax + b = 0
Function GiaiPhuongTrinh_BacNhat(ByRef a As Double, ByRef b As Double) As Double
    If a = 0 And b <> 0 Then
        MsgBox "Phuong trinh vo nghiem"
        Exit Function
    End If
    If a = 0 And b = 0 Then
        MsgBox "Phuong trinh vo so nghiem"
        Exit Function
    End If
    If b <> 0 Then
        GiaiPhuongTrinh_BacNhat = -b / a
    End If
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Phê ngoài lề:
Đối với toán số thực, không thể so sánh chính xác với toán tử =
Nhớ câu này nằm lòng: Hai con số coi như bằng nhau khi chúng cách nhau một dung sai rất nhỏ.
Áp dụng vào zero, một số rất nhỏ coi như zero.

biểu thức -b/a sẽ bị "error 11 division by zero" nếu a = 0
bị "error 6 overflow (tràn số)" nếu a rất nhỏ.

Phê trong lề:
1. Cái function trên sử dụng như thế nào? (Chương trình chính gọi nó ra sao. Trước mắt thì tôi thấy nó chưa xài được)
2. lý do tại sao a và b nhập ByRef mà không phải ByVal ?
 
Upvote 0
Phê ngoài lề:
Đối với toán số thực, không thể so sánh chính xác với toán tử =
Nhớ câu này nằm lòng: Hai con số coi như bằng nhau khi chúng cách nhau một dung sai rất nhỏ.
Áp dụng vào zero, một số rất nhỏ coi như zero.

biểu thức -b/a sẽ bị "error 11 division by zero" nếu a = 0
bị "error 6 overflow (tràn số)" nếu a rất nhỏ.

Phê trong lề:
1. Cái function trên sử dụng như thế nào? (Chương trình chính gọi nó ra sao. Trước mắt thì tôi thấy nó chưa xài được)
2. lý do tại sao a và b nhập ByRef mà không phải ByVal ?
Dạ con xử lý lỗi a=0 và thêm cái sub gọi nó ạ:
Mã:
Option Explicit

Function GPT_B1(ByVal a As Double, ByRef b As Double) As Double
    If a = 0 And b <> 0 Then
        MsgBox "Phuong trinh vo nghiem":    Exit Function
    ElseIf a = 0 And b = 0 Then
        MsgBox "Phuong trinh vo so nghiem": Exit Function
    ElseIf b <> 0 Then GPT_B1 = -b / a:     End If
End Function

Sub Test_GPT()
    Dim a As Double, b As Double, c As Integer, nghiem As Double
    a = 3.1:    b = 6
    nghiem = GPT_B1(a, b)
    
    MsgBox "Ket qua GPT bac nhat: ax + b = 0" & _
            vbNewLine & "khi: a=" & a & "   va   " & "b=" & b & _
            vbNewLine & "=>nghiem:" & nghiem
    
    
    c = 3.1
    nghiem = GPT_B1(c, b)
    MsgBox "Ket qua GPT bac nhat: cx + b = 0" & _
            vbNewLine & "khi: c=" & c & "   va   " & "b=" & b & _
            vbNewLine & "=>nghiem:" & nghiem
End Sub
1613470647297.png
Còn cái số rất nhỏ gì đó con chưa biết cách xử lý, Bác chỉ thêm cho con cách xử lý ạ.
Bài toán này a và b không thể khai báo ByVal vì nếu khai báo ByVal thì giá trị truyền vào có thể không còn là kiểu Double nữa giả sử là Integer thì kết quả sẽ sai
1613470679548.png

Hehe Chú Mỹ thấy Bác @VetMini chỉ lỗi con nhiều quá cười sung sướng!
 
Upvote 0
Trên nguyên tắc, hàm con thuần không được phép sử dụng giao diện người dùng (user interface). MsgBox là mọt loại giao diện người dùng.
Nói cách khác, nếu không giải được phương trình thì hàm phải có báo cho code gọi nó. Nó không thể chỉ báo cho người dùng trong khi code gọi nó thì không biết kết quả nó trả về có phải là nghiệm hay không.

Gợi ý: đó là lý do tại sao bài toán giải phương trình là bài toán kinh điển của bài tập hàm con (và số thực).
 
Upvote 0
Trên nguyên tắc, hàm con thuần không được phép sử dụng giao diện người dùng (user interface). MsgBox là mọt loại giao diện người dùng.
Nói cách khác, nếu không giải được phương trình thì hàm phải có báo cho code gọi nó. Nó không thể chỉ báo cho người dùng trong khi code gọi nó thì không biết kết quả nó trả về có phải là nghiệm hay không.

Gợi ý: đó là lý do tại sao bài toán giải phương trình là bài toán kinh điển của bài tập hàm con (và số thực).
Cảm ơn Bác đã chỉ dẫn, vậy con chỉnh lại như sau ạ:
Mã:
Option Explicit

Function GPT_B1(ByRef a As Double, ByRef b As Double) As Double
    GPT_B1 = -b / a
End Function

Sub Test_GPTB1()
    Dim a As Double, b As Double, nghiem As Double
    a = 3.1:    b = 6
    If a = 0 And b <> 0 Then
        MsgBox "Phuong trinh vo nghiem":    Exit Sub
    ElseIf a = 0 And b = 0 Then
        MsgBox "Phuong trinh vo so nghiem": Exit Sub
    ElseIf b <> 0 Then
        nghiem = GPT_B1(a, b)
        MsgBox "Ket qua GPT bac nhat: ax + b = 0" & _
            vbNewLine & "khi: a=" & a & "   va   " & "b=" & b & _
            vbNewLine & "=>nghiem cua phuong trinh: " & nghiem
    End If
End Sub
 
Upvote 0
Upvote 0
Function RootFx1(ByVal a As Double, ByVal b As Double, ByRef x As Double) As Long
' hàm giải phương trình ax + b = 0, với nghiệm trả về trong tham x
' hàm trả về kết quả kiểm định phương trình
' 1 : nghiệm bình thường
' 0 : vô nghiệm
' -1 : vô định
' Lưu ý: các số 0, 1, -1 là cứ chọn theo ý từng người, không bắt buộc ai phải giống ai
If a không bằng 0 Then
x = -b/a
RootFx1 = 1 ' có nghiệm
ElseIf b không bằng 0 Then
RootFx1 = 0 ' vô nghiệm
Else
RootFx1 = -1 ' vô định
End If
End Function

' một ví dụ code gọi hàm
Dim meRoot As Double
Select Case RootFx(trị 1, trị 2, meRoot) ' trị 1 và 2 có thể là biến, có thể là số,...
Case 1
MsgBox "Nghiệm tính được là " & meRoot
Case 0
MsgBox "Vô nghiệm (a = 0; b <> 0)"
Case -1
MsgBox "Vô định (a = b = 0)"
End Select

Để ý chỗ tô đỏ. Bắt buộc tham này phải là ByRef. Nếu ByVal thì tuy có gán x = -b/a nhưng khi thoát hàm thì x (tức meRoot trong code ví dụ) trở về trị ban đầu của nó.
 
Upvote 0
Function RootFx1(ByVal a As Double, ByVal b As Double, ByRef x As Double) As Long
' hàm giải phương trình ax + b = 0, với nghiệm trả về trong tham x
' hàm trả về kết quả kiểm định phương trình
' 1 : nghiệm bình thường
' 0 : vô nghiệm
' -1 : vô định
' Lưu ý: các số 0, 1, -1 là cứ chọn theo ý từng người, không bắt buộc ai phải giống ai
If a không bằng 0 Then
x = -b/a
RootFx1 = 1 ' có nghiệm
ElseIf b không bằng 0 Then
RootFx1 = 0 ' vô nghiệm
Else
RootFx1 = -1 ' vô định
End If
End Function

' một ví dụ code gọi hàm
Dim meRoot As Double
Select Case RootFx(trị 1, trị 2, meRoot) ' trị 1 và 2 có thể là biến, có thể là số,...
Case 1
MsgBox "Nghiệm tính được là " & meRoot
Case 0
MsgBox "Vô nghiệm (a = 0; b <> 0)"
Case -1
MsgBox "Vô định (a = b = 0)"
End Select

Để ý chỗ tô đỏ. Bắt buộc tham này phải là ByRef. Nếu ByVal thì tuy có gán x = -b/a nhưng khi thoát hàm thì x (tức meRoot trong code ví dụ) trở về trị ban đầu của nó.
1613478619171.png

Mã:
Option Explicit

Public Function IsZero(ByVal Value As Double) As Boolean
    IsZero = Abs(Value) <= (10 ^ -30)
End Function

Function GPT_B1(ByVal a As Double, ByVal b As Double) As Variant
    On Error Resume Next
    GPT_B1 = -b / a
    If Err.Number = 11 Or Err.Number = 6 Then
        GPT_B1 = CVErr(xlErrNum)
    ElseIf Err.Number <> 0 Then
        GPT_B1 = "Mot loi nao do da xay ra ahihi"
    End If
    On Error Resume Next
End Function

Sub Test_GPT()
    Dim a As Double, b As Double, c As Integer, nghiem As Variant
    a = 1:    b = 6
    nghiem = GPT_B1(a, b)

    MsgBox "Ket qua GPT bac nhat: ax + b = 0" & _
            vbNewLine & "khi: a=" & a & "   va   " & "b=" & b & _
            vbNewLine & "=>nghiem:" & nghiem
End Sub


Bài toán bác Vẹt đặt ra hay quá, cháu xin tham gia giải.
Bài đã được tự động gộp:

1613479277874.png
 
Lần chỉnh sửa cuối:
Upvote 0
Sai rồi. Hàm trả kết quả lỗi về sub chứ không phải bắt lỗi ngay trong sub. Cứ tưởng tượng hàm Vlookup trả về #N/A

Vâng vậy phải bắt lỗi trong hàm để xử lý khi gọi qua sub hay nhập xuống tính ạ, còn msgbox sẽ đưa vào sub ạ.

Để ý chỗ tô đỏ. Bắt buộc tham này phải là ByRef. Nếu ByVal thì tuy có gán x = -b/a nhưng khi thoát hàm thì x (tức meRoot trong code ví dụ) trở về trị ban đầu của nó.
Dạ con đã hiểu cảm ơn Bác @VetMini
 
Upvote 0
Upvote 0
Hoán vị không dùng biến trung gian chỉ hiệu quả với số nguyên.
Với số thập phân, nó có hai điểm bất lợi sau:
1. cộng trừ nhân chia số thập phân thì phải chấp nhận khả năng bị sai số.
2. con toán cộng trừ nhân chia số nguyên rất nhanh nhưng số double thì chưa chắc.
 
Upvote 0
Hoán vị không dùng biến trung gian chỉ hiệu quả với số nguyên.
Với số thập phân, nó có hai điểm bất lợi sau:
1. cộng trừ nhân chia số thập phân thì phải chấp nhận khả năng bị sai số.
2. con toán cộng trừ nhân chia số nguyên rất nhanh nhưng số double thì chưa chắc.
Nếu coi số thập phân là string và làm được với câu hỏi bài #96 thì sẽ được
 
Upvote 0
Vâng vậy phải bắt lỗi trong hàm để xử lý khi gọi qua sub hay nhập xuống tính ạ, còn msgbox sẽ đưa vào sub ạ.
Dạ con đã hiểu cảm ơn Bác @VetMini
Tham khảo hàm PTBac1 sau, hàm này có thể sử dụng trên sheet thí dụ C1=PTBac1(A1, B1):
PHP:
Function PTBac1(ByRef a, ByRef b) As Variant
Dim Epsilon As Double
Epsilon = 10 ^ (-9)
If Not IsNumeric(a) Or Not IsNumeric(b) Then
    PTBac1 = "#NUM!"
ElseIf Abs(a) < Epsilon And Abs(b) > Epsilon Then
    PTBac1 = "vô nghiem": Exit Function
ElseIf Abs(a) < Epsilon And Abs(b) < Epsilon Then
    PTBac1 = "vô so nghiem"
Else
    PTBac1 = -b / a
End If
End Function
'--------------'
Sub TestPtB1()
Dim a, b, c
a = "3": b = 0.005
c = PTBac1(a, b)
If IsNumeric(c) Then
    MsgBox "Ban duoc li xi " & c & " VND"
ElseIf c = "vô nghiem" Then
    MsgBox "het tet goy! khong lixi gi sat"
ElseIf c = "vô so nghiem" Then
    MsgBox "Muon li xi bi nhiu tuy y chon?"
Else
    MsgBox "Zo zien!"
End If
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Rất hiếm khi người ta viết hàm IsZero như bài #95.
Cách chuyên nghiệp là viết một hàm so sánh. Muốn biết có zero hay không thì so sánh với 0.

Function CoiNhuBang(a As Double, b As Double) As Boolean
Const RATNHO = 1E-20
CoiNhuBang = Abs(a - b) < RATNHO
End Function

' test zero
If CoiNhuBang(a, 0) Then
MsgBox "a coi nhu bang 0, voi sai so rat nho"
End If
 
Upvote 0
Rất hiếm khi người ta viết hàm IsZero như bài #95.
Cách chuyên nghiệp là viết một hàm so sánh. Muốn biết có zero hay không thì so sánh với 0.
Hàm CoiNhuBang của anh viết khi cần sử dụng nhiều lần trong 1 hoặc nhiều Sub, khi làm việc chủ yếu với Double
 
Upvote 0
Hàm CoiNhuBang của anh viết khi cần sử dụng nhiều lần trong 1 hoặc nhiều Sub, khi làm việc chủ yếu với Double
Nhiệm vụ của hàm con là:
1. sử dụng (được gọi) nhiều lần trong chương trình
2. chuẩn hoá một công việc. (*) (**)

(*) trong hàm trên, trị const = 1E-20 chuẩn hoá độ dung sai chấp nhận. Nếu project lớn, có thể người ta cho thêm một Optional Param để đòi hỏi dung sai khác.

(**) tôi làm việc theo nguyên tắc chia để trị. Một hàm con chỉ làm một công việc, không nhiều hơn, không ít hơn. Khác với truyền thống GPE một hàm con làm cả đống công việc khác nhau.
 
Upvote 0
Nếu coi số thập phân là string và làm được với câu hỏi bài #96 thì sẽ được
1613560648183.png



Mã:
Sub Test_GPT()
    Dim A As Double, B As Double, c As Integer, nghiem As Variant
    A = 1:    B = 6
    Dim truong As String
    Dim vu As String
    truong = "truong"
    vu = "vu"
    HoanVi truong, vu
    MsgBox "truong: " & truong & vbNewLine & "vu: " & vu
End Sub

Sub HoanVi(A, B)
    'A=truong
    'B=vu
    A = A & B 'truongvu
    B = Mid(A, 1, Len(A) - Len(B)) 'Mid(truongvu,1,8-2)=Truong;B-->A
    A = Mid(A, Len(B) + 1) 'Mid(truongvu,len(truong)+1)=vu:B-->A
    
    
    
    
End Sub
 
Upvote 0
Tôi cứ tưởng đâu có cách dùng VarPtr và XOR để đổi con trỏ chuỗi.
Té ra mình tưởng xa quá.
 
Upvote 0
2. Hàm Swap (sub), hoán đổi trị hai số a và b (double)
Bác góp ý thêm cho con ạ:
Mã:
Option Explicit
Sub test_Swap()
    Dim a As Variant, b As Double
    a = 10.12345:     b = 0.12345
    MsgBox "  IN:  a:" & a & " | " & "b:" & b & vbNewLine & _
            "OUT:  " & Swap(a, b)
End Sub
Function Swap(ByVal a As Variant, ByVal b As Double) As String
     a = a & "|" & b
     a = Split(a, "|")
     b = a(0):  a = a(1)
     Swap = "a:" & a & " | " & "b:" & b
End Function
1613565767251.png
Có vẻ càng ngày càng xa chủ đề. Bản thân mình có cảm giác kỳ kỳ.
OT cũng cảm giác vậy,có lẽ nên tách từ bài 69 ra thành một chủ đề khác anh Quang Hải nhỉ :"'
 
Lần chỉnh sửa cuối:
Upvote 0
Bác góp ý thêm cho con ạ:
Mã:
Option Explicit
Sub test_Swap()
    Dim a As Variant, b As Double
    a = 10.12345:     b = 0.12345
    MsgBox "  IN:  a:" & a & " | " & "b:" & b & vbNewLine & _
            "OUT:  " & Swap(a, b)
End Sub
Function Swap(ByVal a As Variant, ByVal b As Double) As String
     a = a & "|" & b
     a = Split(a, "|")
     b = a(0):  a = a(1)
     Swap = "a:" & a & " | " & "b:" & b
End Function
View attachment 254227
Bài đã được tự động gộp:


Bác góp ý thêm cho con ạ:
Mã:
Option Explicit
Sub test_Swap()
    Dim a As Variant, b As Double
    a = 10.12345:     b = 0.12345
    MsgBox "  IN:  a:" & a & " | " & "b:" & b & vbNewLine & _
            "OUT:  " & Swap(a, b)
End Sub
Function Swap(ByVal a As Variant, ByVal b As Double) As String
     a = a & "|" & b
     a = Split(a, "|")
     b = a(0):  a = a(1)
     Swap = "a:" & a & " | " & "b:" & b
End Function
View attachment 254227

OT cũng cảm giác vậy,có lẽ nên tách từ bài 69 ra thành một chủ đề khác anh Quang Hải nhỉ :"'
Giải như này hay nè, vận dụng mảng một cách uyển chuyển.
Bài đã được tự động gộp:

Cháu nghĩ bụng viết hàm đó ra nhưng sau cùng cũng không sài tới.
Bài đã được tự động gộp:

Sao tôi thấy nhiều người dùng Mid thay cho Left, Right nhỉ?
Bị quen tay thôi bác ơi, lắm lúc cứ nghĩ viết sao cho ra kết quả mà cũng không phân biệt được sự khác nhau ở đây.
Bài đã được tự động gộp:

Vụ này có khả thi không bác, cháu thấy nó không liên quan lắm.
 
Lần chỉnh sửa cuối:
Upvote 0
Giải như này hay nè, vận dụng mảng một cách uyển chuyển.
...
Trời ạ. Sử dụng mảng "uyển chuyển" hay không chưa biết. Nhưng sau khi si-quáp, a từ kiểu gì đó tự nhiên bị thành String. b nhờ được định là Double cho nên tự động ép kiểu, tuy nhiên nếu a không phải isnumeric thì ép kiểu sẽ ra "error".

Này thi 'mảng': cứ declare variant tuốt. Tham a, b nạp vào kiểu quái gì cũng được, trừ kiểu object (kẹt cái lệnh Set)
a = array(a, b)
b = a(0)
a = a(1)
 
Upvote 0
Nhưng sau khi si-quáp, a từ kiểu gì đó tự nhiên bị thành String. b nhờ được định là Double cho nên tự động ép kiểu, tuy nhiên nếu a không phải isnumeric thì ép kiểu sẽ ra "error".
Con chỉnh tiếp:
Mã:
Function Swap(ByVal a As Variant, ByVal b As Double, ByRef Sw As Variant)
    a = Array(a, b)
    b = a(0): a = a(1)
    Sw = Array(a, b)
End Function

Sub Test_Swap()
    Dim a As Variant, b As Double, ar As Variant
    a = 0.54321:      b = 0.12345
    Call Swap(a, b, ar)
    MsgBox "Ban dau:    a: " & a & " | " & "b: " & b & vbNewLine & _
           "Sau Swap:  a: " & ar(0) & " | " & "b: " & ar(1)
End Sub

1613577074205.png
 
Upvote 0
Sao không tận dụng Function bỏ luôn tham số Sw và thay bằng Swap = Array(a,b)
Cảm ơn Bạn đã quan tâm, do kiến thức OT hạn hẹp nên cũng không nghĩ đến, theo gợi ý của bạn thì OT viết như sau:
Mã:
Function Swap(ByVal a As Variant, ByVal b As Double) 
    a = Array(a, b)
    b = a(0): a = a(1)
    Swap = Array(a, b)
End Function

Sub test_Swap()
    Dim a As Variant, b As Double, ar As Variant
    a = 0.54321:      b = 0.12345
    ar = Swap(a, b)
    MsgBox "Ban dau:    a: " & a & " | " & "b: " & b & vbNewLine & _
               "Sau Swap:  a: " & ar(0) & " | " & "b: " & ar(1)
End Sub
Nhưng liên quan đến việc phân biệt giữa ByVal, ByRef OT thấy bài 42 vẫn thích hợp hơn.
 
Upvote 0
Hehe, tại nhìn thấy mảng đẹp mắt mà lại có lực.
Cháu đang ngân ngơ theo cái này mà chưa ra, với điều kiện không dùng biến trung gian.
Mã:
Sub HoanVi(a As Object, b As Object)
    
End Sub
Bài đã được tự động gộp:

a = array(a, b)
b = a(0)
a = a(1)
Mã:
Sub HoanVi1(A, B)
    A = Array(A, B)
    B = A(LBound(A))
    A = A(UBound(A))
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Mã:
Sub check()
    Dim r1 As Range
    Dim r2 As Sheet2
    Set r1 = Range("R1")
    Set r2 = Sheet2
    HoanVi r1, r2
    MsgBox "r1:" & r1.Address & vbNewLine & "r2:" & r2
End Sub
Sub HoanVi(A As Object, B As Object)
   Dim c As Object
   Set c = A
   Set A = B
   Set B = c
   MsgBox "Hoan vi da xong."
End Sub

Code trên chạy bị lỗi, nhưng cháu không lý giải được tại sao nó lỗi. Khi chạy thì có hiện thông báo "Hoav vi da xong.", bấm nút ok thì bị báo lỗi.
 
Upvote 0
kiến thức OT hạn hẹp nên cũng không nghĩ đến,

Mấy bài trước kêu hiểu rồi nhưng đến đây lại khiêm tốn thế.

Khi nào nắm chắc tầm vực của biến (tính chất "có thể thấy được" và tính chất "tồn tại") thì lúc đó mới áp dụng ByVal, ByRef nhanh được.
Kế đó mới rõ được 2 câu dưới này.

1613579961647.png

ByRef thường dùng trong Sub hơn.
Nếu dùng ByRef trong Function thì ngoài việc lấy trị chính gán vào tên Function, dùng thêm ByRef cho biến ngoại để lấy trị phụ.

Về lý thuyết nếu nắm được bài 4 và 9 cũng có thể nói là tạm biết dùng ByVal, ByRef, Sub/Function con, bố mẹ...

Ghi chú: Nội dung lý thuyết trong các bài cơ bản đều là những kiến thức mà anh "nhiều chữ" ở trên hướng dẫn :-p

1613580399074.png
 
Upvote 0
Mã:
Sub check()
    Dim r1 As Range
    Dim r2 As Sheet2
    Set r1 = Range("R1")
    Set r2 = Sheet2
    HoanVi r1, r2
    MsgBox "r1:" & r1.Address & vbNewLine & "r2:" & r2
End Sub
Sub HoanVi(A As Object, B As Object)
   Dim c As Object
   Set c = A
   Set A = B
   Set B = c
   MsgBox "Hoan vi da xong."
End Sub

Code trên chạy bị lỗi, nhưng cháu không lý giải được tại sao nó lỗi. Khi chạy thì có hiện thông báo "Hoav vi da xong.", bấm nút ok thì bị báo lỗi.
Hoán vị xong thì r1 = Sheet2 nên lỗi đoạn code r1.Address
 
Upvote 0
Mấy bài trước kêu hiểu rồi nhưng đến đây lại khiêm tốn thế.

Khi nào nắm chắc tầm vực của biến (tính chất "có thể thấy được" và tính chất "tồn tại") thì lúc đó mới áp dụng ByVal, ByRef nhanh được.
Kế đó mới rõ được 2 câu dưới này.

View attachment 254232

ByRef thường dùng trong Sub hơn.
Nếu dùng ByRef trong Function thì ngoài việc lấy trị chính gán vào tên Function, dùng thêm ByRef cho biến ngoại để lấy trị phụ.

Về lý thuyết nếu nắm được bài 4 và 9 cũng có thể nói là tạm biết dùng ByVal, ByRef, Sub/Function con, bố mẹ...

Ghi chú: Nội dung lý thuyết trong các bài cơ bản đều là những kiến thức mà anh "nhiều chữ" ở trên hướng dẫn :p

Cảm ơn Bạn nhiều @befaint
Danh mục các bài viết đó của Bạn thi thoảng OT vẫn thường vào xem (trước đây xem vài lần chưa hiểu nhưng giờ xem lại thấy hiểu dần chút chút) đúng là rất bổ ích, nhưng do chưa có điều kiện luyện tập và va chạm nên chưa hiểu rõ Bạn ạ.
Tiện đây OT cũng đề xuất nếu có thể bạn giới thiệu thêm đôi chút về các vòng lặp for, do, hoặc if, select case.. nữa thì đầy đủ tư liệu hơn cho những người chậm hiểu như OT vì thi thoảng OT vẫn phải tìm kiếm các vấn đề liên quan đến kiến thức này..
 
Upvote 0
Hoán vị xong thì r1 = Sheet2 nên lỗi đoạn code r1.Address
Mã:
Sub check()
    Dim r1 As Range
    Dim r2 As Sheet2
    Set r1 = Range("R1")
    Set r2 = Sheet2
    HoanVi r1, r2
    
    
    MsgBox "ahihi"
End Sub
Sub HoanVi(A As Object, B As Object)
   Dim c As Object
   Set c = A
   Set A = B
   Set B = c
   MsgBox "Hoan vi da xong."
End Sub
Sửa code lại và chạy thì vẫn bị lỗi, lỗi type gì gì đó.
 
Upvote 0
Tiện đây OT cũng đề xuất nếu có thể bạn giới thiệu thêm đôi chút về các vòng lặp for, do, hoặc if, select case.. nữa thì đầy đủ tư liệu hơn cho những người chậm hiểu như OT vì thi thoảng OT vẫn phải tìm kiếm các vấn đề liên quan đến kiến thức này..
Bài 7 .
 
Upvote 0
Mã:
Sub check()
    Dim r1 As Range
    Dim r2 As Sheet2
    Set r1 = Range("R1")
    Set r2 = Sheet2
    HoanVi r1, r2
   
   
    MsgBox "ahihi"
End Sub
Sub HoanVi(A As Object, B As Object)
   Dim c As Object
   Set c = A
   Set A = B
   Set B = c
   MsgBox "Hoan vi da xong."
End Sub
Sửa code lại và chạy thì vẫn bị lỗi, lỗi type gì gì đó.
Thử chỉnh code theo như vậy rồi test thử xem
PHP:
Sub Test()
  Dim A, B
  A = 100
  Set B = New Collection
  HoanVi A, B
  Stop
End Sub

Sub HoanVi(A, B)
  Dim C: C = Array(A, B)
  TaoBanSao B, C(0)
  TaoBanSao A, C(1)
End Sub

Sub TaoBanSao(A, A1)
  If IsObject(A1) Then
    Set A = A1
  Else
    A = A1
  End If
End Sub
 
Upvote 0
VBA không phải là ngôn ngữ có khả năng hướng đối tượng.
Vì thế, biết sử dụng được Objects là tốt rồi, đừng có nghĩ đến chuyện hoán trị Objects.
Công việc đó liên quan đến biến tham chiếu (*1), rất dễ bị rò bộ nhớ, và rất dễ sai.

Ở bài #41 tôi chỉ nói ra cái gì được và cái gì không được thôi. Để thì giờ và tâm trí học tập những điều khác hiệu quả hơn.

Cũng như việc hoán trị không qua biến tạm. Đối với một số ngôn ngữ có khả năng 'piping', người ta dùng cách này để có thể viết tất cả các lệnh gom về một dòng và gói gọn thành hàm trực tiếp (*2). VBA không có klhar năng này cho nên công việc 'giảm số biến' chỉ là trò chơi cho vui thôi.

(*1) cái này dân chuyên LTHĐT dùng nó để thể hiện tínhn đa hình (metamorphism). Cỡ nhơn vật "$200 một tiết C++" chưa chắc biết dùng.

(*2) điển hình macro trong C hoặc inline fucntion trong C++
a += b -= a = b - a; // cộng và trừ (piping values). Hoặc
(a ^= b), (b ^= a), (a ^= b); // bit xor vàv toán tử , (dấu phẩy)
Những ngôn ngữ có thư viện làm việc với dataset thì có lệnh làm thẳng luôn. Ví dụ Python:
x, y = y, x
 
Upvote 0
Mã:
Sub check()
    Dim r1 As Range
    ...
End Sub
Sửa code lại và chạy thì vẫn bị lỗi, lỗi type gì gì đó.

Msgbox chạy vì code sub Hoanvi không lỗi, Hoanvi không lỗi vì khai báo A, B là Variant.

Msgbox xong quay về sub Test mới lỗi trên sub Test. Sub Test lỗi vì khai báo r1 = range sau đó bị đổi thành sheet. Chẳng phải Type Mismatch thì là gì bây giờ
 
Upvote 0
Upvote 0
Dạ, cháu chữ nghĩ tới đoạn này, cảm ơn bác vì đã bật bí.hihi
Chủ yếu là đọc thông báo lỗi, dò code lỗi xem đã chạy tới đâu, câu lệnh nào bị lỗi, ...
Như ví dụ trên thì ngay từ range chuyển sang sheet đã lỗi rồi có đâu mà đến câu lệnh .Address
 
Upvote 0

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

Back
Top Bottom