Xin giúp đỡ đổi "Function" thành "Sub"

Liên hệ QC

Cô Bé Dễ Thương

Thành viên thường trực
Tham gia
30/9/16
Bài viết
223
Được thích
48
Giới tính
Nữ
Bài toán như sau:
Ví dụ:
39014
2915
106
16
7
- Lần lượt lấy 3+9, rồi lấy 9+0, tiếp 0+1, tiếp 1+4.Nếu lớn 10 thì chỉ lấy hàng đơn vị. Được số mới là: 2915
- Và lại liên tiếp 2+9=11 lấy hàng đơn vị được 1, tiếp 9+1=10 lấy 0,tiếp 1+5=6.Ta được số mới: 106
- Tiếp tục: 1+0=1, 0+6=6 Được số mớ là 16
- Tiếp tục: 1+6=7

Được sự giúp đỡ của bác haonlh có Function (kết quả ok),như sau:
Chép hàm sau vào code của bảng tính
Function SPascal(ss$) As Long
' Tinh tong pascal
Dim lenth&, i&, tam$, s1$, s2$
lenth = Len(ss)
Do While lenth > 1
tam = "": s1 = Left(ss, 1)
For i = 2 To lenth
s2 = Mid(ss, i, 1)
tam = tam & ((Val(s1) + Val(s2)) Mod 10)
s1 = s2
Next
ss = tam
lenth = Len(ss)
Loop
SPascal = ss
End Function

Chú ý đối số trên ô nào đó phải không quá 15 chữ số. Còn nếu hơn thì phải định dạng ô đó là text (hoặc gõ dấu nháy đơn rồi nhập số).

Nay em muốn đổi chương trình này thành Sub. Các anh chị giúp em với. Vốn VBA của em hiện chưa đủ để viết lai chương trình này.
Vậy xin các anh chị có ghé qua đổi Function trên thành Sub giúp em với ạ.
Em cảm ơn chân thành!
 
tong_chuso dùng phép ép kiểu để đổi chuỗi thành số.
Các code khác dùng hàm Val. Có lẽ (đoán thôi) do hàm này là hàm "lấy số cho đến lúc hết lấy được" cho nên không hiệu quả lắm.
Tôi chỉ chưa hiểu tại sao code tong_chuso gọi hàm String để lập chuỗi (len-1) lần mà không bị mất hiệu quả. Có lẽ hàm String được viết rất tốt cho VBA?
Đúng là hàm ở bài #8 còn có thể tối ưu. Tức vẫn dùng chuỗi nhưng viết khác chút.
 
Upvote 0
Tôi không hiểu ý bạn.

Tôi muốn tính số Fibonacci vd. thứ 70. Tôi không quan tâm tới các số Fibonacci 1, 2, ..., 69, 71, 72, ... nhưng nếu code tính thêm thì cũng chả sao. Để tính số Fibonacci thứ 70 tôi chạy test1 và test2 ở bài #8. Kết quả là test1 chạy trong nháy mắt, còn test2 tôi phải giết vì đợi quá lâu.

Bạn đề nghị code trong bài #9. Bạn đã thử chạy code bài #9 với n = 70 chưa? Tôi không quan tâm tới n = 20 vì quá tầm thường. Với n = 70 bạn thử thấy code chạy hết bao lâu?
Với Function Fibo_dequi máy mình không dám thử với n=70 vì lệnh
Fibo_dequi = Fibo_dequi(n - 2) + Fibo_dequi(n - 1)
1 sẽ thành 2, 2 thành 4 và 8, 16 ... số lần xử lý tăng theo cấp số nhân, minh họa điển hình về cấp số nhân hầu như ai cũng biết bài toán nhà vua thưởng lúa cho người sáng tạo trò chơi cờ tướng (có thể là cờ vua)
Code bài #9 chỉ tính số lần gọi Fibo_dequi chứng tỏ số lần xử lý tăng nhanh hơn nhiều lần khi dùng vòng For. 2 ví dụ bài #8 cách thức xử lý khác nhau nên không thể chứng minh đệ quy chậm hơn nhiều lần so với vòng For
Private Sub DeQui(ByRef Res, ByVal Res0#, ByVal n&) bài #11 thay thế Function Fibo_dequi và tốc độ khác nhiều
 
Upvote 0
Với Function Fibo_dequi máy mình không dám thử với n=70 vì lệnh
Fibo_dequi = Fibo_dequi(n - 2) + Fibo_dequi(n - 1)
1 sẽ thành 2, 2 thành 4 và 8, 16 ... số lần xử lý tăng theo cấp số nhân, minh họa điển hình về cấp số nhân hầu như ai cũng biết bài toán nhà vua thưởng lúa cho người sáng tạo trò chơi cờ tướng (có thể là cờ vua)
Code bài #9 chỉ tính số lần gọi Fibo_dequi chứng tỏ số lần xử lý tăng nhanh hơn nhiều lần khi dùng vòng For.
Tôi viết bài chẳng qua là do bạn thôi.
Bạn viết bài #9 có vẻ như góp ý cho tôi, tôi tưởng bạn có code đệ qui siêu nhân nên thử với n = 70 xem thế nào. Hoá ra là bạn chẳng đề xuất được hàm đệ qui nào mà chạy tàm tạm được với n = 70. Tôi đã nói rõ rồi. Mục đích của tôi là so sánh code đệ qui và không đệ qui. Nếu code đệ qui mà chạy nhanh hơn hẳn code KHÔNG đệ qui thì mới có thể là một phản bác có giá trị. Tôi đang nói về khuyết điểm của đệ qui mà lị. Vậy tại sao bạn lại phàn nàn về cấp số nhân? Bạn viết đệ qui không có cấp số nhân, chạy nhanh, chạy không lỗi đi để phản bác cái gọi là KHÔNG đệ qui ưu việt hơn. Sao bạn không viết mà lại phàn nàn về code của tôi? Nếu bạn đề xuất code hiệu quả của mình thì đó mới là phản bác có giá trị.
Còn về Sub đệ qui thì tôi đã có ý kiến rồi. Ngoài những ý kiến đó ra tôi không cho là nó ưu việt hơn hàm KHÔNG đệ qui. Cùng lắm thì bằng.
Nếu bạn muốn bàn về đệ qui hay không đệ qui thì bạn cứ viết bài, góp thêm ý kiến của mình. Nhưng một khi bạn cố tình phản bác bài của tôi thì hãy viết code của mình, viết code đề nghị để test, và cho biết kết quả test thế nào. Đừng phàn nàn là tại sao code của tôi thế này hay thế khác. Hãy đề xuất code của mình và chỉ ra những ưu điểm vượt trội của nó. Nếu không chỉ ra được thì không nên phản bác bài của người khác.
2 ví dụ bài #8 cách thức xử lý khác nhau nên không thể chứng minh đệ quy chậm hơn nhiều lần so với vòng For
Thế sao bạn không đề xuất hàm đệ qui của mình để chỉ ra là nó nhanh hơn rất nhiều code KHÔNG đệ qui?
Private Sub DeQui(ByRef Res, ByVal Res0#, ByVal n&) bài #11 thay thế Function Fibo_dequi và tốc độ khác nhiều
Nhanh hơn nhưng chắc chắn không nhanh hơn VƯỢT TRỘI so với hàm KHÔNG đệ qui ở bài #8. Còn những cái mà tôi nói về SUB đệ qui thì tôi đã nói rồi.

Một lần nữa tôi nhắc lại quan điểm của mình. Không phải đệ qui luôn là mầu hồng. Không phải bao giờ đệ qui cũng tốt. Có những lúc PHẢI dùng đệ qui, có những lúc KHÔNG NÊN dùng đệ qui. Thế thôi.
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng ra thì tôi không nên dẫn "đệ quy" vào bài này.

Chủ đề nên bàn cãi hơn ở bài này là cách sử dụng khác nhau giữa Function và Sub. Và cách tách hàm con, thêm hàm mẹ,...
 
Upvote 0
Có ô A1 chứa giá trị :39014
Giải Pascal theo hình tháp:
- Lần lượt lấy 3+9, rồi lấy 9+0, tiếp 0+1, tiếp 1+4.Nếu lớn 10 thì chỉ lấy hàng đơn vị. Được số mới là: 2915
- Và lại liên tiếp 2+9=11 lấy hàng đơn vị được 1, tiếp 9+1=10 lấy 0,tiếp 1+5=6.Ta được số mới: 106
- Tiếp tục: 1+0=1, 0+6=6 Được số mớ là 16
- Tiếp tục: 1+6=7
Kết quả như sau:
39014
2915
106
16
7
Bằng các "Function" và "Sub" ở các bài trên đã giải 1 các triệt để.Phải nói là quá sảng khoái và phong phú. Quá ngưỡng mộ các anh trên diễn đàn.
Ở trên là giải theo hình tháp.Có 1 cách giải nữa là theo hàng ngang(tổng liên tiếp và tất nhiên cho kết quả khác).Cụ thể như sau:
3+9+0+1+4 = 17
1+7 = 8
Qua nay em học mót mà chưa viết được.Xin các anh chỉ cho một Sub mà ra kết quả là 8 ở ô A2.
Em chân thành cảm ơn!
 
Upvote 0
Ở trên là giải theo hình tháp.Có 1 cách giải nữa là theo hàng ngang(tổng liên tiếp và tất nhiên cho kết quả khác).Cụ thể như sau:
3+9+0+1+4 = 17
1+7 = 8
Đệ quy đơn giản này không đến nỗi chậm
PHP:
Function TongSHang(ByVal Num) As Long
Dim Len1 As Long, Temp As Long
Len1 = Len(CStr(Num))
If Len1 = 1 Then TongSHang = Num: Exit Function
For i = 1 To Len1
    Temp = Temp + Mid(Num, i, 1)
Next
If Len(CStr(Temp)) > 1 Then
    TongSHang = TongSHang(Temp)
Else
    TongSHang = Temp
End If
End Function
Ghi chú: Số lớn hơn 15 con phải chuyển về dạng text bằng cách điền dấu nháy ' ở trước
 
Upvote 0
Đệ quy đơn giản này không đến nỗi chậm
PHP:
Function TongSHang(ByVal Num) As Long
Dim Len1 As Long, Temp As Long
Len1 = Len(CStr(Num))
If Len1 = 1 Then TongSHang = Num: Exit Function
For i = 1 To Len1
    Temp = Temp + Mid(Num, i, 1)
Next
If Len(CStr(Temp)) > 1 Then
    TongSHang = TongSHang(Temp)
Else
    TongSHang = Temp
End If
End Function
Ghi chú: Số lớn hơn 15 con phải chuyển về dạng text bằng cách điền dấu nháy ' ở trước
Code này lợi dụng đặc tính Variant để đối phó với dạng của tham số hàm.
Vì chuỗi đầu tiên khó thể dài hơn trăm triệu ký tự cho nên kể từ lượt thứ nhì có thể dùng Long mà không sợ tràn số.
Tuy đây là một xảo thuật chấp nhận được ở VBA nhưng với lý thuyết đệ quy thì nó không thuần chủng (tiếng nghề: thoroughbred)
 
Upvote 0
Tôi nằm mơ thấy code như sau, nhưng không dám chắc là sẽ đúng.
Mã:
Function tong_ngang(ByVal so As String) As Double
Dim k As Long, result As Double
    If Len(so) = 0 Then Exit Function
    For k = 1 To Len(so)
        result = result + Mid(so, k, 1)
    Next k
    tong_ngang = ((result - 1) Mod 9) + 1
End Function
Có thể có rắc rối khi số có rất rất nhiều chữ số và result cực lớn. Nguyên nhân do dùng MOD (chưa nghĩ kỹ)
 
Upvote 0
Nếu so có quãng tới 14 chữ số thì code chỉ là
Mã:
Function tong_ngang(ByVal so As String) As Double
Dim result As Double
    If Len(so) Then
        result = so
        tong_ngang = ((result - 1) Mod 9) + 1
    End If
End Function
 
Upvote 0
Có thể có rắc rối khi số có rất rất nhiều chữ số và result cực lớn. Nguyên nhân do dùng MOD (chưa nghĩ kỹ)
Nếu vậy thì trong vòng lặp thay bằng result = (result + Mid(so, k, 1) - 1) Mod 9 + 1
Bài Tổng ngang này em cũng đã nghĩ đến chuyện mod 9 nhưng chưa nghĩ được là sẽ trừ 1 rồi sau đó cộng 1 như anh.
 
Upvote 0
Nếu vậy thì trong vòng lặp thay bằng result = (result + Mid(so, k, 1) - 1) Mod 9 + 1
Bài Tổng ngang này em cũng đã nghĩ đến chuyện mod 9 nhưng chưa nghĩ được là sẽ trừ 1 rồi sau đó cộng 1 như anh.
Đấy là lý thuyết thôi. Thậm chí nếu có giới hạn thì tôi nghĩ là chỉ khi so có hàng trăm triệu chữ số. Nếu số có ít chữ số hơn thì khỏi lo. Tóm lại là yên tâm đủ dùng.
Mà nếu tôi nhớ không lầm thì hàm MOD trên sheet có giới hạn. Tôi không rõ MOD trong VBA có giới hạn không.
 
Upvote 0
Đệ quy đơn giản này không đến nỗi chậm
PHP:
Function TongSHang(ByVal Num) As Long
Dim Len1 As Long, Temp As Long
Len1 = Len(CStr(Num))
If Len1 = 1 Then TongSHang = Num: Exit Function
For i = 1 To Len1
    Temp = Temp + Mid(Num, i, 1)
Next
If Len(CStr(Temp)) > 1 Then
    TongSHang = TongSHang(Temp)
Else
    TongSHang = Temp
End If
End Function
Ghi chú: Số lớn hơn 15 con phải chuyển về dạng text bằng cách điền dấu nháy ' ở trước
Dùng Sub của anh VetMini
Sub DoiTuFunction()
[a2].Value = SPascal([a1].Value)
End Sub
Kết hợp Function của anh ptm0412 batman1 thì được luôn.
Nhưng em muốn thành "Sub" giải Pascal theo hàng ngang để học xong cái bài Pascal này. Các anh có ghé quá giúp em với ạ.
Em chân thành cảm ơn!
 
Lần chỉnh sửa cuối:
Upvote 0
Này thì chuyển Function thành Sub
PHP:
Sub TongNgang()
Dim Len1 As Long, Temp As Long, Num As String
Len1 = Len([A1])
If Len1 = 1 Then [B1] = Num: Exit Sub
Num = [A1]
Do
    Len1 = Len(Num)
    For i = 1 To Len1
        Temp = Temp + Mid(Num, i, 1)
    Next
    If Len(CStr(Temp)) > 1 Then
        Num = Temp: Temp = 0
    Else
        Exit Do
    End If
Loop
[B1] = Temp
End Sub
PHP:
Sub tong_ngang()
    'Code anh batman1'
Dim k As Long, result As Double, So As String
So = [A1].Value
    If Len(So) = 0 Then Exit Sub
    For k = 1 To Len(So)
        result = result + Mid(So, k, 1)
    Next k
    [C1] = ((result - 1) Mod 9) + 1
End Sub
 
Upvote 0
Này thì chuyển Function thành Sub
...
Thớt cứ nói mãi cái chuyện "đổi function thành sub" mà luôn tảng lờ không cho biết mục đích của mình.
Phải rõ mục đích mới biết cách chuyển đổi.

Như tôi nhận xét lời tác giải bài #6, có những người học VBA đốt giai đoạn cho nên không nắm vững sự khác biệt giữa hai loại hàm con này.
Khác biệt như sau:

Nhiệm vụ và vận hành:
- Function trả về một trị. Để có thể trả về kết quả, Sub phải dùng tham byRef, hoặc dùng biến toàn cục, hoặc dùng cách ghi vào đâu đó mà không bị mất sau khi sub thoát.
- Vì có thể trả về một trị cho nên function có thể dùng làm đối tác trong biểu thức. Sub chỉ có thể làm đối tác trong một lệnh gọi sub.

Tầm vực:
- Sub không có tham số sẽ được VBE xếp vào danh sách macro; Function thì không xuất hiện trong danh sách macro.
- Sub không thể gọi thẳng trên bảng tính mà phải qua link vào macro.
- Đại khái Sub có hể coi như thủ tục trong class module và Function có thể coi như thuộc tính.

Trên căn bản sự khác biệt trên, dữ liệu mà thớt đưa ra không đủ để tiến hành công việc chuyển một Function thành Sub:
1. Cái function ban đầu có tham số. Thớt không hề cho biết qua Sub có còn nhận tham hay không?
2. Nếu không nhận tham thì có cần xuất hiện trong danh sách macro hay không?
Thực tế, nếu không cần nằm trong danh sách macro mà chỉ phải ghi kết quả vào ô nào đó thì Function cũng làm được, chả có lý do gì để đổi qua Sub.
 
Upvote 0
Thớt cứ nói mãi cái chuyện "đổi function thành sub" mà luôn tảng lờ không cho biết mục đích của mình.
Thực ra, theo tôi cách học như của tác giả là học ngược. Người ta học viết thủ tục (Sub) trước, đến sub có tham số, rồi sau cùng mới đến Function, và yêu cầu thường là chuyển từ sub thành function chứ không phải thế này.
 
Upvote 0
Giờ em mới vào cảm ơn các thầy.Dịch covit đâm ra có nhiều thời gian mày mò.Mục đích em là học vba. Sau khi thấy chỉ dùng hàm excel mà kết hơp công thức mảng hay đơ và file nặng,có lẽ lên học vba chăng?
Em nghĩ như này giống như học nhạc guita cổ điển âý. Nghe những bản nhạc hay thì thích và nghiền lắm. Nhưng ngó vào bản nhạc lại ngất. Nhưng có 1 cách học là học truyền tay mà không cần biết nốt nhạc vẫn chơi được.Tuy vất vả nhưng thích bài nào cũng chơi được.Giờ mỗi thứ biết 1 tý mà động vào vba toàn toán là toán,cũng hãi thật.
Em thấy thớt nhiều view và được các cây cổ thụ trong diễn đàn có để ý đến. Em nghĩ là "truyền tay" được nhiều đấy ạ.Các vấn đề các thầy chia sẻ quả thật rất công phu.Các vấn đề được các thầy giải quyết quá thấu, quá xuất sắc. Sao lại vào tay các thầy nó lại đơn giản vậy?
Mỗi thứ em biết có 1 chút không đến nơi đến chốn các thầy có mắng thì nhưng vẫn chỉ cho em và các bạn nhé!
Giả như em không hỏi làm sao được các thầy chỉ cho(chủ đề phải gây được hứng thú).Thiên thư vạn quyển(vba) đọc cũng không chắt ra được cô đọng như bài của các thầy đâu!
 
Lần chỉnh sửa cuối:
Upvote 0
Đã biết ví lập trình như đờn ghi ta thì cũng nên biết cách học cũng giống luôn.
Không ai có thể đọc và hỏi mà đờn ghi ta được cả - trừ phi là nhơn tài xuất chúng. Mà đã là nhơn tài xuất chúng thì đâu có lên đây hỏi.

Túm lại, học lập trình cũng giống như học đờn. Người giỏi hỏi 1 thực tập 3; người trung bình hỏi 1 thực tập 5; người kém hỏi 1 thực tập 10.

Chính tôi tự xếp mình giữa trung bình và kém. Tức tỷ lệ hỏi/thực tập là 1/7. Để đạt trình độ này tôi đã trải qua hằng ngàn giờ thực tập và hằng chục năm kinh nghiệm thực tế.
 
Upvote 0
Đã biết ví lập trình như đờn ghi ta thì cũng nên biết cách học cũng giống luôn.
Không ai có thể đọc và hỏi mà đờn ghi ta được cả - trừ phi là nhơn tài xuất chúng. Mà đã là nhơn tài xuất chúng thì đâu có lên đây hỏi.

Túm lại, học lập trình cũng giống như học đờn. Người giỏi hỏi 1 thực tập 3; người trung bình hỏi 1 thực tập 5; người kém hỏi 1 thực tập 10.

Chính tôi tự xếp mình giữa trung bình và kém. Tức tỷ lệ hỏi/thực tập là 1/7. Để đạt trình độ này tôi đã trải qua hằng ngàn giờ thực tập và hằng chục năm kinh nghiệm thực tế.
Vâng thầy!Chúng em cảm ơn thầy!
 
Lần chỉnh sửa cuối:
Upvote 0
- Cháu xin chào các chú các bác, thầy cô, anh chị và toàn thể các bạn!
- Cháu tìm được một Function tên "Tongtext"của chú @Ba Tê. Function này thật độc đáo.Nó tính được tổng các số có text.
Cháu(em) đã thử mày mò đổi qua dạng "Sub" nhưng không được, càng rối hơn.
Xin các chú các bác, thầy cô, anh chị giúp cháu(em) đổi Function này qua Sub với ạ!
(có kèm file đính kèm minh họa)
Public Function Tongtext(ByVal Rng As Range, Optional Txt As String = "*") As Double
Dim Cll As Range, Num As Double
For Each Cll In Rng
If Not IsNumeric(Cll.Value) Then
Num = Val(Replace(Left(Cll, InStr(Cll, " ") - 1), ",", "."))
Else
Num = Cll.Value
End If
If Cll.Value Like "*" & Txt & "*" Then
Tongtext = Tongtext + Num
End If
Next Cll
End Function
 

File đính kèm

  • File tinh tong so co text.xlsm
    16.1 KB · Đọc: 6
Lần chỉnh sửa cuối:
Upvote 0
- Cháu xin chào các chú các bác, thầy cô, anh chị và toàn thể các bạn!
- Cháu tìm được một Function tên "Tongtext"của chú @Ba Tê. Function này thật độc đáo.Nó tính được tổng các số có text.
Cháu(em) đã thử mày mò đổi qua dạng "Sub" nhưng không được, càng rối hơn.
Xin các chú các bác, thầy cô, anh chị giúp cháu(em) đổi Function này qua Sub với ạ!
(có kèm file đính kèm minh họa)
Public Function Tongtext(ByVal Rng As Range, Optional Txt As String = "*") As Double
Dim Cll As Range, Num As Double
For Each Cll In Rng
If Not IsNumeric(Cll.Value) Then
Num = Val(Replace(Left(Cll, InStr(Cll, " ") - 1), ",", "."))
Else
Num = Cll.Value
End If
If Cll.Value Like "*" & Txt & "*" Then
Tongtext = Tongtext + Num
End If
Next Cll
End Function
Thử như vầy xem sao:
PHP:
Public Sub s_TongText()
Const Rng As String = "C10"
Const Cols As Long = 6
Dim sArr(), dArr(), J As Long, CoL As Long, Num As Double, Tmp As Variant, Txt As String
    sArr = Range(Rng).Resize(, Cols).Value
ReDim dArr(1 To 2, 1 To Cols)
With CreateObject("Scripting.Dictionary")
    For J = 1 To Cols
        Tmp = Split(Application.WorksheetFunction.Trim(sArr(1, J)), " ")
        Txt = Tmp(1)
        Num = Val(Replace(Tmp(0), ",", "."))
        If Len(Txt) Then
            If Not .Exists(Txt) Then
                CoL = CoL + 1
                .Item(Txt) = CoL
                dArr(1, CoL) = Txt
                dArr(2, CoL) = Num
            Else
                dArr(2, .Item(Txt)) = dArr(2, .Item(Txt)) + Num
            End If
        End If
    Next J
End With
    Range(Rng).Offset(-1, Cols).Resize(2, CoL) = dArr
End Sub
 
Upvote 0
Web KT

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

Back
Top Bottom