Thảo luận về IF, AND, OR... trong lập trình VBA (1 người xem)

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

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

VetMini

Ăn cùng góc phố
Tham gia
21/12/12
Bài viết
17,745
Được thích
24,589
Bạn mô tả kiểu võ lâm thì mình làm theo kiểu thiếu lâm tự nhé, còn thiếu xíu nữa là hoàn chỉnh bạn tự bổ sung nhé. Khi nào siêng thì rút gọn code lại tí

Đúng ra Sub này nên tách ra làm 2 sub. Nếu H7 <> "" thì chạy 1 Sub và ngược lại
PHP:
Sub loc()
Dim Data(), Res(), i, j, k
[A13:H5000].ClearContents
Data = Sheet1.Range(Sheet1.[AB9], Sheet1.[AB65536].End(3)).Resize(, 10).Value
ReDim Res(1 To UBound(Data), 1 To 8)
For i = 1 To UBound(Data)
   If Data(i, 1) >= [D5].Value Then
      If Data(i, 1) <= [D6].Value Then
         If [H7].Value = "" Then
            If Data(i, 9) = [H6].Value Or Data(i, 8) = [H6].Value Then
               k = k + 1
               Res(k, 1) = Data(i, 1)
               Res(k, 2) = Data(i, 2)
               Res(k, 3) = Data(i, 3)
               Res(k, 4) = Data(i, 5)
               Res(k, 5) = Data(i, 7)
               If Data(i, 8) = [H6].Value Then Res(k, 6) = Data(i, 9)
               If Data(i, 9) = [H6].Value Then Res(k, 6) = Data(i, 8)
               If Data(i, 8) = [H6].Value Then Res(k, 7) = Data(i, 10)
               If Data(i, 9) = [H6].Value Then Res(k, 8) = Data(i, 10)
            End If
         Else
            If Data(i, 6) = [H7].Value Then
                If Data(i, 9) = [H6].Value Or Data(i, 8) = [H6].Value Then
                  k = k + 1
                  Res(k, 1) = Data(i, 1)
                  Res(k, 2) = Data(i, 2)
                  Res(k, 3) = Data(i, 3)
                  Res(k, 4) = Data(i, 5)
                  Res(k, 5) = Data(i, 7)
                  If Data(i, 8) = [H6].Value Then Res(k, 6) = Data(i, 9)
                  If Data(i, 9) = [H6].Value Then Res(k, 6) = Data(i, 8)
                  If Data(i, 8) = [H6].Value Then Res(k, 7) = Data(i, 10)
                  If Data(i, 9) = [H6].Value Then Res(k, 8) = Data(i, 10)
               End If
            End If
         End If
      End If
   End If
Next
k = k + 1
Res(k, 7) = "=sum(R13C:R[-1]C)"
Res(k, 8) = "=sum(R13C:R[-1]C)"
[A13].Resize(k, 8) = Res
End Sub

Không cần phải vậy. Các IF's trong bài này lồng trơn vào nhau - tức là không có lệnh khác. Như vậy ta có thể dùng AND. Phần code trong ELSE cũng in hệt như IF, nên có thể dùng OR

PHP:
   If Data(i, 1) >= [D5].Value AND Data(i, 1) <= [D6].Value _ ' trong giới hạn ngày '
         AND ([H7].Value = "" OR Data(i, 6) = [H7].Value) _ ' đúng mã KH '
            AND (Data(i, 9) = [H6].Value Or Data(i, 8) = [H6].Value) Then ' dúng mã tài khoản đối chiếu '
               k = k + 1
               Res(k, 1) = Data(i, 1)
               Res(k, 2) = Data(i, 2)
               Res(k, 3) = Data(i, 3)
               Res(k, 4) = Data(i, 5)
               Res(k, 5) = Data(i, 7)
               ' If Data(i, 8) = [H6].Value Then Res(k, 6) = Data(i, 9) '
               ' If Data(i, 9) = [H6].Value Then Res(k, 6) = Data(i, 8) '
               ' If Data(i, 8) = [H6].Value Then Res(k, 7) = Data(i, 10) '
               ' If Data(i, 9) = [H6].Value Then Res(k, 8) = Data(i, 10) '
               ' điều kiện đã thử rồi, [H6].Value bắt buộc phải = Data(i, 8 hoặc 9) '
               Res(k, 6) = Data(i, Iif(Data(i, 8) = [H6].Value, 9, 8))
               Res(k, Iif(Data(i, 8) = [H6].Value, 7, 8)) = Data(i, 10)
   End If
 
Lần chỉnh sửa cuối:
Không cần phải vậy. Các IF's trong bài này lồng trơn vào nhau - tức là không có lệnh khác. Như vậy ta có thể dùng AND. Phần code trong ELSE cũng in hệt như IF, nên có thể dùng OR

PHP:
   If Data(i, 1) >= [D5].Value AND Data(i, 1) <= [D6].Value _ ' trong giới hạn ngày '
         AND ([H7].Value = "" OR Data(i, 6) = [H7].Value) _ ' đúng mã KH '
            AND (Data(i, 9) = [H6].Value Or Data(i, 8) = [H6].Value) Then ' dúng mã tài khoản đối chiếu '
               k = k + 1
               Res(k, 1) = Data(i, 1)
               Res(k, 2) = Data(i, 2)
               Res(k, 3) = Data(i, 3)
               Res(k, 4) = Data(i, 5)
               Res(k, 5) = Data(i, 7)
               ' If Data(i, 8) = [H6].Value Then Res(k, 6) = Data(i, 9) '
               ' If Data(i, 9) = [H6].Value Then Res(k, 6) = Data(i, 8) '
               ' If Data(i, 8) = [H6].Value Then Res(k, 7) = Data(i, 10) '
               ' If Data(i, 9) = [H6].Value Then Res(k, 8) = Data(i, 10) '
               ' điều kiện đã thử rồi, [H6].Value bắt buộc phải = Data(i, 8 hoặc 9) '
               Res(k, 6) = Data(i, Iif(Data(i, 8) = [H6].Value, 9, 8))
               Res(k, Iif(Data(i, 8) = [H6].Value, 7, 8)) = Data(i, 10)
   End If
Hic mình nhắm mắt viết chứ có hiểu mấy đâu. Trúng ý của tác giả là hên lắm rồi. Còn vụ rút gọn phải để cho khổ chủ có việc làm chứ. Mình chỉ khoái mần nửa chừng vậy mà. Mọi người biết rõ cái tật lớn của mình là vậy. Viết code phải 3 lần mới chạy.
 
Upvote 0
Không cần phải vậy. Các IF's trong bài này lồng trơn vào nhau - tức là không có lệnh khác. Như vậy ta có thể dùng AND. Phần code trong ELSE cũng in hệt như IF, nên có thể dùng OR

PHP:
   If Data(i, 1) >= [D5].Value AND Data(i, 1) <= [D6].Value _ ' trong giới hạn ngày '
         AND ([H7].Value = "" OR Data(i, 6) = [H7].Value) _ ' đúng mã KH '
            AND (Data(i, 9) = [H6].Value Or Data(i, 8) = [H6].Value) Then ' dúng mã tài khoản đối chiếu '
               k = k + 1
               Res(k, 1) = Data(i, 1)
               Res(k, 2) = Data(i, 2)
               Res(k, 3) = Data(i, 3)
               Res(k, 4) = Data(i, 5)
               Res(k, 5) = Data(i, 7)
               ' If Data(i, 8) = [H6].Value Then Res(k, 6) = Data(i, 9) '
               ' If Data(i, 9) = [H6].Value Then Res(k, 6) = Data(i, 8) '
               ' If Data(i, 8) = [H6].Value Then Res(k, 7) = Data(i, 10) '
               ' If Data(i, 9) = [H6].Value Then Res(k, 8) = Data(i, 10) '
               ' điều kiện đã thử rồi, [H6].Value bắt buộc phải = Data(i, 8 hoặc 9) '
               Res(k, 6) = Data(i, Iif(Data(i, 8) = [H6].Value, 9, 8))
               Res(k, Iif(Data(i, 8) = [H6].Value, 7, 8)) = Data(i, 10)
   End If

Nếu tôi không lầm thì code của Hải là chuẩn hơn code của bạn.
Ta lấy ví dụ trong lập trình Delphi (thực ra tôi làm nhiều trong Delphi nên hiểu Delphi hơn là Visual Basic nên xét Delphi thôi)
Khi Delphi gặp cấu trúc "IF A and B and C ... and Z then"
thì: Delphi xét từ đk đầu, ở đây là A. Nếu A = TRUE thì xét tiếp đk tiếp theo, cứ thế cho tới đk cuối cùng. Nếu đk hiện hành = FALSE thì Delphi KHÔNG XÉT các đk tiếp theo nữa mà kết luận là "A and B and C ... and Z = FALSE". Rõ ràng Delphi làm như thế là đúng và hợp lý vì 1 đk = FALSE trong cấu trúc trên nghĩa là đk tổng quát = FALSE, chả lý gì mà xét các đk tiếp theo làm gì cho tốn "điện nước".
Nhưng VBA lại không thao tác hợp lý như thế. Dù cho có 1 đk nào = FALSE thì "nó" vẫn tính tất cả các đk rồi trên cơ sở đó tính đk tổng quát.
Tóm lại trong Delphi nếu bạn có n đk thì xấu nhất là Delphi tính n đk (khi n - 1 đk đầu = TRUE) nhưng có thể chỉ tính 1 đk (khi đk đầu tiên = FALSE). Còn trong VBA thì LUÔN tính n đk.

Các bạn hãy chạy Sub he sau:

Mã:
Sub [COLOR=#ff0000]he[/COLOR]()
Dim a As Long
    a = 10
    If a < 9 And bla(a) Then
        MsgBox "good"
    Else
        MsgBox "bad"
    End If
End Sub

Function [COLOR=#ff0000]bla[/COLOR](ByVal a As Long) As Boolean
    If a < 5 Then
        bla = False
    Else
        bla = True
    End If
    MsgBox "bla"
End Function

Rõ ràng đk (a < 9) = FALSE nên cho dù bla trả về TRUE hay FALSE thì đk "a < 9 And bla(a)" = FALSE. Vậy chả lý gì phả tính bla cho tốn "điện nước". Nhưng chạy sub he ta thấy hàm bla vẫn được thực hiện.
-------------
Từ đây ta thấy nếu trong code của Hải đk của IF đầu tiên = FALSE thì những đk của các IF sau sẽ không phải tính. Còn trong code của bạn thì chúng được tính hết. Nếu các đk này đơn giản thì tốn thêm chút điện nước cũng chả sao nhưng ta thử tưởng tượng là trong những trường hợp khác khi các đk tiếp theo chứa giá trị do các hàm nào đó trả về mà code của các hàm ấy tính toán nhiều thì "điện nước" sẽ lớn, phí phạm sẽ lớn.
 
Upvote 0
Còn vụ rút gọn phải để cho khổ chủ có việc làm chứ. Mình chỉ khoái mần nửa chừng vậy mà.

Ấy, khoan đã. Ta thử xét vụ rút gọn này xem có hợp lý không đã. Theo tôi là không hợp lý. Các bạn khác cho ý kiến nhé.
Rút gọn không sai nhưng nếu ta biết được VBA thao tác như thế nào thì ta sẽ biết viết code tối ưu hơn
 
Upvote 0
Ấy, khoan đã. Ta thử xét vụ rút gọn này xem có hợp lý không đã. Theo tôi là không hợp lý. Các bạn khác cho ý kiến nhé.
Rút gọn không sai nhưng nếu ta biết được VBA thao tác như thế nào thì ta sẽ biết viết code tối ưu hơn

Tôi cũng nghĩ như siwtom. Khi 1 điều kiện False sẽ không cần xét những điều kiện còn lại mà chạy thẳng đến Else. Tốc độ cũng tăng đáng kể.

Ngoài ra, tôi đã từng bị lỗi với nhiều điều kiện kết hợp bằng and, or. Viết thêm mấy dòng If không mất công bao nhiêu mà ít bị lỗi hơn, và lỗi dễ tìm để sửa hơn.

Lỗi thí dụ như: Đặt điều kiện a>0 mà a tại 1 dòng dữ liệu nào đó tình cờ lại là text. Nếu 4 điều kiện 4 If, thì khi debug, biết ngay đang lỗi ở dòng chứa điều kiện nào. Nếu 4 điều kiện 1 If với 3 And, khi debug không biết đang lỗi điều kiện nào, vì dòng đó đang chứa cả 4 điều kiện.

2. Ngoài ra tôi cũng ít dùng hàm IIf. Hàm IIf(condition, value if true, value if false), thì cả 3 tham số đều không được lỗi. Thí dụ:

n = a - b
MyResult = IIf(n < 0, n, n ^ (1/2))

nếu n > 0 thì không nói làm gì, nó rút căn bậc 2 của n và ra kết quả.

Chạy đến 1 lúc nào đó a - b < 0, điều kiện thỏa, nhưng vẫn báo lỗi rút căn bậc 2 của số âm, dù ta không xài kết quả đó.
 
Upvote 0
Tôi cũng nghĩ như siwtom. Khi 1 điều kiện False sẽ không cần xét những điều kiện còn lại mà chạy thẳng đến Else. Tốc độ cũng tăng đáng kể.

Ngoài ra, tôi đã từng bị lỗi với nhiều điều kiện kết hợp bằng and, or. Viết thêm mấy dòng If không mất công bao nhiêu mà ít bị lỗi hơn, và lỗi dễ tìm để sửa hơn.

Lỗi thí dụ như: Đặt điều kiện a>0 mà a tại 1 dòng dữ liệu nào đó tình cờ lại là text. Nếu 4 điều kiện 4 If, thì khi debug, biết ngay đang lỗi ở dòng chứa điều kiện nào. Nếu 4 điều kiện 1 If với 3 And, khi debug không biết đang lỗi điều kiện nào, vì dòng đó đang chứa cả 4 điều kiện.

2. Ngoài ra tôi cũng ít dùng hàm IIf. Hàm IIf(condition, value if true, value if false), thì cả 3 tham số đều không được lỗi. Thí dụ:

n = a - b
MyResult = IIf(n < 0, n, n ^ (1/2))

nếu n > 0 thì không nói làm gì, nó rút căn bậc 2 của n và ra kết quả.

Chạy đến 1 lúc nào đó a - b < 0, điều kiện thỏa, nhưng vẫn báo lỗi rút căn bậc 2 của số âm, dù ta không xài kết quả đó.

Thực ra tôi viết: "Rút gọn không sai" là chưa rõ ràng. Vì trong trường hợp này không sai nhưng trong trường hợp khác có thể sai - đi đêm lắm sẽ có ngày gặp ma mà.

Vì có những tình huống khi mà A = TRUE thì KHÔNG BAO GIỜ CÓ LỖI ở B nhưng khi A = FALSE thì B có thể có lỗi. Lúc đó dùng 2 IF thì không có lỗi nhưng thay bằng AND thì có lỗi.
Ví dụ như code, đơn giản thôi vì chỉ để miêu tả vấn đề:

Mã:
Dim sp As Shape

Sub he()
    If Not sp Is Nothing Then
        If sp.Name = "hic hic" Then
            MsgBox "good"
        End If
    End If
End Sub

Sub bla()
    If Not sp Is Nothing And sp.Name = "hic hic" Then
            MsgBox "good"
    End If
End Sub

Giả sử sp = NOTHING. Lúc này chạy code he không có lỗi nhưng code bla có lỗi.
 
Upvote 0
Luật "thấy code thì tính" của VBA (hình như áp dụng cho hầu hết các ngôn ngữ dòng họ Basic) không được hiệu quả như luật "cần mới tính" của dòng họ C. Tuy nhiên tôi chỉ nghĩ tới tốc độ khi gặp những ứng dụng dùng thường xuyên, và ứng dụng có độ quan trọng cần sử lý kịp thời.

Ứng dụng này là mỗi tháng chạy vài lần, một lần chạy tối đa vài chục sheets (nhiều hơn nữa mà dùng Excel là tự đào hố chôn mình). Điểm quan trọng hơn là viết làm sao cho dễ sửa. Một đoạn code lặp lại hai lần chắc chắn là không thuộc loại dễ sửa rồi.

Việc thêm mấy dòng IF's cho dễ sửa thuỳ theo thói quen. Thí dụ như tôi thì không ngại số IF's nhưng lại ngại số END IF's

Hàm IIF chẳng có gì phải ngại nếu dùng với literals. Bính thường thì người ta chỉ tránh dùng nó khi gọi functions:

IIF( điều kiện, gọi hàm 1, gọi hàm 2 )

Nếu muốn đo từng lệnh máy (machine instruction) thì có thể phê bình IIF là hàm, phải qua thủ tục nối, gọi hàm rất rườm rà; trong khi IF là lệnh rẽ nhánh, nhanh hơn nhiều.
 
Lần chỉnh sửa cuối:
Upvote 0
Luật "thấy code thì tính" của VBA (hình như áp dụng cho hầu hết các ngôn ngữ dòng họ Basic) không được hiệu quả như luật "cần mới tính" của dòng họ C. Tuy nhiên tôi chỉ nghĩ tới tốc độ khi gặp những ứng dụng dùng thường xuyên, và ứng dụng có độ quan trọng cần sử lý kịp thời.

Ứng dụng này là mỗi tháng chạy vài lần, một lần chạy tối đa vài chục sheets (nhiều hơn nữa mà dùng Excel là tự đào hố chôn mình). Điểm quan trọng hơn là viết làm sao cho dễ sửa. Một đoạn code lặp lại hai lần chắc chắn là không thuộc loại dễ sửa rồi.

Việc thêm mấy dòng IF's cho dễ sửa thuỳ theo thói quen. Thí dụ như tôi thì không ngại số IF's nhưng lại ngại số END IF's

Hàm IIF chẳng có gì phải ngại nếu dùng với literals. Bính thường thì người ta chỉ tránh dùng nó khi gọi functions:

IIF( điều kiện, gọi hàm 1, gọi hàm 2 )

Nếu muốn đo từng lệnh máy (machine instruction) thì có thể phê bình IIF là hàm, phải qua thủ tục nối, gọi hàm rất rườm rà; trong khi IF là lệnh rẽ nhánh, nhanh hơn nhiều.

1. Ta bàn ở đây là để ý thức cho mọi người cái "bẫy" mà rất có thể ta sẽ sa vào. Nhiều người không rành lắm chỉ "học" theo các bậc sư phụ thì họ không biết và biết đâu sau này cứ có 1 loạt IF họ lại gộp thành AND thì sao?
2. Nếu một người không biết gì mà viết như bạn thì tôi có khi cũng chả góp ý làm gì. Nhưng tôi biết bạn biết về lập trình, vậy ta bàn với nhau về mặt lý thuyết, về nguyên tắc lập trình.
3. Nhiều người "bình thường" sai nhưng tôi không có hứng để bàn luận. Nhưng nếu đó là người có uy tín thì thường tôi thấy "ngứa tay". Nhiều người hiểu lầm là tôi cố ý hạ uy tín của cao thủ kia là thế.
Nhưng không phải như vậy. Nếu A là "gà" mà A viết sai thì tai hại không đáng kể, cùng lắm A chỉ hại mình thôi. Nhưng nếu A là "uy tín", lời nói của A có hàng triệu người nghe, mỗi lời phán mọi người chấp nhận như tiên đề thì sao? Nếu không chỉ ra cái sai của A thì có nghĩa là chấp nhận triệu người ấy cũng làm sai như A. Nếu A sai thì hậu quả khôn lường. Vì thế nên chỉ ra cái sai của A, ý thức cho mọi người những cái bẫy đang đợi.
 
Upvote 0
Tôi thì chưa thấy được cái sai của cao thủ, nhưng tôi viết về hàm IIf vì bản thân tôi đã gặp phải, và tôi muốn cảnh báo những người đang tập tành học để đừng lúng túng giống tôi khi tôi gặp lỗi hàm IIf lần đầu tiên: Lập luận đúng, thuật toán đúng, hàm dùng đúng, thủ thuật cũng đúng, ... Mà kết quả cứ báo lỗi. Chỉ vì không biết các nguyên lý hoạt động của cái mình đang dùng.
 
Lần chỉnh sửa cuối:
Upvote 0
Đáng lẽ thì bàn luận đến đây thì đủ rồi. Nhưng đã nói chuyện "dẫn dắt cừu non" thì bắt buộc tôi phải biện luận.

Theo quan niệm chính tôi thì con nguời có khả năng hướng thiện. Người có ý chí lập trình tự động tìm ra con đường tốt cho mình.

"Lập trình viên chân chính không dùng lệnh GoTo, lập trình viên lão thành không cần lý đến luật này" (real programmers don't use goto, experienced programmers ignore this rule)

Cũng giống như lệnh goto, những điều nguy hiểm của những lênh/hàm nguy hiểm hầu như không thể xảy ra cho một lập trình viên có căn bản. Một khi code có khả năng nguy hiểm thì người ta tự động biết cách tránh.

Nếu muốn giải thích cái khác nhau giữa IF dkA THEN IF dkB và IF (dkA AND dkB) thì giải thích luôn cho cặn kẽ:

Toán tử AND mặc định rằng 2 điều kiện là một nhóm điều kiện trong khi THEN IF mặc định rằng đó là hai điều kiện riêng biệt, cái sau lồng vào cái trước.

Một nhóm điều kiện điều khiển vận hành của cả block cho tới từ khoá kết block (END IF). Hai điều kiện riêng biệt thì có thể xảy ra trường hợp block bên trong đạt đến kết nhưng block bên ngoài còn chứa một số code. Điều này không hẳn đã là "code phản ánh lô gic" (vì vậy trước đây tôi mới đề cập đến vấn đề kiểm tra các từ khoá END IF)

Riêng trong trường hợp của Basic muốn bàn về error thì bàn thêm như thế này:
nếu code đang nằm trong phạm vi hoạt động (scope) của định tính "On Error Resume Next" thì IF (một nhóm điều kiện) khác với IF (kiều kiện a) THEN IF (điều kiện b) ở chỗ nếu xảy ra error trong lúc xét đièu kiên a thì ở trường hợp nhóm execution engine coi như cả nhóm đạt điều kiện trong khi ở trường hợp tuần tự execution engine còn tiếp tục xét điều kiện b. Lối vận hành nào nguy hiểm hơn tuỳ theo quan niệm cá nhân.

MyResult = IIf(n < 0, n, n ^ (1/2))
Như đã nói trước đây, IIF cũng như GoTo, người dùng tự động biết cách tránh lỗi. Trên nguyên tắc thì người ta chỉ dùng cho literals (giống như hàm Choose)

Mã:
Sub [COLOR=#ff0000]he[/COLOR]() Dim a As Long
    a = 10 
    If a < 9 And bla(a) Then
         MsgBox "good" 
    Else
         MsgBox "bad"
     End If 
End Sub

  Function [COLOR=#ff0000]bla[/COLOR](ByVal a As Long) As Boolean
     If a < 5 Then
         bla = False
     Else
         bla = True
     End If     MsgBox "bla"
 End Function

Sub he() Dim a As Long a = 10 If a < 9 And bla(a) Then MsgBox "good" Else MsgBox "bad" End If End Sub Function bla(ByVal a As Long) As Boolean If a < 5 Then bla = False Else bla = True End If MsgBox "bla" End Function
Rõ ràng đk (a < 9) = FALSE nên cho dù bla trả về TRUE hay FALSE thì đk "a < 9 And bla(a)" = FALSE. Vậy chả lý gì phả tính bla cho tốn "điện nước". Nhưng chạy sub he ta thấy hàm bla vẫn được thực hiện.

Khi đem function làm tham số cho hàm (tạm ví IF ở đây là một hàm, và điều kiện tính là tham số), người ta luôn luôn cân nhắc lợi hại. Nhất là trường hợp dùng biến toàn cục. Đối với Basic, người ta làm tuỳ theo trường hợp có muốn hàm LUÔN LUÔN được gọi hay không. Đây là chưa kể tới truờng hợp nếu vận hành của hàm tham số làm biến đổi giá trị của một tham số khác.

Khi nói tới chuyện đem gộp các điều kiện lại thành một nhóm, tôi chỉ nói cho trường hợp thông thường thôi. Các bạn dẫn ra những trường hợp ngặt nghèo mà đáng lẽ một người viết code đàng hoàng không bao giờ vuớng phải.

Khi nói "dùng Goto cũng chẳng sao" không có nghĩa là bất cứ lệnh rẽ nhánh nào cũng dùng được.
 
Upvote 0
Xuất phát từ topic này:
http://www.giaiphapexcel.com/forum/...ột-thành-1-cột-tách-1-cột-thành-2-cột-theo-ĐK
Các cao thủ đã có cuộc tranh luận sôi nỗi qua việc dùng nhiều IF hoặc gộp lại chúng bằng AND và OR
Xét thấy việc tranh luận này rất bổ ích, giúp cho các thành viên mới học lập trình (như tôi) có cái nhìn sâu sắc hơn. Tuy nhiên để chung với topic của Hong.Van thì lại không hợp với chủ đề, tôi mạn phép dời toàn bộ các bài viết thảo luận sang đây!
Xin mời các cao thủ TIẾP TỤC
 
Upvote 0
Tôi thì chưa thấy được cái sai của cao thủ, nhưng tôi viết về hàm IIf vì bản thân tôi đã gặp phải, và tôi muốn cảnh báo những người đang tập tành học để đừng lúng túng giống tôi khi tôi gặp lỗi hàm IIf lần đầu tiên: Lập luận đúng, thuật toán đúng, hàm dùng đúng, thủ thuật cũng đúng, ... Mà kết quả cứ báo lỗi. Chỉ vì không biết các nguyên lý hoạt động của cái mình đang dùng.

Em không biết gì về nguyên lý hoạt động của các hàm, em thường lấy dữ liệu lớn để test về thời gian, cái nào chạy đúng và nhanh em sẽ sử dụng.

Qua việc Sư phụ nhắc đến hàm IIF, trước đây em cũng đã kiểm tra, thấy nó xét tất cả các đối số TRUE, FALSE trong đó, vì thế nếu các điều kiện loại trừ trong đối số bị lỗi sẽ báo lỗi, mặt khác, nếu dùng hàm này trong một số điều kiện cụ thể với dữ liệu ít (chừng 1000 dòng) thì được, nhưng khi vài chục ngàn dòng, em đã test thì dùng IF ... THEN chạy nhanh hơn vì nó có tính rẽ nhánh, nếu nó tìm đúng, ngay lập tức thủ tục trong nó được tính toán, còn không nó chạy đến END IF tức thì.

Nhưng IF vẫn chưa nhanh bằng SELECT CASE.

Nếu như ta có For i = 1 to 10, cái ta cần giá trị từ 1 đến 8 và giá trị khác từ 9 đến 10, nếu ta dùng IF có lẽ ta viết:

If i = 1 Or i = 2 Or ... Or i = 8 Then

ElseIf i = 9 Or i = 10 Then

End If

Nếu viết như vậy thì tính hơi bị mệt!

Tuy nhiên với Select Case thì quá đơn giản, không OR hay AND gì cả:

Select Case i
Case 1 To 8
Case 9, 10
End Select


Với nhiều điều kiện, để tăng tốc cũng như tường minh, ta nên chọn SELECT CASE.
 
Upvote 0
Đáng lẽ thì bàn luận đến đây thì đủ rồi. Nhưng đã nói chuyện "dẫn dắt cừu non" thì bắt buộc tôi phải biện luận.

Theo quan niệm chính tôi thì con nguời có khả năng hướng thiện. Người có ý chí lập trình tự động tìm ra con đường tốt cho mình.

"Lập trình viên chân chính không dùng lệnh GoTo, lập trình viên lão thành không cần lý đến luật này" (real programmers don't use goto, experienced programmers ignore this rule)

Cũng giống như lệnh goto, những điều nguy hiểm của những lênh/hàm nguy hiểm hầu như không thể xảy ra cho một lập trình viên có căn bản. Một khi code có khả năng nguy hiểm thì người ta tự động biết cách tránh.

Nếu muốn giải thích cái khác nhau giữa IF dkA THEN IF dkB và IF (dkA AND dkB) thì giải thích luôn cho cặn kẽ:

Toán tử AND mặc định rằng 2 điều kiện là một nhóm điều kiện trong khi THEN IF mặc định rằng đó là hai điều kiện riêng biệt, cái sau lồng vào cái trước.

Một nhóm điều kiện điều khiển vận hành của cả block cho tới từ khoá kết block (END IF). Hai điều kiện riêng biệt thì có thể xảy ra trường hợp block bên trong đạt đến kết nhưng block bên ngoài còn chứa một số code. Điều này không hẳn đã là "code phản ánh lô gic" (vì vậy trước đây tôi mới đề cập đến vấn đề kiểm tra các từ khoá END IF)

Riêng trong trường hợp của Basic muốn bàn về error thì bàn thêm như thế này:
nếu code đang nằm trong phạm vi hoạt động (scope) của định tính "On Error Resume Next" thì IF (một nhóm điều kiện) khác với IF (kiều kiện a) THEN IF (điều kiện b) ở chỗ nếu xảy ra error trong lúc xét đièu kiên a thì ở trường hợp nhóm execution engine coi như cả nhóm đạt điều kiện trong khi ở trường hợp tuần tự execution engine còn tiếp tục xét điều kiện b. Lối vận hành nào nguy hiểm hơn tuỳ theo quan niệm cá nhân.


Như đã nói trước đây, IIF cũng như GoTo, người dùng tự động biết cách tránh lỗi. Trên nguyên tắc thì người ta chỉ dùng cho literals (giống như hàm Choose)



Khi đem function làm tham số cho hàm (tạm ví IF ở đây là một hàm, và điều kiện tính là tham số), người ta luôn luôn cân nhắc lợi hại. Nhất là trường hợp dùng biến toàn cục. Đối với Basic, người ta làm tuỳ theo trường hợp có muốn hàm LUÔN LUÔN được gọi hay không. Đây là chưa kể tới truờng hợp nếu vận hành của hàm tham số làm biến đổi giá trị của một tham số khác.

Khi nói tới chuyện đem gộp các điều kiện lại thành một nhóm, tôi chỉ nói cho trường hợp thông thường thôi. Các bạn dẫn ra những trường hợp ngặt nghèo mà đáng lẽ một người viết code đàng hoàng không bao giờ vuớng phải.

Khi nói "dùng Goto cũng chẳng sao" không có nghĩa là bất cứ lệnh rẽ nhánh nào cũng dùng được.

Tôi ý thức được và chắc bạn cũng ý thức được là tôi viết không phải với ý "mở mang" cho bạn, và bạn viết không phải để mở mang cho tôi.
Có bao thành viên khác, và cả bao không thành viên khác, âm thầm đọc bài của bạn và của tôi. Nếu họ đều là những người lập trình có kinh nghiệm thì chả có gì để nói. Vì lúc đó như bạn nói:
"Lập trình viên chân chính không dùng lệnh GoTo, lập trình viên lão thành không cần lý đến luật này" (real programmers don't use goto, experienced programmers ignore this rule)

Cũng giống như lệnh goto, những điều nguy hiểm của những lênh/hàm nguy hiểm hầu như không thể xảy ra cho một lập trình viên có căn bản. Một khi code có khả năng nguy hiểm thì người ta tự động biết cách tránh.

Đối tượng mà tôi muốn viết cho họ là những người chưa có kinh nghiệm. Nếu họ đọc:

Không cần phải vậy. Các IF's trong bài này lồng trơn vào nhau - tức là không có lệnh khác. Như vậy ta có thể dùng AND. Phần code trong ELSE cũng in hệt như IF, nên có thể dùng OR

thì họ, những người chưa có kinh nghiệm, sẽ nghĩ: ừ nhỉ, có lý. Tôi chỉ muốn chỉ ra cho họ những cái "bẫy" đang chờ. Không chỉ là code không tối ưu mà có thể trong một trường hợp nào đấy code bị sai.
Tôi trả lời bài viết của bạn nhưng tôi viết cho những người đang và sẽ đọc bài của bạn. Bạn có hiểu không? Đâu chỉ có tôi và bạn đọc bài của nhau? Phải giải thích cho họ chứ nếu họ không ý thức được những cái mà tôi, bạn hay ptm0412 biết mà cứ "nhắm mắt" làm theo thì nguy to.

À mà:

Khi nói tới chuyện đem gộp các điều kiện lại thành một nhóm, tôi chỉ nói cho trường hợp thông thường thôi. Các bạn dẫn ra những trường hợp ngặt nghèo mà đáng lẽ một người viết code đàng hoàng không bao giờ vuớng phải.

Tôi nhắc lại: Tôi không viết cho những "người viết code đàng hoàng", vì những người có kinh nghiệm thì họ biết cách viết code. Tôi chỉ muốn ý thức cho những người chưa có nhiều kinh nghiệm thôi.

Có lẽ tôi đã nói hết ý của mình trong chủ đề này.
 
Upvote 0
@Hoàng Trọng Nghĩa:

Trong lập trình game, hoặc lập trình realtime, tốc độ là điều tất yếu. Cái này không cần phải bàn cãi.

Trong môi trường làm việc thương mãi (kế toán là một phân khoa của thương mãi), người làm còn phải cân nhắc giữa tốc độ và độ chỉnh sửa cũng như cách thức diễn giải code đi sát với lô gíc quy trình. Đối với các phần mềm đạt tính chất kiểm toán được (auditable), điều kiện sau quan trọng hơn tốc độ nhiều.

Thậm chí ở một vài nơi tôi từng hợp tác, cơ quan có chỉ thị rõ ràng là chỉ sử dụng VBA trong trường hợp không thể dùng hàm worksheet. Chỉ sử dụng CSE (array formula) khi không thể sử dụng hàm thường. Và dùng hàm phải hạn chế số dấu ngoăc "()". Vì có những luật đó nên dùng cột phụ là điều được khuyến khích. Làm quản lý một thời gian rồi sẽ thấy mục đích của luật lệ trên. Mấy cái công thức phức tạp, lúc đầu thì chạy rất ngon, lâu ngày tự dưng gặp một vài dữ liệu không thích hợp - thà nó báo lỗi thì không nói chi, gặp lúc người ta bẫy lỗi rồi đưa ra kết quả trật lất mới chết chứ. Nhiều bảng tính, mình mò cả buổi không biết con số ở đâu ra. Gặp sở thuế kiểm toán thì ủ tờ.

Lý do chính tôi đưa ra đoạn code trên là vì code nguyên thuỷ có sự lặp lại hai đoạn code in hệt nhau. Tôi chỉ đề nghị gộp lại thành một đoạn cho dễ nhìn dễ sửa. Những cái còn lại tuỳ thuộc vào trường phái sở thích riêng từng ngưòi.
 
Lần chỉnh sửa cuối:
Upvote 0
Riêng trong trường hợp của Basic muốn bàn về error thì bàn thêm như thế này:
nếu code đang nằm trong phạm vi hoạt động (scope) của định tính "On Error Resume Next" thì IF (một nhóm điều kiện) khác với IF (kiều kiện a) THEN IF (điều kiện b) ở chỗ nếu xảy ra error trong lúc xét đièu kiên a thì ở trường hợp nhóm execution engine coi như cả nhóm đạt điều kiện trong khi ở trường hợp tuần tự execution engine còn tiếp tục xét điều kiện b. Lối vận hành nào nguy hiểm hơn tuỳ theo quan niệm cá nhân.

Xét trong trường hợp bạn nói, thì sự nguy hiểm đến từ câu "On Error Resume Next", chứ không phải từ chỗ If nhóm hay nhóm If.
Do đó, bản thân tôi cũng ít khi dùng câu này. Không dùng, và test thật kỹ để sửa lỗi, hoặc bẫy lỗi, chứ không bỏ qua lỗi. Trừ khi biết chắc rằng lỗi có thể bỏ qua.

Còn đối với người mới học tại GPE đây, (bài viết tại GPE thì thành viên GPE đọc nhiều nhất), thực sự mà nói thì đa số không qua trường lớp bài bản, kể cả tôi.

Vậy nếu tôi đi trước, tôi truyền lại 1 số kính nghiệm, truyền lại 1 số lỗi tôi đã gặp, thì tốt hơn là để họ trông mong vào "bản năng hướng thiện". Ít nhất, họ có thể rút ngắn thời gian học được vài giờ. Hơn nữa, học online thì những vấn đề căn bản như "chỉ dùng IIf cho tham số literal" có mấy ai nói cho mà biết, nên họ sẽ khó mà đạt mức "thấy nguy hiểm tự động tránh"

Còn vấn đề nguy hiểm của Goto, cho đến nay tôi chưa gặp phải, (vì bản thân tôi cũng ít dùng), nhân dịp này tôi cũng muốn lắng nghe thêm, bạn vui lòng viết tiếp cặn kẽ hộ.
 
Lần chỉnh sửa cuối:
Upvote 0
Nhưng IF vẫn chưa nhanh bằng SELECT CASE.
Với nhiều điều kiện, để tăng tốc cũng như tường minh, ta nên chọn SELECT CASE.
Select Case dùng cho nhiều điều kiện khác nhau của cùng 1 tham số. Không thể dùng cho nhiều điều kiện liên quan đến 2 tham số trở lên.
 
Upvote 0
Em không biết gì về nguyên lý hoạt động của các hàm, em thường lấy dữ liệu lớn để test về thời gian, cái nào chạy đúng và nhanh em sẽ sử dụng.

Qua việc Sư phụ nhắc đến hàm IIF, trước đây em cũng đã kiểm tra, thấy nó xét tất cả các đối số TRUE, FALSE trong đó, vì thế nếu các điều kiện loại trừ trong đối số bị lỗi sẽ báo lỗi, mặt khác, nếu dùng hàm này trong một số điều kiện cụ thể với dữ liệu ít (chừng 1000 dòng) thì được, nhưng khi vài chục ngàn dòng, em đã test thì dùng IF ... THEN chạy nhanh hơn vì nó có tính rẽ nhánh, nếu nó tìm đúng, ngay lập tức thủ tục trong nó được tính toán, còn không nó chạy đến END IF tức thì.

Nhưng IF vẫn chưa nhanh bằng SELECT CASE.

Nếu như ta có For i = 1 to 10, cái ta cần giá trị từ 1 đến 8 và giá trị khác từ 9 đến 10, nếu ta dùng IF có lẽ ta viết:

If i = 1 Or i = 2 Or ... Or i = 8 Then

ElseIf i = 9 Or i = 10 Then

End If

Nếu viết như vậy thì tính hơi bị mệt!

Tuy nhiên với Select Case thì quá đơn giản, không OR hay AND gì cả:

Select Case i
Case 1 To 8
Case 9, 10
End Select


Với nhiều điều kiện, để tăng tốc cũng như tường minh, ta nên chọn SELECT CASE.

Muốn thử nghiệm thì test file này thử xem, đừng dùng mảng dể kéo dài t, từ đó sẽ rút ra nên dùng if hay and trong trường hợp này.
 

File đính kèm

Upvote 0
Kinh nghiệm của tôi, khi dùng nhiều điều kiện AND tôi phải suy luận.

Với các điều kiện A, B, C, D, ta xét như sau:

Nếu thấy: A chứa trong B, B chứa trong C, C chứa trong D

Thì ta cần xét đầu tiên đó là A, tức tập con nhỏ nhất, đến tập con lớn hơn là B, đến tập con lớn hơn là C và cuối cùng là tập mẹ D.

Vậy thì khi sử dụng tôi sẽ:

If giá trị = A AND giá trị = B AND giá trị = C AND giá trị = D Then
....
End If


Về tính logic thì "thằng nhỏ" không có thì mắc mớ gì đến "thằng lớn" sẽ có, cho nên khi xét đến AND thì chỉ 1 giá trị FALSE thì tất cả cùng FALSE vì thế nó xét giá trị đầu tiên, thấy False là nó bỏ qua, không tính thêm gì nữa.

Nhưng với cấu trúc IF ... AND ... THEN trên không hay cho lắm, tôi thường chẻ nó ra từng cặp AND với nhau:

If giá trị = A AND giá trị = B Then ....
If giá trị = C AND giá trị = D Then
....

End If
End If


Tại sao phải làm vậy? Tôi tự suy rằng nếu có nhiều đối số nó sẽ xét cái nào là AND cái nào là OR, sau đó nó xét tiếp từng biểu thức bên trong AND và OR đó. Vậy thì nếu ta xét điều kiện chủ yếu mà không có thì mắc mớ gì xét tiếp những cái còn lại, thế thì không những dài dòng văn tự mà còn bị tính cho tất cả nữa, có nên không?

Đó là kinh nghiệm mà tôi tự rút ra được sau nhiều lần thử code, còn các lập trình viên chuyên nghiệp không biết họ tính như thế nào.
 
Upvote 0
Test file thanhlanh thì phải tạo dữ liệu nhiều. Tôi test với 60 ngàn dòng:
- Code 1 If và And: 5.02 giây
- Code 2 If: 4.08 giây

Chứng tỏ nhiều If nhanh hơn 1 If nhiều And 1 chút.
 
Upvote 0
Web KT

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

Back
Top Bottom