Bài tập VBA về cơ bản ByVal, ByRef; Bài tập hàm con

Liên hệ QC

Hoàng Nhật Phương

Thành viên gắn bó
Tham gia
5/11/15
Bài viết
1,894
Được thích
1,214
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:
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
Web KT

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

Back
Top Bottom