Đố vui về VBA! (1 người xem)

Liên hệ QC

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

anhtuan1066

Thành viên gạo cội
Tham gia
10/3/07
Bài viết
5,802
Được thích
6,912
Nhằm cũng cố kiến thức về VBA cho các bạn mới bắt đầu và cả những bạn đang ứng dụng mà chưa hiểu nhiều về nó, tôi mở topic này với mong mõi qua những câu hỏi vui, các bạn sẽ nhận định lại sự hiểu biết cũa mình... (Kễ cã chính tôi cũng đang tập tành nên có rất nhiều cái chưa biết)
Mong rằng topic sẽ mang đến cho các bạn những khám phá thú vị với những cái tưỡng chừng như đã biết
Mong nhận dc bài viết về câu đố cũa các cao thủ! Còn các bạn mới thì đừng ngại khi đưa ra ý kiến cũa mình.. Có sai có sữa sẽ hoàn thiện!
Tôi xin mỡ màn trước bằng 1 câu hỏi đơn giãn
ANH TUẤN

CÂU HỎI 1: Tại sao biến K ko hoạt động?
Tôi muốn khi nhấn vào 1 button thì cell A1 sẽ tăng lên 1 đơn vị... Tôi đã làm như sau:
-Tạo 1 Command Button (nút nhấn thuộc thanh Control Toolbox), click phải chuột lên nút nhấn, chọn View code, rồi gõ vào đoạn code sau:
PHP:
Private Sub CommandButton1_Click()
   K = K + 1
   Range("A1").Value = K
End Sub
Ban đầu K chưa có gì, xem như =0, nhấn nút lần thứ nhất thì K dc tăng thêm 1, vậy K hiện tại sẽ bằng 1, và gán K vào cell A1 thì đương nhiên A1 sẽ =1... Nhấn nút lần 2, K lại dc tăng thêm 1 nên hiện tại K sẽ =2 và cell A1 cũng sẽ =2... vân vân.. từ đó diễn tiến tiếp...
Hi.. hi.. Điều này nghe qua có vẽ rất hợp lý, ấy thế mà khi nhấn nút nó chỉ hoạt động dc duy nhất 1 lần (A1 = 1) rồi thôi ko nhút nhít nữa...
Các bạn có thể giãi thích tại sao lại như thế ko? Tại sao những lần nhấn nút sau đó K lại ko tăng thêm tí nào (vì thực tế A1 vẫn cứ = 1 hoài) ?
ANH TUẤN
 
Pờ ro pơ ty thì phải. Không cần thuật toán, sự kiện hay thủ tục nào luôn.
Oh, kinh chưa, kinh chưa! Không phải như ComboBox đâu nha, nhưng CBB nó cho match 1 ký tự, và chỉ được nhập 1 ký tự, còn câu này em đố không hạn chế ký tự nhập vào, nhưng nếu bị kẹt phím (nhấn lâu 1 phím bất kỳ) thì sẽ chỉ cho nhảy đúng 1 ký tự, chứ không phải cho ra rất nhiều ký tự.
 
Upvote 0
Oh, kinh chưa, kinh chưa! Không phải như ComboBox đâu nha, nhưng CBB nó cho match 1 ký tự, và chỉ được nhập 1 ký tự, còn câu này em đố không hạn chế ký tự nhập vào, nhưng nếu bị kẹt phím (nhấn lâu 1 phím bất kỳ) thì sẽ chỉ cho nhảy đúng 1 ký tự, chứ không phải cho ra rất nhiều ký tự.
Cái gì mà kinh. Câu đố không rõ lắm:
"không phải gõ một ký tự mà gõ bao nhiêu cũng được" khiến cho tôi nhầm rằng gõ nhiều ký tự trên bàn phím (khác nhau). Nếu nói rõ là gõ 1 ký tự nhiều lần thì khác rồi.
Khi phần đầu nhầm thì phần sau cũng nhầm theo: "nó vẫn chỉ ra 1 ký tự mà thôi." lại hiểu là gõ những ký tự khác nhau nhưng kết quả chỉ toàn 1 ký tự giống nhau.

TB:
Gõ nhiều ký tự khác nhau nhưng chỉ ra 1 loại ký tự là như sau:

1674118458959.png
 
Upvote 0
Cái gì mà kinh. Câu đố không rõ lắm:
"không phải gõ một ký tự mà gõ bao nhiêu cũng được" khiến cho tôi nhầm rằng gõ nhiều ký tự trên bàn phím (khác nhau). Nếu nói rõ là gõ 1 ký tự nhiều lần thì khác rồi.
Khi phần đầu nhầm thì phần sau cũng nhầm theo: "nó vẫn chỉ ra 1 ký tự mà thôi." lại hiểu là gõ những ký tự khác nhau nhưng kết quả chỉ toàn 1 ký tự giống nhau.

TB:
Gõ nhiều ký tự khác nhau nhưng chỉ ra 1 loại ký tự là như sau:

View attachment 285827
Không phải kiểu gõ Password đâu, lưu ý lại là khi gõ 1 phím bất kỳ, nếu giữ lâu, thông thường nó cho ra rất nhiều ký tự đó trong 1 lần nhấn và giữ phím, câu đố ở đây là ngăn không cho điều đó xảy ra, dù cho nhấn phím liền hoặc/và giữ lâu nó cũng chỉ cho ra đúng 1 ký tự.
 
Upvote 0
Oh, kinh chưa, kinh chưa! Không phải như ComboBox đâu nha, nhưng CBB nó cho match 1 ký tự, và chỉ được nhập 1 ký tự, còn câu này em đố không hạn chế ký tự nhập vào, nhưng nếu bị kẹt phím (nhấn lâu 1 phím bất kỳ) thì sẽ chỉ cho nhảy đúng 1 ký tự, chứ không phải cho ra rất nhiều ký tự.
Có phải gán biến so sánh số lần keypress và keyup không anh nhỉ? nếu keypress > keyup thì xử lý
 
Upvote 0
Có phải gán biến so sánh số lần keypress và keyup không anh nhỉ? nếu keypress > keyup thì xử lý
Xử lý OK chưa bạn ơi. Hình như đếm nó cũng không phải là cách nhỉ?

Các thành viên khác chắc giờ đang chuẩn bị đón tết Nguyên đán nên cũng thấy ít tham gia cho vui hen.
 
Upvote 0
Mồ được đào lên, chêm câu hỏi mới.
Mình tò mò đọc thử câu hỏi đầu tiên là cái gì. Mới té ra hơn 15 năm rồi mà nó chưa được giải thích chính đáng, theo đúng ngôn ngữ VBA.
:confused::confused::confused:
 
Upvote 0
Tôi có một TextBox trên UserForm, bằng thuật toán nào, thủ tục nào, sự kiện nào để khi tôi gõ phím nó chỉ ra đúng 1 ký tự dù có nhấn lâu phím đó.

Lưu ý, không phải gõ một ký tự mà gõ bao nhiêu cũng được, nhưng mỗi lần gõ dù có bị kẹt phím (nhấn lâu) nó vẫn chỉ ra 1 ký tự mà thôi.

Bạn xem thử code này có ra đúng yêu cầu không nhé.

Rich (BB code):
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    Static key As Long
    Dim total As Long

    total = Len(TextBox1.Text) + 1
    If KeyAscii = key Then
        Me.TextBox1.Value = Left(Me.TextBox1.Text, total - 2)
    End If
    key = KeyAscii
End Sub
 
Upvote 0
Bạn xem thử code này có ra đúng yêu cầu không nhé.

Rich (BB code):
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    Static key As Long
    Dim total As Long

    total = Len(TextBox1.Text) + 1
    If KeyAscii = key Then
        Me.TextBox1.Value = Left(Me.TextBox1.Text, total - 2)
    End If
    key = KeyAscii
End Sub
Tuyệt vời! Đây là một đáp án đúng, nhưng nó khá dùng công thức tính toán, thử dùng sự kiện và đơn giản hơn xem sao!
Bài đã được tự động gộp:

Mồ được đào lên, chêm câu hỏi mới.
Mình tò mò đọc thử câu hỏi đầu tiên là cái gì. Mới té ra hơn 15 năm rồi mà nó chưa được giải thích chính đáng, theo đúng ngôn ngữ VBA.
:confused::confused::confused:
Vậy anh có câu trả lời chính đáng cho bài đầu tiên hay không? Nếu có xin trả lời để anh em được học hỏi, còn không thì đừng có spam để tăng lượng bài viết lên. Số lượng bài viết thấy nhiều quá mà bài viết toàn là những bài chả ý nghĩa gì cả!
 
Lần chỉnh sửa cuối:
Upvote 0
Xử lý OK chưa bạn ơi. Hình như đếm nó cũng không phải là cách nhỉ?

Các thành viên khác chắc giờ đang chuẩn bị đón tết Nguyên đán nên cũng thấy ít tham gia cho vui hen.
Em làm theo hướng đó, nhưng thử nhập chậm thì ổn. Gõ mà nhanh thì không hiểu sao nó chưa chuẩn lắm, tết bận rộn nên cũng không có nhiều thời gian suy nghĩ:
Mã:
Private i As Long, txt As String
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    If i > 0 Then
        TextBox1.Text = txt
    Else
        txt = TextBox1.Text
        i = i + 1
    End If
End Sub

Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    i = 0
End Sub
Cách của anh @ongke0711 lại hơi bất tiện nếu như không phải kẹt phím mà người dùng muốn nhập 2 ký tự giống nhau liên tiếp thì không được
 
Upvote 0
Upvote 0
Nếu muốn gõ yellow, success thì không được. Tiếng Việt gõ kiểu telex ee = ê, oo = ô, aa = â, dd = đ cũng không được.
Code chỉ đúng yêu cầu đề bài chứ không yêu cầu phải gõ được đúng từ vựng tiếng Anh, tiếng Việt nhé bác. vd: Boong tàu..
Còn bác nào viết được code phân biệt và cho phép gõ: 2 chữ "o" để thành "Boong" hay "bô" thì rẩt đáng khâm phục và học hỏi đó.
 
Lần chỉnh sửa cuối:
Upvote 0
Code chỉ đúng yêu cầu đề bài chứ không yêu cầu phải gõ được đúng từ vựng tiếng Anh, tiếng Việt nhé bác. vd: Boong tàu..
Còn bác nào viết được code phân biệt và cho phép gõ: 2 chữ "o" để thành "Boong" hay "bô" thì rẩt đáng khâm phục và học hỏi đó.
Code bài #1412 của Nhattanktnn làm được rồi á, tôi test rồi dù bạn đó tự nhận "chưa chuẩn lắm".

Code đó test được như sau: gõ telex oo = ô, ooo = oo

1674195101579.png

Đề bài không rõ ràng lắm (tôi cũng đã nhầm), nhưng giờ thì hiểu là chỉ "tránh việc lỡ đè phím ra nhiều ký tự" thôi, nhấn riêng rẽ phải được như bình thường.
Tôi không phân biệt tiếng Anh và tiếng Việt gì trong thí dụ ở trên, chỉ là "gõ riêng rẽ 2 ký tự giống nhau liền kề".
 
Upvote 0
Nếu muốn gõ yellow, success thì không được. Tiếng Việt gõ kiểu telex ee = ê, oo = ô, aa = â, dd = đ cũng không được.
Cái này anh chỉ việc nhấn 2 lần phím L với Yellow và 2 lần phím C, S với Success thôi, ý là cứ mỗi chữ mình chỉ được gõ 1 lần thôi, nếu có bị kẹt phím, nhấn và giữ lâu nó cũng vẫn cho ra 1 ký tự chứ không phải chữ đó mà ra một dãy luôn.
 
Upvote 0
Cái này anh chỉ việc nhấn 2 lần phím L với Yellow và 2 lần phím C, S với Success thôi, ý là cứ mỗi chữ mình chỉ được gõ 1 lần thôi, nếu có bị kẹt phím, nhấn và giữ lâu nó cũng vẫn cho ra 1 ký tự chứ không phải chữ đó mà ra một dãy luôn.
Đó chính là điều mà tôi nói ở bài 1415, nói rằng bài 1412 đã làm được. Còn gõ không được là nói về code bài 1409.
 
Upvote 0
Đó chính là điều mà tôi nói ở bài 1415, nói rằng bài 1412 đã làm được. Còn gõ không được là nói về code bài 1409.
Bài đó, đã đạt yêu cầu, không sai vào đâu được. Nhưng nếu ngắn gọn hơn nữa mới thật sự là đáp án của em.
 
Upvote 0
Em làm theo hướng đó, nhưng thử nhập chậm thì ổn. Gõ mà nhanh thì không hiểu sao nó chưa chuẩn lắm, tết bận rộn nên cũng không có nhiều thời gian suy nghĩ:
Mã:
Private i As Long, txt As String
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    If i > 0 Then
        TextBox1.Text = txt
    Else
        txt = TextBox1.Text
        i = i + 1
    End If
End Sub

Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    i = 0
End Sub
Cách của anh @ongke0711 lại hơi bất tiện nếu như không phải kẹt phím mà người dùng muốn nhập 2 ký tự giống nhau liên tiếp thì không được

Muốn nhập thêm thì bấm phím Shift, muốn bấm phím Shift để nhập thêm thì viết thêm Code cho nó..... :D
 
Upvote 0
Em làm theo hướng đó, nhưng thử nhập chậm thì ổn. Gõ mà nhanh thì không hiểu sao nó chưa chuẩn lắm, tết bận rộn nên cũng không có nhiều thời gian suy nghĩ:
Mã:
Private i As Long, txt As String
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    If i > 0 Then
        TextBox1.Text = txt
    Else
        txt = TextBox1.Text
        i = i + 1
    End If
End Sub

Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    i = 0
End Sub
Cách của anh @ongke0711 lại hơi bất tiện nếu như không phải kẹt phím mà người dùng muốn nhập 2 ký tự giống nhau liên tiếp thì không được
Bài này về hình thức là đạt rồi nhé bạn, rất cố gắng và sáng tạo, tuy nhiên nói tối ưu thì chưa, bởi vì nếu ta gõ 4 ký tự thì có nghĩa sự kiện nó sẽ thay đổi 4 lần, nhưng mình thử thêm vào sự kiện Change thì nó bị thay đổi mấy lần nhé:

Mã:
Private Sub TextBox1_Change()
    Debug.Print "Change"
End Sub
 
Upvote 0
Hôm nay cuối năm âm lịch rồi, mình gửi đáp án luôn cho trọn vẹn nè, dưới đây là code ngăn không cho kẹt phím:
Mã:
Private priBlnOneKey As Boolean
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    If priBlnOneKey Then KeyCode = 0
    priBlnOneKey = True
End Sub
Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    priBlnOneKey = False
End Sub
''TEST:
Private Sub TextBox1_Change()
    Debug.Print "Change"
End Sub
Đơn giản nhưng hiệu quả phải không quý vị?!
 

File đính kèm

Upvote 0
Upvote 0
Chàng Nghĩa này kỳ; Kẹt fím thì nên mua bàn fím khác hay dùng búa, đục, . . . mà sửa; Xài Code là lạm dụng!
:D :D :D

Chúc mọi người vui vẻ nhân dịp xuân về!
Là tỉ dụ mừ bác _)(#;, mà phải công nhận ra được đáp án của anh Nghĩa “đẹp zai” thì pha xử lý “cồng kềnh” thật!
 
Upvote 0
Chàng Nghĩa này kỳ; Kẹt fím thì nên mua bàn fím khác hay dùng búa, đục, . . . mà sửa; Xài Code là lạm dụng!
Anh chưa từng bị tê ngón tay, nhấn phím xuống và nó đơ không nhấc ngón tay lên được? Bàn phím không hư cũng bị tuốt ấy chứ.
Tuy vậy, tôi cũng sẽ không viết code cho các sự kiện như vậy, vì sự kiện còn dùng cho những việc khác. Viết nhiều nó rối. Với lại có những sự kiện rất khó kiểm soát.
 
Lần chỉnh sửa cuối:
Upvote 0
Điểm khác biệt giữa máy tính và điện thoại là bàn phím, nếu như máy tính mà kẹt phím thì rất nhiều ký tự được sản sinh ra trong thời gian nhấn và giữ phím, còn điện thoại thì không, một và chỉ một mà thôi.

Anh chưa từng bị tê ngón tay, nhấn phím xuống và nó đơ không nhấc ngón tay lên được? Bàn phím không hư cũng bị tuốt ấy chứ.
Tuy vậy, tôi cũng sẽ không viết code cho các sự kiện như vậy, vì sự kiện còn dùng cho những việc khác. Viết nhiều nó rối. Với lại có những sự kiện rất khó kiểm soát.

Thật ra có những thứ mình cần khống chế số lượng ký tự, nếu quá sẽ bị lỗi. Câu đố trên thực tế mình đã làm ở bài này cho cái TextBox nhập giờ mà mọi người không để ý:


Mọi người tải file về và kiểm tra sẽ thấy 2 sự kiện đó trong file.

Mình đã thử nhiều cách, nhưng thực sự là bó tay.
Chủ thớt đăng cái này làm anh em hại não quá :unknw:
Tôi nghĩ bạn này đã biết lâu rồi nhưng "giả vờ giả vật" để làm tăng sự chú ý của bài viết chăng?
 
Lần chỉnh sửa cuối:
Upvote 0
Đâu phải ai cũng có điều kiện như bác Sa hen!
Mình gắn thêm bàn fím & xài nó; Khi nào sắp hư (hiện giờ đang ở cữ thôi) thì xài cái gốc. & như vậy có thòi gian tha hồ vọc sửa; cho đến khi dùng đến búa & kìm mà không đặng thì ra tiệm!
. . . . .

Chúc mọi người vui vẻ!
 
Upvote 0
Khi nào sắp hư (hiện giờ đang ở cữ thôi) thì xài cái gốc
Thế nguy hiểm bác, khi nào gần hư ta mua cái khác thay vào chứ, cái gốc phải giữ đấy tuyệt đối giữ cho mới.
Tính em là cứ cái gì mới là nâng như nâng trứng, sau đó có vô tình làm rơi, hay gặp sự cố gì là bắt đầu tung như tung trứng, kéo theo 1 dãy cái sự bõ công.
Chúc bác năm mới vui vẻ, khỏe y năm ngoái!
TextBox nhập giờ mà mọi người không để ý
Chào bác, cái này lúc bác up lên là em test luôn, giữ lên xuống mà time nó không chạy phải nói là hơi bị ức chế, nhưng em thấp cổ bé họng nên không ý kiến được. Với lại có yêu cầu ************ mở code nên phải bỏ qua luôn.
Chắc bác nghiên cứu bỏ cái kìm kẹp phím thì tiện hơn. Thêm nữa là em không thích font dạng đá chân như Times ... lắm. Màu sắc thì dễ nhìn nhưng hơi đậm quá, nhẹ nhàng tí thì hợp ý em hơn. Đây là mong muốn mang tính chất cá nhân, nếu được thì mỗi mình em khoái, còn không được thì em vẫn dùng thôi. Tùy bác nhé.
Chúc bác năm mới vui vẻ, khỏe y như bác trên!
Mình đã thử nhiều cách, nhưng thực sự là bó tay.
Bác có nhiều ứng dụng hơi bị chất đấy. Có điều bác luôn khẳng định không biết VBA, chỉ ghép nối các thứ lại mà tạo thành. Nếu thế thì thực sự trình độ ghép nối hơi bị đỉnh, có thể liên kết các code lại để tạo thành file chuẩn, dùng thường xuyên không bị lỗi. Nó giống giống cao thủ ẩn mình.
Với quan điểm chủ quan của mình, thì khiêm tốn là điều mọi người nên hướng đến, nhưng khiêm tốn đến mức phi lý thì không nên lắm.
Chúc bác thành công và giúp đỡ được mọi người theo đúng ý muốn của mình!
 
Upvote 0
Chào bác, cái này lúc bác up lên là em test luôn, giữ lên xuống mà time nó không chạy phải nói là hơi bị ức chế, nhưng em thấp cổ bé họng nên không ý kiến được. Với lại có yêu cầu ************ mở code nên phải bỏ qua luôn.
Chắc bác nghiên cứu bỏ cái kìm kẹp phím thì tiện hơn. Thêm nữa là em không thích font dạng đá chân như Times ... lắm. Màu sắc thì dễ nhìn nhưng hơi đậm quá, nhẹ nhàng tí thì hợp ý em hơn. Đây là mong muốn mang tính chất cá nhân, nếu được thì mỗi mình em khoái, còn không được thì em vẫn dùng thôi. Tùy bác nhé.
Chúc bác năm mới vui vẻ, khỏe y như bác trên!
Nếu bạn là thành viên lâu năm và thường xuyên sẽ biết VBA password của tôi luôn là HoangTrongNghia hoặc HTN chỉ 2 cái pass này đổi qua đổi lại thôi, vì thế tôi không cần phải nhắc đi nhắc lại pass. Vả lại, đó là một ứng dụng nếu dùng không được thì cứ việc bỏ qua, đừng quan tâm người ta viết cái "quỷ" gì trong đó. Nếu quan tâm thì việc hỏi xin pass thì cứ việc viết bài xin pass thôi hay phá pass (diễn đàn này đầy rẫy các thủ thuật phá pass) cũng được.

Vấn đề "giữ lên xuống mà time nó không chạy" là do tôi ngăn không cho kẹt phím, tuy nhiên nhờ bạn nhắc tôi sẽ chỉnh lại nếu Keycode là mũi tên trái phải, lên xuống sẽ bỏ qua.

Khi bạn đã có password thì bạn muốn cá nhân hóa nó thế nào là tùy bạn, tôi đã chia sẻ thì tôi cũng chẳng quan tâm chuyện xào nấu code hay thiết kế mẫu mã giao diện, chỉnh sửa hay thiết kế lại theo ý bạn cứ việc tự nhiên ha.

Còn vài tiếng nữa là hết năm rồi. Chúc mọi người vui khỏe, phát triển và thành công.
 
Upvote 0
Hôm nay cuối năm âm lịch rồi, mình gửi đáp án luôn cho trọn vẹn nè, dưới đây là code ngăn không cho kẹt phím:
Mã:
Private priBlnOneKey As Boolean
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    If priBlnOneKey Then KeyCode = 0
    priBlnOneKey = True
End Sub
Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    priBlnOneKey = False
End Sub
''TEST:
Private Sub TextBox1_Change()
    Debug.Print "Change"
End Sub
Đơn giản nhưng hiệu quả phải không quý vị?!
Chắc anh chỉ thử với các phím số?
Code của anh có một số vấn đề bất cập khi áp dụng:
  1. Nhập ký tự với phím Shift
  2. Nhập liệu tốc độ cao
  3. Không có tác dụng với một số phím khi bật chế độ gõ tiếng việt kiểu Telex
  4. Các phím điều hướng, phím Delete và phím Backspace cũng bị ảnh hưởng bởi code trong khi các phím này thường được nhấn giữ có chủ đích.
Code sau khắc phục được 3/4 vấn đề trên, riêng vấn đề với kiểu gõ Telex sẽ không có cách xử lý bằng VBA.
Mã:
Private LastKey As Long
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    If KeyAscii = LastKey Then KeyAscii = 0 Else LastKey = KeyAscii
End Sub
Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    LastKey = 0
End Sub
 
Upvote 0
Code của anh có một số vấn đề bất cập khi áp dụng:
  1. Nhập ký tự với phím Shift
  2. Nhập liệu tốc độ cao
  3. Không có tác dụng với một số phím khi bật chế độ gõ tiếng việt kiểu Telex
  4. Các phím điều hướng, phím Delete và phím Backspace cũng bị ảnh hưởng bởi code trong khi các phím này thường được nhấn giữ có chủ đích
Do code sử dụng sự kiện KeyDown, tham số là KeyCode nên có cả các phím chức năng như shift, tab, mũi tên, delete, back, ... Sử dụng sự kiện KeyPress như code của NhatAnh bài 1,411 thì tham số Key AScii không có các phím chức năng và không bị như vậy.
Về tốc độ cao thì tôi mới chỉ test code NhatAnh thấy được, chưa test code này.
 
Upvote 0
Do code sử dụng sự kiện KeyDown, tham số là KeyCode nên có cả các phím chức năng như shift, tab, mũi tên, delete, back, ... Sử dụng sự kiện KeyPress như code của NhatAnh bài 1,411 thì tham số Key AScii không có các phím chức năng và không bị như vậy.
Về tốc độ cao thì tôi mới chỉ test code NhatAnh thấy được, chưa test code này.
Khi gõ nhanh sẽ có hiện tượng nhấn phím thứ 2 trước khi nhả phím thứ nhất. Khi đó sự kiện KeyPress/ KeyDown của phím thứ 2 xảy ra trước sự kiện KeyUp của phím thứ nhất. Thuật toán ở cả 2 bài 1411 và 1420 đều "nhận diện lầm" trường hợp này với trường hợp nhấn giữ 1 phím.
Ngoài ra code ở bài 1411 không dùng cách hủy sự kiện KeyPress (gán KeyAscii = 0) mà lưu chuỗi ký tự và gán ngược lại TextBox nên nếu con trỏ đang ở giữa chuỗi ký tự sẽ bị nhảy về cuối chuỗi ký tự.
 
Upvote 0
Khi gõ nhanh sẽ có hiện tượng nhấn phím thứ 2 trước khi nhả phím thứ nhất. Khi đó sự kiện KeyPress/ KeyDown của phím thứ 2 xảy ra trước sự kiện KeyUp của phím thứ nhất. Thuật toán ở cả 2 bài 1411 và 1420 đều "nhận diện lầm" trường hợp này với trường hợp nhấn giữ 1 phím.
Ngoài ra code ở bài 1411 không dùng cách hủy sự kiện KeyPress (gán KeyAscii = 0) mà lưu chuỗi ký tự và gán ngược lại TextBox nên nếu con trỏ đang ở giữa chuỗi ký tự sẽ bị nhảy về cuối chuỗi ký tự.
Quả đúng là như vậy, trường hợp chưa up cái này đã down cái kia mình chưa nghĩ đến. Cả trường hợp edit giữa từ/ giữa chuỗi. Chỉ phát hiện ra vụ phím chức năng của KeyCode thôi.
 
Upvote 0
Chắc anh chỉ thử với các phím số?
Code của anh có một số vấn đề bất cập khi áp dụng:
  1. Nhập ký tự với phím Shift
  2. Nhập liệu tốc độ cao
  3. Không có tác dụng với một số phím khi bật chế độ gõ tiếng việt kiểu Telex
  4. Các phím điều hướng, phím Delete và phím Backspace cũng bị ảnh hưởng bởi code trong khi các phím này thường được nhấn giữ có chủ đích.
Code sau khắc phục được 3/4 vấn đề trên, riêng vấn đề với kiểu gõ Telex sẽ không có cách xử lý bằng VBA.
Mã:
Private LastKey As Long
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    If KeyAscii = LastKey Then KeyAscii = 0 Else LastKey = KeyAscii
End Sub
Private Sub TextBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    LastKey = 0
End Sub
Mặc dù tôi đã có đáp án tối ưu, nhưng đáp án của Hữu Thắng tôi nghĩ là tối ưu nhất.
 
Upvote 0
Nói thêm về code của Nghĩa sử dụng sự kiện KeyDown trong câu đố này. Nếu người dùng muốn:
- Đứng ở giữa chuỗi xóa từ đó đến hết, thì không nhấn đè phím delete được, phải delete từng ký tự
- Đứng ở giữa chuỗi xóa từ đó về đầu, thì không nhấn đè phím backspace được, cũng phải back từng ký tự.
- Nếu đứng giữa chuỗi nhấn Shift Home hoặc Shift End để xóa hết phía sau hoặc phía trước, sẽ phải đè Shift và nhấn Home hoặc End 2 lần.
- Nhấn Ctrl A để xóa hết phải nhấn Ctrl và 2 lần a.
- Muốn viết hoa 1 ký tự (thí dụ Bác) phải nhấn Shift và nhấn b 2 lần.
- Tiếng Việt "Anh By" gõ telex và cả gõ VNI: chữ ả không được, ra Bary hoặc Ba3y
- Chữ D (hoa), nhấn shift và d 1 lần không được, lần thứ 2 ra Đ. Tương tự A thành Â, O thành Ô, E thành Ê. Nếu giữa chuỗi sẽ mất khoảng trắng giữa 2 từ.
- Gõ kiểu VNI: gõ riêng rẽ ả thì được, gõ Bảy sẽ ra Ba3y
- Gõ kiểu VNI: Gõ Đánh thì dấu (số 2) gõ đâu cũng được, nhưng gõ Dành ra Da2nh. Nếu gõ Danh2 lúc ra Dành lúc không.
- ....
Nên không gọi là tối ưu.
 
Upvote 0
Đã tối ưu (bậc cao đỉnh) rồi còn có chuyện "tối ưu nhất"
Ôi thương cho tiếng Việt ngày nay...
 
Upvote 0
Đã tối ưu (bậc cao đỉnh) rồi còn có chuyện "tối ưu nhất"
Ôi thương cho tiếng Việt ngày nay...
Không chỉ tiếng Việt, các tiếng quốc tế trong các mục quảng cáo cũng dùng đại loại như "đã tuyệt vời nay còn tuyệt vời hơn" hay "sữa mẹ là tốt nhất, sữa ABC còn tốt hơn" v.v...
 
Upvote 0
Không chỉ tiếng Việt, các tiếng quốc tế trong các mục quảng cáo cũng dùng đại loại như "đã tuyệt vời nay còn tuyệt vời hơn" hay "sữa mẹ là tốt nhất, sữa ABC còn tốt hơn" v.v...
Tôi chỉ quan tâm tiếng mẹ đẻ của tôi. Tiếng gì khác không thành vấn đề.
Dân quảng cáo thì chỉ chú tâm thu hts, họ làm gì cần đếm xỉa đến văn hóa hay tình cảm. Đến đây thì bạn hoàn toàn dẫn chứng trật chỗ.
 
Upvote 0
Âu cũng đặng, ví như cơ cấu giải thưởng có nhất, nhì, ba , khuyến khích và trên tất thảy lại là giải đặc biệt đó thôi.
_)(#;
 
Upvote 0
Âu cũng đặng, ví như cơ cấu giải thưởng có nhất, nhì, ba , khuyến khích và trên tất thảy lại là giải đặc biệt đó thôi.
_)(#;
Đối với ngành toán và khoa học (tự nhiên cũng như nhân văn), người ta giành giải đặc biệt cho "giải pháp hoặc tư duy độc đáo"
Đem giải này trao cho "trên thảy" coi chừng vướng phải lỗi "không biết khuyến khích tư duy ngoài khuôn khổ lệ thường"
 
Upvote 0
…….……
 
Lần chỉnh sửa cuối:
Upvote 0
Âu cũng đặng, ví như cơ cấu giải thưởng có nhất, nhì, ba , khuyến khích và trên tất thảy lại là giải đặc biệt đó thôi.
_)(#;
Chỉ có xổ số Việt Nam sau nầy mới dùng "giải đặc biệt" là giải lớn nhất, thông thường giải đặc biệt là giải phụ và giá trị thấp hơn giải nhất nhiều
 
Upvote 0
Đố mẹo, chỉ thử độ hiểu biết VBA. Nếu bạn không cho đấy là thục dụng thì cứ coi như câu đố không liên quan gì đến thực dụng.

Có một array được khai báo như vầy:
Public mang(1 to 10)

Bây giờ làm cách nào nạp 1..10 vào 10 phần tử của mảng?

Điều kiện buộc:
Không dùng vòng lặp. Không dùng lệnh GoTo theo kiểu vòng đi vòng lại (vòng lặp tự tạo). Vầ ai cũng biết đệ quy có thể thay thế 99% trường hợp vòng lặp cho nên cũng không được dùng luôn.
Đương nhiên kiểu 10 lệnh code mang(1) = 1: mang2) = 2 :... là không được.

Điều kiện mở:
Không cần code tối ưu hay tối u gì cả. Miễn ra kết quả thôi.
Có quyền làm việc tùm lum. Muốn để lại một đống rác cũng chả ai trách. Miễn có kết quả thôi.
.
 
Lần chỉnh sửa cuối:
Upvote 0
Đố mẹo, chỉ thử độ hiểu biết VBA. Nếu bạn không cho đấy là thục dụng thì cứ coi như câu đố không liên quan gì đến thực dụng.

Có một array được khai báo như vầy:
Public mang(1 to 10)

Bây giờ làm cách nào nạp 1..10 vào 10 phần tử của mảng?

Điều kiện buộc:
Không dùng vòng lặp. Không dùng lệnh GoTo theo kiểu vòng đi vòng lại (vòng lặp tự tạo). Vầ ai cũng biết đệ quy có thể thay thế 99% trường hợp vòng lặp cho nên cũng không được dùng luôn.
Đương nhiên kiểu 10 lệnh code mang(1) = 1: mang2) = 2 :... là không được.

Điều kiện mở:
Không cần code tối ưu hay tối u gì cả. Miễn ra kết quả thôi.
Có quyền làm việc tùm lum. Muốn để lại một đống rác cũng chả ai trách. Miễn có kết quả thôi.
.
Tưởng gì bác, em khơi mào đống rác thứ 1.
Sau khi thử đi thử lại (thêm ngoặc, bỏ ngoặc, thêm phần tử, bỏ phần tử, thêm set, bỏ set, thêm redim, bỏ redim, ...) khoảng vài chục lần là nó ra.
Nhưng em không hiểu tại sao nó ra nhé.
Mã:
Public mang(1 To 10)
Sub jkl()
Dim mang()
Range("a1:a10").Value = Application.WorksheetFunction.Sequence(10)
mang() = Range("a1:a10").Value
Range("b1:b10").Value = mang()
End Sub
 
Upvote 0
Tưởng gì bác, em khơi mào đống rác thứ 1.
Sau khi thử đi thử lại (thêm ngoặc, bỏ ngoặc, thêm phần tử, bỏ phần tử, thêm set, bỏ set, thêm redim, bỏ redim, ...) khoảng vài chục lần là nó ra.
Nhưng em không hiểu tại sao nó ra nhé.
...
Khơi trật đống rác rồi. Có thấy cái gì nhét vào mang đâu mà bảo là ra:

1674754945139.png

Chấm điểm: một con nhạn bị tên.

Gợi ý: tôi đã bảo là thử độ hiểu biết. Nên tìm hiểu về biến và tầm vực của biến.
Tự tìm lấy. Nếu tôi tự dẫn bài của mình thì hóa ra là tự sướng?
 
Upvote 0
Khơi trật đống rác rồi. Có thấy cái gì nhét vào mang đâu mà bảo là ra:

View attachment 285900

Chấm điểm: một con nhạn bị tên.

Gợi ý: tôi đã bảo là thử độ hiểu biết. Nên tìm hiểu về biến và tầm vực của biến.
Tự tìm lấy. Nếu tôi tự dẫn bài của mình thì hóa ra là tự sướng?
À, mang () em cho vào B1:B10 rồi. Xong sub thì mang() về rỗng mà.
Mà mang() khi dim chắc cũng không đúng nữa, hình như nó thành mang(0 to 9) thì phải.
Sequence lại máy có máy không nữa.
Còn ý là thêm yêu cầu khác đấy, xong sub biến phải còn sống. Thế thì khó đây.

PS: bài biến của bác em xem rồi, khiếp toàn chữ là chữ. Tính em thì thích đọc truyện tranh, hình ảnh mới dễ nhớ được.
 
Upvote 0
À, mang () em cho vào B1:B10 rồi. Xong sub thì mang() về rỗng mà.
Mà mang() khi dim chắc cũng không đúng nữa, hình như nó thành mang(0 to 9) thì phải.
Sequence lại máy có máy không nữa.
Còn ý là thêm yêu cầu khác đấy, xong sub biến phải còn sống. Thế thì khó đây.

PS: bài biến của bác em xem rồi, khiếp toàn chữ là chữ. Tính em thì thích đọc truyện tranh, hình ảnh mới dễ nhớ được.
Xin lỗi trước, tôi không có ý chê hay chỉ trích cá nhân bạn.

Bạn lười đọc chữ cho nên biết các thủ thuật hạng trung (trên bắt đầu) nhưng khong có căn bản từ đầu.
Điển hình, trong code bạn dùng kỹ thuât copy range ra mảng và copy ngược lại là kỹ thuật của bậc trung. Nhưng về vấn dề cái mảng của bạn là gì thì bạn hoàn toàn không biết.

Đề bài tôi đưa ra một chiếc xe, nhờ đổ xăng cho đầy.
Bạn chỉ cho tôi thấy bạn có đổ xăng nhưng khi tôi xem lại thì xe tôi chả có giọt xăng nào.
Gợi ý: nếu bạn hiểu căn bản về biến thì đã biết bạn đổ xăng vào xe nhà của bạn. Trong suốt quá trình bạn làm việc, bạn không hề cầm đến chìa khóa xe tôi. Chưa chắc bạn đã biết nó màu xanh hay đỏ, đừng nói đến đổ xăng.
 
Upvote 0
Khơi trật đống rác rồi.
Tôi bỏ câu Dim mang(), chạy code sairoi của anh, chạy trực tiếp code của cantl, đều bị lỗi can't assign to array, nếu chỉ khai báo Public mang() không kích thước thì được. Anh cho biết vì sao? Xin cám ơn.
 
Upvote 0
Tôi bỏ câu Dim mang(), chạy code sairoi của anh, chạy trực tiếp code của cantl, đều bị lỗi can't assign to array, nếu chỉ khai báo Public mang() không kích thước thì được. Anh cho biết vì sao? Xin cám ơn.
Mảng có hai loại, fixed length và dynanic.
Mảng tôi ra trong đề bài là fixed length. Tuy chưa định kiểu (các phần tửu hiện tại là variant)
Cái phương thức của range cho phép chopy range ra mảng cho ra mảng động. Vì vậy phải bào mang() thì được.

Nhng dề bài tôi cố tình dùng mảng tĩnh để buoojc quý vị có muốn chơi copy từ reaneg thì cũng vất vả chút, chứ dễ vậy thì đố mần chi.
 
Upvote 0
Cái phương thức của range cho phép copy range ra mảng cho ra mảng động. Vì vậy phải bào mang() thì được.
Tôi bỏ phương thức copy range vì copy range cho ra mảng 2 chiều, trong khi khai báo của anh là 1 chiều. Dùng cách khác như mang = array(...), hoặc bằng evaluate("...") cũng bị lỗi can't assign. Và đã tự rút ra kết luận.
Câu tôi hỏi là theo hình anh chụp thì chạy được mà không bị lỗi?
 
Lần chỉnh sửa cuối:
Upvote 0
Đố mẹo, chỉ thử độ hiểu biết VBA. Nếu bạn không cho đấy là thục dụng thì cứ coi như câu đố không liên quan gì đến thực dụng.

Có một array được khai báo như vầy:
Public mang(1 to 10)

Bây giờ làm cách nào nạp 1..10 vào 10 phần tử của mảng?

Điều kiện buộc:
Không dùng vòng lặp. Không dùng lệnh GoTo theo kiểu vòng đi vòng lại (vòng lặp tự tạo). Vầ ai cũng biết đệ quy có thể thay thế 99% trường hợp vòng lặp cho nên cũng không được dùng luôn.
Đương nhiên kiểu 10 lệnh code mang(1) = 1: mang2) = 2 :... là không được.

Điều kiện mở:
Không cần code tối ưu hay tối u gì cả. Miễn ra kết quả thôi.
Có quyền làm việc tùm lum. Muốn để lại một đống rác cũng chả ai trách. Miễn có kết quả thôi.
.
Em chưa mở máy được để thử, không biết transpose kết hợp evaluate có giải quyết được câu này không
 
Upvote 0
...
Câu tôi hỏi là theo hình anh chụp thì chạy được mà không bị lỗi?
Hình chụp ở bài #1444 là tôi chứng minh cho tác giả bài #1443 rằng sau khi chạy sub jkl (giải pháp của tác giả), mảng vẫn trống trơn.
Bài kế có giải thích cho tác giả rằng tôi tin xăng có đổ (biên lai rõ ràng), nhưng xe nào đó chứ chẳng phải xe tôi nhờ đổ (có thấy ai đến lấy chìa khóa xe đâu).. Tức là code chạy không lỗi gì cả, nhưng code ấy chả ăn nhập gì đến mảng của tôi.
 
Lần chỉnh sửa cuối:
Upvote 0
Hình chụp ở bài #1444 là tôi chứng minh cho tác giả bài #1443 rằng sau khi chạy sub jkl (giải pháp của tác giả), mảng vẫn trống trơn.
Tôi nhìn thấy sự trống trơn, và biết nguyên nhân trống trơn rồi. Nhưng anh chạy cách nào ra thông báo trống trơn đó mà không bị lỗi can't assign?
 
Upvote 0
Tôi nhìn thấy sự trống trơn, và biết nguyên nhân trống trơn rồi. Nhưng anh chạy cách nào ra thông báo trống trơn đó mà không bị lỗi can't assign?
Dòng Public mang(1 To 10) là kiểu xác định khá rõ, chỉ còn thiếu As Long nữa là 100%.
Tuy không thể phát luôn 40 bytes cho mang nhưng cũng đủ để trình dịch phát bộ nhớ cho 10 variant.
Vì vậy, khi code sub sairoi truy cập phần tử mang(5) thì chả có vấn đề gì.
Nếu mang được khai báo là mảng động thì trình dịch chỉ để tên đó nhưng không cấp phát bộ nhớ (kết nối trễ). Luc ấy code truy cập sẽ bị lỗi. Tôi đã từng viết bài chỉ dẫn cách thủ mảng đã có địa chỉ chưa, chịu khó tìm.

Lưu ý:
Nếu mảng của tôi được khai báp là
Public mang(1 To 10) As Long
thì trình dịch sẽ cấp phát luôn một dãy 40 bytes cho biến mang; đồng thơi, VBA cũng sẽ khởi trị mặc định cho cacs phần tử là 0.
Lúc tôi chạy sub sairoi sẽ thấy msgbox trưng ra 0 chứ không phải trống trưn.
 
Lần chỉnh sửa cuối:
Upvote 0
Đố mẹo, chỉ thử độ hiểu biết VBA. Nếu bạn không cho đấy là thục dụng thì cứ coi như câu đố không liên quan gì đến thực dụng.

Có một array được khai báo như vầy:
Public mang(1 to 10)

Bây giờ làm cách nào nạp 1..10 vào 10 phần tử của mảng?

Điều kiện buộc:
Không dùng vòng lặp. Không dùng lệnh GoTo theo kiểu vòng đi vòng lại (vòng lặp tự tạo). Vầ ai cũng biết đệ quy có thể thay thế 99% trường hợp vòng lặp cho nên cũng không được dùng luôn.
Đương nhiên kiểu 10 lệnh code mang(1) = 1: mang2) = 2 :... là không được.

Điều kiện mở:
Không cần code tối ưu hay tối u gì cả. Miễn ra kết quả thôi.
Có quyền làm việc tùm lum. Muốn để lại một đống rác cũng chả ai trách. Miễn có kết quả thôi.
.
Có thể dùng hàm API CopyMemory là một giải pháp.
 
Upvote 0
Có thể dùng hàm API CopyMemory là một giải pháp.
Đúng là một giải pháp.
Cũng như đệ quy giải được hết 99% vòng lạp, dùng pointer để copy memory là một thủ thuât để chuyển trị từ mảng này sang mảng khác.

Có ai ra giải pháp gì nữa không?
(ở trên tôi cố ý khơi "đệ quy" để gợi ý cách giải của tôi - không đệ quy nhưng cũng gần như vậy)
 
Upvote 0
Dùng sự kiện change của worksheet để tạo vòng lặp được tính không anh?
Đó là cách của tôi.
Bài này vốn chôm từ câu đố bất hủ của LTHĐT.
Với C++, người ta tạo một lớp (lớp A) chứa một mảng 10 đối tượng của lớp khác (lớp B).
Trong hàm dựng của lớp B, có code dựa vào vị trí của đối tuonwgj này mà ghi trị vào vị trí tương ứng trên mảng yêu cầu.
Khi tạo đối tượng (instantiate) lớp A, hàm dựng của A sẽ tự động tạo các đối tượng lớp B; và hàm dựng của B sẽ tự dộng làm việc, đủ 10 lần cho 10 đối tượng.
Tuy cấu này vốn đố trên C++ nhưng tôi tin rằng tất cả các ngôn ngữ LTHĐT đều có cách làm tương tự. Bạn có thể thử Python xem.

Rất tiếc VBA khong phải là ngôn ngữ hướng đối tượng. Class Module của VBA khong có hàm nào được tự động gọi khi đối tượng được khởi tạo.
Vì vậy, nguyên tắc trong giải pháp của tôi là phải dựa vào một cái gfi để buộc VBA tự động chạy code ghi trị vào mảng. Chỗ dễ thấy nhất là bắt sự kiện.
Tôi chỉ nói dễ thấy, có thể còn nhiều cách đào sâu hơn mọt chút thì thấy.
Và cách của bài #1454 rõ ràng là không cần chạy tự động cái gì cả.

Mặt khác:.
Có ai còn cách khác? Ở đây không ai nói đến chuyện cách nào hay hơn cách nào. Những phương pháp tư duy khác nhau mới là quan trọng.
 
Upvote 0
Năm nay chắ quý vị Thầy/Cô ăn Tết lơn, quên ra bài tập. Chứ theo mọi năm thì mùa này tràn ngập học sinh hỏi hoặc nhờ làm giúm bài tập.

Thôi thì trong lúc bà con ngóng mỏ, đố tiếp. Câu này dễ thôi - chỉ dùng ôn lại kiến thức.

Code (có lẽ ngắn nhất) cho một hàm tính giai thừa (n!) là:

Function giaithua(n)
If n <= 1 Then giaithua = 1 Else giaithua = n * giaithua(n - 1)
End Function

Nếu tôi muốn ngắn hơn thì dùng hàm IIF:

Function giaithua(n)
giaithua =IIF( n <= 1, 1, n * giaithua(n - 1)
End Function

Có đúng không? Xin quý vị phân tích.

Bố-nợt cho các bạn chuyên Google Sheets và G Script
Bài này viết G Script ra sao?
 
Upvote 0
Tại cửa số Immediate ta có
?GiaiThua(2.5)
3.75
 
Upvote 0
Tại cửa số Immediate ta có
?GiaiThua(2.5)
3.75
Giai thừa là một hàm toán.. Dân toán không ngu, họ thừa biết chỉ nạp số nguyên dương. Nếu không biết chắc kiểu của trị được nạp vào thì họ dùng Abs(Fix(x)).
Khác với dân "huyền thoại" của GPE, tôi không theo trường phái "một hàm lo lắng đủ mọi thứ".

Vả lại, hàm giải thừa viết theo đệ quy là chỉ dùng để dạy học thôi. Sách dạy lập trình nào cũng dùng giai thừa để dạy đệ quy kèm theo câu dặn "rất kém hiệu quả".
 
Upvote 0
Thôi thì trong lúc bà con ngóng mỏ, đố tiếp.
...
Nếu tôi muốn ngắn hơn thì dùng hàm IIF:
Cũng ngóng mỏ, giương cổ nhưng không phải để giải mà chờ đến lượt.
Cấu trúc If Then Else khác hàm IIf, nên hàm IIf chạy mãi đến khi quá tải. Khác thế nào thì có hiểu nhưng khó diễn giải thành lời :p
Em chưa thử nhưng mà sao không có vòng lặp gì hết vậy nhỉ? Theo suy đoán như hàm thì 5! = 5x4 ư?
Bôi đỏ như hình rồi chạy (chạy bằng Immediate hoặc gõ = giaithua(5) ở 1 ô trên sheet. Mỗi lần nó xanh ngó coi n bằng bao nhiêu, giaithua bằng bao nhiêu. Đếm xem xanh bao nhiêu lần, cuối cùng ra kết quả là bao nhiêu.

1674894979889.png

1674895049917.png
 
Upvote 0
Cũng ngóng mỏ, giương cổ nhưng không phải để giải mà chờ đến lượt.
Cấu trúc If Then Else khác hàm IIf, nên hàm IIf chạy mãi đến khi quá tải. Khác thế nào thì có hiểu nhưng khó diễn giải thành lời :p
...
Cái lời kêu gọi JavaScript/Google Script thực ra là một gợi ý.
 
Upvote 0
Theo kinh nghiệm đoán mò của em là hàm sẽ kiểm tra các đối số có nghĩa trước rồi mới tiến hành thực thi, và đối số sẽ không gọi được chính bản thân nó. Nó sẽ tự hỏi gà trứng cái nào có trước. Tất cả chỉ là sự đoán mò thiên bẩm.

Giống như hàm if của excel (đk, đk nếu true, đk nếu false) bắt buộc cả 3 cái này phải có nghĩa, chứ N/A là tịt, nên các bác hay bổ sung thêm Iferror cho nó hoành tráng.
 
Upvote 0
Cái lời kêu gọi JavaScript/Google Script thực ra là một gợi ý.
Trong JavaScript thì viết như vầy:

Function GiaiThua(n) {
return ((n <= 1)? 1 : n*GiaiThua(n-1));
}

Trông bề ngoài thù gần với IIF bên VBA hơn, nhưng cách hoạt động thì giống IF Then Else hơn.
 
Upvote 0
Theo kinh nghiệm đoán mò của em là hàm sẽ kiểm tra các đối số có nghĩa trước rồi mới tiến hành thực thi, và đối số sẽ không gọi được chính bản thân nó. Nó sẽ tự hỏi gà trứng cái nào có trước. Tất cả chỉ là sự đoán mò thiên bẩm.

Giống như hàm if của excel (đk, đk nếu true, đk nếu false) bắt buộc cả 3 cái này phải có nghĩa, chứ N/A là tịt, nên các bác hay bổ sung thêm Iferror cho nó hoành tráng.
Kinh nghiệm đoán mò của bạn lần này đã phản bạn rồi

1674902211596.png

1674902480346.png
So sánh hai hình trên bạn sẽ thấy hàm IF của Excel chỉ tính điều kiện rồi chọn tính một trong hai biểu thức trả về chứ không tính cả hai..
 
Upvote 0
Hic, đúng là nhầm thật. Thế này mới nhớ lâu được bác ạ, chứ học từ căn bản nó không nhớ được.
Tại không chịu tự mày mò, không tự suy nghĩ, và không tự test trước khi phát biểu hoặc trước khi hỏi.
Thí dụ bài bên trên (1464) tôi không nói ra để bạn tự hiểu, nhưng bạn không nhận thấy: trong biểu thức n * giaithua(n - 1) có chữ giaithua to tổ pố, mà khi gán n = 5 vào lại chỉ là 5 * 4
Người khác lanh lợi đọc thoáng qua đã thấy, nếu mình dở hơn người ta (gọi là có khiêm tốn) thì đọc chậm lại, nghiền ngẫm từng chữ lại, 5 phút chưa ra thì 10 phút, ... Khi nào quá 1 tiếng đồng hồ vẫn chưa thấy mới phải cầu thầy để hỏi, chứ tay nhanh hơn ... thì ...
Thêm số 2 vào thì chạy được ạ.
Mã:
Function giaithua2(n)
giaithua2 = IIf(n <= 1, 1, n * giaithua(n - 1))
End Function
Tôi không tin. Bạn chụp hình cái chạy được ra xem nào? Đừng nói là bạn copy cả 2 hàm trong câu hỏi vào 1 chỗ rồi sửa thêm 2 vào 1 trong 2 hàm.
 
Lần chỉnh sửa cuối:
Upvote 0
Đừng nói là bạn copy cả 2 hàm trong câu hỏi vào 1 chỗ rồi sửa thêm 2 vào 1 trong 2 hàm
Vâng, dùng cả 2 hàm mới được, phải gọi hàm trên. Vậy nên em mới đoán mò là IIF nó cần phải có nghĩa giống IF Excel, đối số không tự gọi chính nó được, thế là tèo.
Giờ em đang dựa cột mà nghe đây. Các bác cứ chỉ bảo nhẹ nhàng là em nghe hết, đấy là cái hay của dở bác ạ.
 
Upvote 0
Khác thế nào thì có hiểu nhưng khó diễn giải thành lời
Cũng ráng nói ra thành lời như sau (nhưng không phải nói kiểu lý thuyết, mà là nói kiểu thực hành):
1. Nếu ta viết thủ tục với hàm IIf như sau:
a. Bình thường:

Mã:
Sub tinhY()
y = IIf(5 > 3, 5 , 5 / 2)
Debug.Print y
End Sub
Ta sẽ có kết quả 5, vì (5 > 3) = True
Nếu sửa IIf( 5 = 3, 5, 5/ 2), điều kiện bị False nên kết quả = 5/ 2 = 2.5
Đến đây chưa có vấn đề gì

b. Viết lại:
Mã:
Sub tinhY()
y = IIf(5 > 3, 5 , 5 / 0)
Debug.Print y
End Sub
Ta thấy (5 > 3) = True, tưởng nó ra 5, nhưng không. Nó chưa tính gì hết, thấy ngay 5 / 0 là lỗi Divided by zero, nó cóc tính nữa. Nếu thay 5 / 0 bằng 5 / "a" cũng vậy, lỗi Type Mismatch và quăng thông báo, không tính tiếp
Thay (5 = 3) = False, cũng vậy
c. Viết lại lần 2:
Mã:
Sub tinhY()
y = IIf(5 = 3, 5 / 0, 5 / 2)
Debug.Print y
End Sub
Kết quả giống như viết lại lần 1: Một trong 2 cách tính True/ False mà lỗi thì cách gì cũng không tính tiếp

Hàm IF của worksheet không phải như vậy, nên đánh đồng 2 cái với nhau là sai (không thực hành mà dám kết luận)

2. Viết code với If Then Else
a. Lỗi Else

Mã:
Sub tinhX()
If 5 > 3 Then x = 5 / 2 Else x = 5 / "a"
Debug.Print x
End Sub
x = 5 / "a" rõ ràng là lỗi, nhưng cứ ra kết quả. Nghĩa là nếu điều kiện True thì code không xét Else có lỗi hay không.
Nhưng khi thay If 5 = 3 (False), code xét Else và quăng ra thông báo lỗi
b. Lỗi Then
Mã:
Sub tinhX()
If 5 > 3 Then x = 5 / "a" Else x = 5
Debug.Print x
End Sub
Ngược với trên, lỗi Then nên True mới báo lỗi, 5 = 3 bị False, không xét Then mà xét Else (không lỗi) nên ra kết quả.

3. Tạm kết luận:
Hàm IIf bị xét lỗi trước khi tính.

Cụ thể trong hàm giaithua(n):
giaithua = IIf(n <= 1, 1, giaithua(n - 1))
vế 1: giaithua = 1, không lỗi
vế 2: giaithua = giaithua(n - 1), code phải xét xem có lỗi với n - 1 hay không.
Sau rất nhiều lần xét với n giảm 1 dần dần, vẫn chưa biết có lỗi hay không thì đã bị tràn (quá hớp)
1674916105878.png
May là bị quá hớp, chứ không nó chạy đến sáng!
 
Upvote 0
Hàm IF của worksheet không phải như vậy, nên đánh đồng 2 cái với nhau là sai (không thực hành mà dám kết luận)
Vâng bác. Đấy là trong cái rủi có cái may, trong ẩu có lý. If trong excel mà điều kiện lỗi là trả kết quả lỗi luôn, chắc tay em nhanh quá nên bình loạn luôn chứ để lâu lại quên, chiều em mới thử lại if với điều kiện bị NA hoặc DIV0 thì thấy thế.
Nhanh nhẩu đoảng. He he.
 
Upvote 0
Chả phải do nhanh nhẩu. Tôi biết có nhiều bạn sẽ vướng vấn đề này cho nên mới ra câu đố.

Lý thuyết:
Hầu hết các ngôn ngữ lập trình đều phân biệt lệnh, biểu thức, và hàm.

IF Then Else là lệnh rẽ nhánh. Nếu đúng điều kiện thì chạy tiếp tục lệnh kế tiếp Then; và nếu khong thì nhảy qua phần ấy, chạy cái lệnh kế tiếp theo những gì sau Else (hoặc nếu có ElseIF thì xét tiếp điều kiện rồi chiếu theo đó mà rẽ nhánh..
If (dk) Then
x = biểu thức a
Else
x = biểu thức b ' giả sử biểu thức b có lỗi Div/0
End If
Bên trong máy thực ra có một lệnh JMP (nhảy) sau khi xét dk sang Else. Trong khi đó, sau lệnh gán x = a sẽ có một lênh nhảy vượt qua phần Else
Vì tất cả đều là lệnh với biểu thức cho nên VBA cứ chạy theo đúng lô gic. Những lệnh và biểu thức bị nhảy qua sé không được chạy. Dẫu các lệnh ấy có lỗi run time (DIV/0 là lỗi run time) cũng chả thây; vì chúng có chạy bao giờ đâu?

Nhưng IIF là chuyện khác. Đây là một hàm. Hàm này nhận 3 tham; nó sẽ xét lô gic tham nhứ nhât và dựa theo đó mà trả về trị của tham 2 hoặc 3.
Luật của hàm là:
- Tất cả các biểu thức (biến/hàm) nạp vào các tham đều được VBA tính ra trị rồi mới nạp cho hàm.

Luật của đệ quy là:
- Trong sub/function phải có ít nhất một chỗ gội là bẫy thoát.

Thuật toán đệ quy của bài này là "nhân n giảm dần, khi n = 1 thì thoát"

Trong code
giaithua =IIF( n <= 1, 1, n * giaithua(n - 1)
3 biểu thức n <= 1; 1; và n * giaithua(n - 1) đêu được VBA tính ra trước khi nạp cho hàm IIF.
Vì vậy hàm giaithua luôn luôn được gọi,. Biểu thức lô gic n <= 1 chỉ chọn 1 hay n * giaithua(n - 1 để trả về thôi.
Việc này làm cho bẫy thoát đệ quy (n <= 1 thì hàm trả về 1 thay vì tính tiếp) mất công hiệu.
Không có bẫy thoát , hàm giaithua cứ được gọi mãi. Một lúc sau thì stack/ngăn xếp hết chỗ, và bị lỗi tràn ngăn xếp. (xem lý thuyết về tầm vực của biến để biết tại sao có cái vụ ngăn xếp dính dáng vào đây)
Tôi tin là nếu đặt Function (n As Byte); giới hạn n trong vùng khá nhỏ thì code sẽ bị lỗi tràn số trước khi tràn ngăn xếp. Biến Byte nhỏ nhất là 0, dưới 0 là nó tràn số.

Trong khi đó, với code:
If n <= 1 Then giaithua = 1 Else giaithua = n * giaithua(n - 1)
Thì bẫy thoát hoạt động dàng hoàng. Khii n xuống tới 1 thì code nhảy vọt khỏi phần Else,, giaithua không được gọi nữa, chấm dứt.

Giải thích phần bố-nợt:
Các ngôn ngữ thuộc dòng họ C có một toán tử đặc biệt: toán tử 3 vế.
Các toán tử bình thường chỉ có 1 vế (toán tử đổi dấu: -a) hay 2 vế (a + b)

Toán tử đặc biệt này gồm hai ký nhiệu là ? : (dấu hỏi và dấu 2 chấm), và có dạng ngữ pháp sau
(điều kiện)? biểu thức a : biểu thức b
Vế thứ nhất là điều kiện, thứ hai là biểu thức a, và thứ ba là biểu thức b
Toán tử này xét vế thứ nhất, nếu true thì nó tính và trả về biêu thức ở vế thứ hai, nếu false thì nó tính và trả về biểu thức ở vế thứ ba.
Hình thức bề ngoài của nó gần giống hàm IF của bảng tính Excel. Và cách hoạt động thì có thể coi như rất giống.
Điểm cần lưu ý: tuy trông có vẻ là hàm nhưng nó là một toán tử chân chính.; và sau khi vế thứ nhất, nó chỉ tính một trong hai vế còn lại. Tức là tuy có 3 biểu thức, luôn luôn chỉ có 2 được tính.
 
Upvote 0
Chả phải do nhanh nhẩu. Tôi biết có nhiều bạn sẽ vướng vấn đề này cho nên mới ra câu đố.
Vẫn bị tính là nhanh nhẩu. Nếu chịu thực hành như tôi thì dù kém thông minh chưa luận ra nguyên nhân cũng không đến nỗi kết luận sai.
Ghi chú:
Tôi không được học lý thuyết nhiều, nhưng nhờ thực hành nên tránh được vô số lỗi như vậy. Nhiều không phải là nhiều bài tập, mà là mỗi bài thực hành đi thực hành lại nhiều lần.
 
Upvote 0
Điểm chính mà các bạn cần biết ở đây là:

- Khi gọi hàm trong VBA, mọi đối số của tham đều được VBA tính ra kết quả trước khi nạo vào cho hàm chạy. Nếu hàm có N tham thì VBA tính đủ N dối số.
Việc tính từ trái sang phải hay phải sang trái thì chưa biết. Trước mắt, theo tôi thử nghiệm thì nó tính từ trái qua phải. Nhưng tôi khong thấy tài liệu nào của MS nói vậy cho nên bvaats cứ code nào dựa vào tính chất này đều phải coi chừng có ngày MS đổi lại thì sai. Nguyên tắc: cái gì MS có nói thì họ sẽ giữ lời, cái gì họ không nói nhưng bạn mò ra là chuyện của bạn, họ đổi ráng chịu.

- Hàm bảng tính Excel hoạt động khác. Đối số nào cần thiết mới tính. Có những hàm tính tất cả đối số. Nhưng cũng có những hàm có cách đi tắt của chúng: đủ rồi thì dừng.
Điển hình hàm OR, OR(biểu thức 1, biểu thức 2, ..., biểu thức N). Theo lô gic thì OR chỉ cần 1 trong số N điều kiện là true thì kết quả chung là true. Vì vậy, hàm OR chỉ tính từ trái sang phải, đến biểu thức nào True thì nó dừng lại và bỏ rơi các biểu thức còn lại.
Ngày xưa, MS làm như vậy là vì:
1. máy cần tiết kiệm
2. tránh code chạy lòng vòng
3. Lotus123 và Excel có sau Unix. Ý tưởng "đi tắt" bắt nguồn từ Unix.
 
Upvote 0
Xuân năm nay các Thầy/Cô làm biếng bài tập cho học sinh.
Giờ này GPE đối bài dữ.

Ra thử một đề bài đơn giản cho người mới tập cũng không đến nổi lúng túng:

Viết code liệt kê các ước số của mọt số nguyên.
1. muốn số nguyên thuần (Long) cũng được; mà muốn dùng số thực dạng nguyên (cho nó lớn hơn chút) cũng được. Miễn là biết cách xử lý số nguyên số thực nghiêm chỉnh.
2.muốn xuất kết quả ra kiểu nào tùy ý: mảng, chuỗi, hay trên bảng tính cũng được.
 
Upvote 0
Xuân năm nay các Thầy/Cô làm biếng bài tập cho học sinh.
Giờ này GPE đối bài dữ.

Ra thử một đề bài đơn giản cho người mới tập cũng không đến nổi lúng túng:

Viết code liệt kê các ước số của mọt số nguyên.
1. muốn số nguyên thuần (Long) cũng được; mà muốn dùng số thực dạng nguyên (cho nó lớn hơn chút) cũng được. Miễn là biết cách xử lý số nguyên số thực nghiêm chỉnh.
2.muốn xuất kết quả ra kiểu nào tùy ý: mảng, chuỗi, hay trên bảng tính cũng được.
Tôi chưa biết phải xử lý số nguyên số thực thế nào nên tôi viết đơn giản thế này nhưng đến 1 tỷ thì nó chạy lâu quá
Rich (BB code):
Function UocSo(Src As Long)
Dim Res$, i&, k&

For i = 1 To Src
    If Int(Src / i) = Src / i Then
        Res = Res & i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
End Function
 
Upvote 0
Lần chỉnh sửa cuối:
Upvote 0
Tôi chưa biết phải xử lý số nguyên số thực thế nào nên tôi viết đơn giản thế này nhưng đến 1 tỷ thì nó chạy lâu quá
Rich (BB code):
Function UocSo(Src As Long)
Dim Res$, i&, k&

For i = 1 To Src
    If Int(Src / i) = Src / i Then
        Res = Res & i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
End Function
i là ước số của Src thì Src / i là ước số của Src
 
Upvote 0
i là ước số của Src thì Src / i là ước số của Src
Túm lại sẽ có 100 ước số của 1 tỷ. Có tốn giây nào đâu mà chậm.
Mã:
Function UocSo(Src As Long)
Dim Res, i
t = Timer
For i = 1 To Sqr(Src)
    If Src Mod i = 0 Then
        Res = Res & i & ";" & Src / i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
MsgBox Timer - t
End Function

1675655985864.png
 
Upvote 0
Túm lại sẽ có 100 ước số của 1 tỷ. Có tốn giây nào đâu mà chậm.
Mã:
Function UocSo(Src As Long)
Dim Res, i
t = Timer
For i = 1 To Sqr(Src)
    If Src Mod i = 0 Then
        Res = Res & i & ";" & Src / i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
MsgBox Timer - t
End Function

View attachment 286116
Function UocSo(100) có giá trị 10 xuất hiện 2 lần
 
Upvote 0
Tôi chưa biết phải xử lý số nguyên số thực thế nào nên tôi viết đơn giản thế này nhưng đến 1 tỷ thì nó chạy lâu quá
Rich (BB code):
Function UocSo(Src As Long)
Dim Res$, i&, k&

For i = 1 To Src
    If Int(Src / i) = Src / i Then
        Res = Res & i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
End Function
Cỡ trình độ bạn mà làm vầy thì cố hơi ỷ y. Suy tính lại chút xem :p
 
Upvote 0
Chú về code ở bài #1481:
Ước số lớn hơn a/2 thì là a. Do đó không cần đi hết đến a.
res = "1"
For i = 2 To Src/2
If Src Mod i = 0 Then
Res = Res & ";" & i & ";" & Src / i
End If
Next i
funcName = res & ";" & Src

Đấy là tôi chỉnh code của bạn. Nếu tính về hiệu quả thì côn toán Mod không hiệu quả lắm.

For i = 1 To Src
i2= Src \ i ' nếu số nguyên
' i2 = Fix(Src/i) ' nếu số thực
If i * i2 = Src Then
Res = Res & ";" & i
If i <> i2 Then Res = Res & ";" & i2
If i >= i2 Then Exit For
End If
Next i

Code này còn có thể cải tiến thêm 1 bước nữa. Có ai muốn thử xem?

Chú thích: bình thuonwgf thì để chặt bỏ đuôi một số thực, người lập trình viên dùng hàm Fix, một hàm cổ điến gắn liền với lập trình hơn nửa thế kỷ.
Điểm vượt trội của ham này so với Int là Fix luôn luôn chặt bỏ khúc thập phân, khong cần biết số là cái gì. Vì vaayj, ngwoif sử dụng hàm này luôn luôn được bảo đảm kết quả là "lấy phần nguyên, bỏ phần thập phân"

Chú 2: dùng số thực chỉ nới thêm độ lớn của số thôi. Nên ghi nhớ rằng số nào cũng bị giới hạn chính xác ở 15 chữ số.
 
Upvote 0
Upvote 0
Theo tôi thì chạy đến a/2 vẫn là nhiều. Tôi chỉ chạy đến căn bậc 2 của a thôi.
Nếu dùng điểm hội tụ (a/b >= b) như code thứ 2 thì không cần biết đến a/2 hay sqrt(a)

Điểm cải tiến là: nếu a là số lẻ thì ước số của a không thể là số chẵn..

For i = 1 To a Step IIF(a And 1, 2, 1)
...

Câu hỏi phụ:
Code tren cho ra từng cặp ước số, ước số nhỏ nhất rồi đến lớn nhất, kế đó là ước số nhỏ nhì rồi đến lớn nhì,....
Vậy code thế nào để sắp xếp kết quả nhỏ đến lớn?
 
Upvote 0
Vậy code thế nào để sắp xếp kết quả nhỏ đến lớn?
Do đầu bài chỉ yêu cầu liệt kê ở bất kỳ đâu (1), cộng với yêu cầu phụ là sắp xếp (ở bất kỳ đâu theo 1) nên tôi dùng 2 chuỗi kết quả: 1 chuỗi chứa b và 1 chuỗi chứa a/b.
- Chuỗi b đang tăng, không cần sắp lại
- Chuỗi a/b thì nối chuỗi ngược
Nếu yêu cầu liệt kê chung thì cuối code nối 2 chuỗi này với nhau
 
Lần chỉnh sửa cuối:
Upvote 0
Vòng lặp + cặp xuôi ngược ==> đệ quy

1675701744924.png

Code nhảy số chẵn:

1675702028798.png
 
Upvote 0
Nếu không phải hội tụ thì là phân kỳ :p :p. Chỉ 1 chuỗi và tự sắp xếp.
Mã:
Function UocSo(Src As LongPtr)
Dim Res, i As Long, sq
sq = Sqr(Src)
If sq = fix(sq) Then Res = sq
For i = sq - 1 To 1 Step -1
    If Src Mod i = 0 Then
        Res = i & IIf(Res = "", "", ";") & Res & ";" & (Src / i)
    End If
Next
UocSo = Res
End Function
 
Upvote 0
Giải thuật hay ở chỗ sử dụng đư của kiểu chuỗi.ợc tính chất "nối hai đầu" (double ended queue) của kiểu chuỗi.
(VBA không có các kiểu sử dụng con trỏ cho nên khó thực hiện các cấu trúc dữ liệu phức tạp hơn vậy)

Nên để ý tránh các chỗ tự ép kiẻu, khi dùng lẫn lộn số thực, số nguyên, và variant. Nếu cần ép kiểu thì ép thẳng rõ rệt.
VD
For i = sq - 1 To 1 Step -1
là ép kiểu ngầm, sq - 1 là số thực, gán vapf i thì nó ép kiểu thành Long
Src / i theo nguyên tắc là phép chia số thực, kết quả là số thực. Chỉ vì pử đây chia chẵn cho nên khong thấy lỗi.
 
Upvote 0
là ép kiểu ngầm, sq - 1 là số thực, gán vapf i thì nó ép kiểu thành Long (1)
Src / i theo nguyên tắc là phép chia số thực, kết quả là số thực. Chỉ vì pử đây chia chẵn cho nên khong thấy lỗi. (2)
(1) sửa thành For i = Fix(sq) - 1 to 1 Step -1
(2) Src / i là thực, nhưng chắc chắn nguyên vì nằm trong điều kiện If Src Mod i = 0 và đã thỏa
 
Upvote 0
Kết luận.
Khi đố câu ước số trên là tôi muốn chỉ dẫn các bạn mới lập trình hai điểm:
1. Về Toán
Tác giả bài #1481 đã sáng suốt nhận ra định lý số học "nếu b là ước số của a thì cũng hiện hữu một a/b cũng là ước số của a". Chỉ cần thêm trường hợp b = a/b thồi.
2. Về thuật toán
Thuật đệ quy cho phép vòng lặp đảo nghịch thứ tự kết quả.

Bài #1494 cho thấy một thủ thuật sử dụng chuỗi để thay thế cấu trúc xếp lượt 2 đầu.
Lúc đầu tôi định phê là thủ thuật này chỉ sử dụng được cho chuỗi chứ nếu đề bài đòi mảng thì té re. Nhưng kịp nghĩ lại là mình lầm. Chuỗi có thể dùng hàm split để tách thành mảng.

Chú thích:
Nhiều ngôn ngữ có kiểu con trỏ cho nên có những cấu trúc kiểu mà VBA không làm được:
- Kiểu ngăn xếp; LIFO
- Kiểu xếp hàng, FIFO
 
Lần chỉnh sửa cuối:
Upvote 0

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

Back
Top Bottom