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,213
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
Web KT
Back
Top Bottom