Giới thiệu Cơ bản về vòng lặp For . . . next

Liên hệ QC

ptm0412

Bad Excel Member
Thành viên BQT
Administrator
Tham gia
4/11/07
Bài viết
13,763
Được thích
36,257
Donate (Momo)
Donate
Giới tính
Nam
Nghề nghiệp
Consultant
Nhân có người bạn hỏi về For . . . next, nay mình xin đóng góp những gì mình biết để các bạn chưa biết xem qua.
Trong các ngôn ngữ lập trình mình biết: VBA, VB6, FoxPro, Pascal đều có các cấu trúc vòng lặp. Vòng lặp là 1 cấu trúc chương trình cho phép 1 câu lệnh hoặc 1 nhóm câu lệnh thực hiện 1 số lần có giới hạn. Giới hạn này có thể biết trước và có thể không, nhưng phải có để máy tính ngừng lại khi đủ số lần lặp ấn định trước. Giới hạn này có thể xác định bằng 1 con số cụ thể, 1 con số là kết quả của 1 phép tính, và cũng có thể là 1 điều kiện thoát ra khỏi vòng lặp.
Vòng lặp for là đơn giản và dễ sử dụng hơn so với while do vì nó giới hạn cụ thể số vòng lặp.
Thí dụ: for i = 1 to 10, for i = 1 to len(chuoiA), for i = 0 to k*2 . . .
Như vậy, dòng lệnh nào đặt giữa For và Next sẽ thực hiện n lần, kết quả của dòng lệnh đó sẽ bị thay đổi n lần. Kết quả sau lần thực hiện thứ n mới được dùng cho các dòng lệnh sau cấu trúc For này hoặc là kết quả cuối cùng.
Ta có nhận xét rằng sau 1 vòng, biến i tăng lên 1 cho đến khi bằng số lần quy định.
Vậy vấn đề căn bản của chúng ta là gì?

1. Xác định rằng bài toán phải thực hiện nhiều lần 1 phép tính mới ra kết quả.
2. Xác định số lần tính đó.
3. xác định câu lệnh nào để thực hiện sự tính toán.

THí dụ đơn giản nhất: tính giai thừa của 6:
ta biết n! = 1 x 2 x 3 x.... x n.

1. vậy là thích hợp để dùng For.
2. xác định số lần tính: ta thấy 6! có 5 bài toán nhân. Ta chọn số vòng lặp là n. ta viết for i =1 to 5
3. xác định câu lệnh thực hiện nhân:
a. Phải đặt 1 biến là kq
b. giá trị của kq là giá trị của kết quả trước đó nhân với giá trị hiện tại của i vì i tăng lên sau mỗi vòng lặp, ta lấy luôn i làm thừa số cho phép nhân.
Vậy ta có câu lệnh: kq = kq * i
Đến đây ta phải giả định rằng khi chạy vòng đầu tiên, có trục trặc gì không. Có. Có ở chỗ chưa có giá trị ban đầu của kq nên không nhân đưộc. vậy ta gán giá trị ban đầu của kq là 1:
ta viết kq = 1 ở bên trên For
Thứ hai ta giả định rằng sau 5 vòng lặp giá trị của kq là như thế nào. ta được kq = 1 * 1 * 2 * 3 * 4 * 5
số 1 đỏ là giá trị ban đầu, số 1 đen đến số 5 là 5 giá trị của i, nhân 5 lần là do ta quy định.
Không phải là 6! mà chỉ là 5!. vậy ta sửa lại For i = 1 to 6

Cuối cùng ta có vòng lặp hoàn chỉnh:

kq = 1
For i = 1 to 6
kq = kq * i
next i

Để ứng dụng bài tập này lên Excel, ta cần đưa nó vào giữa cặp Private sub và end sub. Mở 1 Worksheet mới, tại cell A1 gõ vào 1 số bất kỳ để tính giai thừa. Ta muốn kết quả nằm ở cell B1. Ta cũng muốn xem sau 1 vòng tính, giá trị của kq là bao nhiêu nằm lần lượt ở A2, A3, . . .
Bạn đừng chê cái ý muốn này (tính giai thừa trò trẻ ấy mà có gì mà xem), có ích đấy khi bạn thử ở những vòng lặp phức tạp hơn, hãy đi từ dễ đến khó.
Tạo 1 nút lệnh đặt tên là cmb1, double click vào cmb1 vào cửa sổ code chèn vào giữa sub và end sub để có 1 macro hoàn chỉnh như sau:

Private Sub Cmb1_click()
num=range("sheet1!A1").value
Range("sheet1!A1:A100").clear
kq = 1
For i = 1 to num
kq = kq * i
range("sheet1!A1").Offset(i,0).value = kq
next i
range("sheet1!B1").value = kq
end sub


Sau đó trở lại Excel, click nút lệnh xem kết quả.

chú ý range("sheet1!A1").Ofset(i,0).value = kq đặt bên trong For next nên chạy 6 lần hiện lên 6 cell, vị trí quy định bởi Offset

Còn range("sheet1!B1").value = kq đặt ngoài vòng For next nên chỉ chạy 1 lần hiện lên ở 1 cell B1.

Lần sau mình sẽ giới thiệu những thí dụ khác khó dần lên, rồi 2 vòng For lồng nhau.
 
Lần chỉnh sửa cuối:
Cảm ơn bạn... Chính tôi là người đang thắc mắc về mấy vòng lập này... Thức ra đễ hiểu nó cũng ko khó lắm (thậm chí có thể nói là quá dễ hiểu), nhưng giữa hiểu và đi đến vận dụng nhuần nhuyễn là 2 chuyện khác nhau... Vậy nên rất cần những bài tập nhỏ từ thấp lên cao đễ các bạn mới học như tôi có thể từ từ nắm vững dc cốt lõi vấn đề...
Mong rằng bạn post thêm nhiều bài hơn nữa về món này... (Có những bài tập cụ thể từ đơn giản đến.. vừa vừa... )
Chân thành cảm ơn!
ANH TUẤN
 
Upvote 0
Tôi chỉ viết được thế này thôi liệu có ra KQ như của bạn ko:
Sub Macro1()

kq = 1
For i = 1 To 6
kq = kq * i
Next i
Range("sheet1!B1").Value = kq
End Sub
Nếu dc thì những dòng lệnh # trong VD của bạn có ý nghĩa gì bạn giải thích hộ với ( trừ lệnh xóa a1:100)
 
Upvote 0
Cám ơn bạn , tất cả phải từ đơn giản đến phức tạp. bài toán lập trình phức tạp chẳng qua chỉ là tổng hợp khoa học những cái đơn giản...
 
Upvote 0
kq = 1
For i = 1 To 6
kq = kq * i
Next i
Range("sheet1!B1").Value = kq
End Sub
Có thể nói thêm 1 tí tại sao phải cần cho giá trị kq=1 ? Theo như tôi hiểu thì đó là nạp giá trị ban đầu... nhưng có 1 vài trường hợp tôi thấy các tác giả bỏ qua giai đoạn này luôn mà chạy thẳng vào vòng lập...
Bạn có thể giãi thích thêm và cho 1 vài ví dụ nào đó ko có giai đoạn nạp giá trị ban đầu ko?
ANH TUẤN
 
Upvote 0
anhtuan1066 đã viết:
Có thể nói thêm 1 tí tại sao phải cần cho giá trị kq=1 ? Theo như tôi hiểu thì đó là nạp giá trị ban đầu... nhưng có 1 vài trường hợp tôi thấy các tác giả bỏ qua giai đoạn này luôn mà chạy thẳng vào vòng lập...
Bạn có thể giãi thích thêm và cho 1 vài ví dụ nào đó ko có giai đoạn nạp giá trị ban đầu ko?
ANH TUẤN
Bạn thử bỏ kq=1 xem thế nào, nó hiểu kq là 0.
Private Sub ViDu()
num = Range("A1").Value
Range("A1:A100").Clear
'kq = 1
For i = 1 To 6
kq = kq + i
'kq = kq & Format(i, "00")
Range("A1").Offset(i, 0).Value = kq
Next i
Range("B1").Value = kq
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Trong ví dụ này thì tôi hiểu.. Ý tôi muốn tìm hiểu thêm 1 ví dụ nào đó ko cần nạp giá trị ban đầu... Các bạn hiểu ko nhỉ?
 
Upvote 0
Thì bác tính theo kiểu cấp số cộng
vd bác muốn tính tổng 1+2+...+n

vậy với For... next
bác dùng

For i = 1 to n do
kq = kq+i
next i

'xuất kết quả.

ở trên bác ko cần khởi động biến kq.

Thân.
 
Upvote 0
Uh, cảm ơn Soibien... Tôi hiểu rồi
Nhờ các bạn cho thêm 1 vài ví dụ nữa... từ thấp thấp lên đến tầm trung...
Cảm ơn trước
 
Upvote 0
Xin góp một í nho nhỏ, chớ phiền lòng!

ptm0412 đã viết:
PHP:
 Private Sub Cmb1_click()
    num=range("sheet1!A1").value
    Range("sheet1!A1:A100").clear
    kq = 1
    For i = 1 to 6
         kq = kq * i
        range("sheet1!A1").Ofset(i,0).value = kq
     next i
    range("sheet1!B1").value = kq
 end sub
Mình xin mạnh dạn góp í với PTN0412
Vì bạn truyền đạt kiến thức cho những đối tượng trình độ vỡ lòng hay cấp 1; Nên hướng dẫn cả cách tránh sai sót không đáng;
Ở đây thay vì như vậy, chúng ta nên khai báo biến kq, như sau
Dim kQ as long (Tại sao 1 chữ viết hoa, 1 chữ thường thì . . . . đã đề cập trong 1 bài nào đó hôm nay!)
Cũng như vậy, không nên khai biến i, mà nên iW (hay iZ, Ij) . . .
Những trường hợp này, nên chăng kèm tiếp đầu ngữ của loại/ kiểu biến
VD
Dim bI As Byte
Dim iK As Interger
Dim lW As Long
Dim dZj As Double
Có gì bỏ qua cho sự đường đột nha!!)*&^)
 
Lần chỉnh sửa cuối:
Upvote 0
ptm0412 đã viết:
Private Sub Cmb1_click()
num=range("sheet1!A1").value
Range("sheet1!A1:A100").clear
kq = 1
For i = 1 to 6
kq = kq * i
range("sheet1!A1").Ofset(i,0).value = kq
next i
range("sheet1!B1").value = kq
end sub
Xin lỗi vì đoạn trên có chỗ sai: ở dòng 5 xin sửa lại là : For i = 1 to num
Như vậy mới có nghĩa phù hợp với dòng 2 : lấy giá trị cho num ở cell A1.
Trong thí dụ thì tính 6! nhưng bạn có thể mở rộng ra tính cho số nguyên bất kỳ (đừng lớn quá, khoảng dưới 120)
Có nghĩa là bạn cho 1 số nguyên vào cell A1, nhấn nút lệnh, kết quả mỗi lần tính hiện ra ở A2, A3, . . ., kết quả cuối cùng hiện ra ở cell B1.
 
Upvote 0
SA_DQ đã viết:
Mình xin mạnh dạn góp í với PTN0412 (Không biết có phải tên bạn là Phan Tú Nam ? - Nhưng dù sao cũng cứ nói vậy;)

Phạm Thành Mỹ, nếu bạn muốn biết, 0412 là ngày và tháng sinh. Bạn không biết mình nhưng mình có biết bạn khi xưa trên diễn đàn Tuổi Trẻ.
SA_DQ đã viết:
Vì bạn truyền đạt kiến thức cho những đối tượng trình độ vỡ lòng hay cấp 1; Nên hướng dẫn cả cách tránh sai sót không đáng;
Mình không hiểu câu này? các dấu chấm phẩy làm mình bối rối.

SA_DQ đã viết:
Ở đây thay vì như vậy, chúng ta nên khai báo biến kq, như sau
Dim kQ as long (Tại sao 1 chữ viết hoa, 1 chữ thường thì . . . . đã đề cập trong 1 bài nào đó hôm nay!)
Cũng như vậy, không nên khai biến i, mà nên iW (hay iZ, Ij) . . .
Những trường hợp này, nên chăng kèm tiếp đầu ngữ của loại/ kiểu biến
VD
Dim bI As Byte
Dim iK As Interger
Dim lW As Long
Dim dZj As Double
Có gì bỏ qua cho sự đường đột nha!!)*&^)
Cám ơn bạn không hết, có gì mà bỏ qua. Có điều mình muốn các bạn khác tập trung vào For next nên lược giản lại, khai báo biến là vấn đề của cả project chứ không phải vấn đề của riêng For. Các thí dụ đơn giản thế này vẫn chạy bình thường chưa bị ảnh hưởng.
Thôi để mình đưa thêm thí dụ đây.
 
Upvote 0
Thí dụ cơ bản thứ hai về For

Thí dụ hôm nay cũng thuộc loại dễ: Đếm số từ trong 1 chuỗi.

1. Trước tiên bạn nghĩ về thuật toán để giải vấn đế này. Ta thấy mỗi từ ngăn cách nhau bằng 1 ký tự trống. Như vậy ta duyệt từng ký tự từ trái qua phải, nếu gặp ký tự trống thì tính là 1 từ cho đến hết chuỗi.
Như vậy phải loại trừ trường hợp do lỗi đánh máy có thể có 2, 3 ký tự trống giũa các từ. Muốn vậy ta dùng hàm trim() của Excel. Xin nói thêm rằng VBA cũng có hàm trim() nhưng trim của VBA chỉ loại những ký tự trắng ở 2 đầu chuỗi, còn trim của Excel loại luôn những ký tự trắng thừa ở giữa chuỗi. Muốn gọi hàm trim của Excel thì dùng
Mã:
Application.trim(chuoi)
Đặt tên biến cho chuỗi cần đếm từ là Mystr
Giả sử ta vẫn lấy giá trị từ cell A1 và cho kết quả vào cell B1.
Mã:
mystr = Range("sheet1!A1").Value
mystr = Application.Trim(mystr)
Range("sheet1!A2:d100").Clear

2. Xác định số lần duyệt: Duyệt từng ký tự đến hết thì số lần duyệt bằng với chiều dài chuỗi. Ta viết:
For i = 1 to len(mystr)

3. Xác định câu lệnh thực hiện:
- Đọc từng ký tự là mid(chuoi,vị trí, 1), vị trí ta có thể lấy biến i vì i tăng đúng số lần ta mong muốn
- Xét ký tự có phải ký tự trắng không là cấu trúc của câu lệnh có điều kiện if:
If mid(mystr,i,1)=" " then . .
- Nếu phải thì đếm thêm 1 từ: đặt một biến kq như thí dụ 1:
Kq = kq + 1
- Nếu không phải thì giữ nguyên kq:
Else
Kq = kq
End if
Vậy ta có vòng lặp đầy đủ như sau:

PHP:
For i = 1 to len(mystr)
If mid(mystr,i,1)=" " then 
Kq=kq+1
Else
Kq = kq
End if
Next i
Tương tự như thí dụ bài #1, bạn phải khai báo biến kq là integer hoặc gán cho kq giá trị ban đầu kq = 0.
Sau đó bạn đưa vào code hoàn chỉnh để chạy trên Excel, và giả sử bạn vẫn muốn xem kết quả từng lần chạy để nếu for chạy không đúng bạn có thể biết nó không đúng chỗ nào:
Bạn cần tạo 1 nút lệnh để thực hiện lệnh chạy giả sử có tên Cm2

Mã:
[COLOR=red]Private Sub Cm2_Click()[/COLOR]
[COLOR=red]mystr = Range("sheet1!A1").Value[/COLOR]
[COLOR=red]mystr = Application.Trim(mystr)[/COLOR]
[COLOR=red]Range("sheet1!A2:d100").Clear[/COLOR]
[COLOR=red]kq = 0[/COLOR]
[COLOR=red]For i = 1 To Len(mystr)[/COLOR]
[COLOR=red]If Mid(mystr, i, 1) = " " Then[/COLOR]
[COLOR=red]kq = kq + 1[/COLOR]
[COLOR=red]Else[/COLOR]
[COLOR=red]Kq = kq[/COLOR]
[COLOR=red]End If[/COLOR]
[COLOR=red]Range("sheet1!A1").Offset(i, 0).Value = kq[/COLOR]
[COLOR=red]Next i[/COLOR]
[COLOR=red]Range("sheet1!B1").Value = "Soá töø laø " & Str(kq)[/COLOR]
[COLOR=red]End sub[/COLOR]

Cho 1 chuỗi vào cell A1 rồi chạy thử. Hình như không đúng, thiếu 1 từ. Bạn hãy xem lại, lý do là chỉ khi gặp ký tự trắng, kq mới tăng giá trị lên 1, ký tự cuối chuỗi không phải ký tự trắng, nên kq không tăng cho từ cuối cùng. Vậy bạn có thể giải quyết bằng nhiều cách thí dụ: chọn 1 trong các cách:
- Thêm ký tự trắng vào cuối chuỗi: mystr = Range("sheet1!A1").Value & “ “
- Tăng kq lên 1 ở cuối chuỗi (ngoài vòng For, nếu trong vòng for sẽ bị tăng nhiều lần): kq=kq + 1
- Gán giá trị ban đầu kq = 1

Sau khi sửa xong bạn chạy lại, bảo đảm như ý.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Cho tôi hỏi:
-Tại sao phải cần Range("sheet1!A2:d100").Clear
-Tại sao phải cần Range("sheet1!A1").Offset(i, 0).Value = kq
Tôi đoán đây là dùng đễ liệt kê kq có dc khi i chạy.. Nhưng sao phải cần nó? Đưa kq vào thẳng B1 ko dc sao?
 
Upvote 0
anhtuan1066 đã viết:
Cho tôi hỏi:
-Tại sao phải cần Range("sheet1!A2:d100").Clear
-Tại sao phải cần Range("sheet1!A1").Offset(i, 0).Value = kq
Tôi đoán đây là dùng đễ liệt kê kq có dc khi i chạy.. Nhưng sao phải cần nó? Đưa kq vào thẳng B1 ko dc sao?
Sao bạn này hay bắt bẻ, bạn Mỹ (Hình như là his name) muốn cho bạn thấy kq sẽ là ...Muốn vậy thì phải clear trước. Còn không MsgBox kq.
Bạn ấy muốn HD cụ thể mà.
 
Upvote 0
Ko phải bắt bẽ.. trời đất ơi... Tôi ko hiểu thật mà... Ý tôi là tại sao phải cần có 2 đoạn code đó... Vì mục đích của ta là đưa kết quả vào cell B1, đúng ko? Vậy A2:D100 liên quan gì trong vụ này? Offset liên quan gì trong vụ này?
 
Upvote 0
anhtuan1066 đã viết:
Ko phải bắt bẽ.. trời đất ơi... Tôi ko hiểu thật mà... Ý tôi là tại sao phải cần có 2 đoạn code đó... Vì mục đích của ta là đưa kết quả vào cell B1, đúng ko? Vậy A2:D100 liên quan gì trong vụ này? Offset liên quan gì trong vụ này?
Tại vì mình muốn cho các bạn mới học biết được và thấy được kết quả sau mỗi vòng lặp. Trong thí dụ dễ này bạn thấy không cần, nhưng khi áp dụng vòng lặp phức tạp hơn, bạn cần biết vòng lặp bị lỗi ở chỗ nào.
Cụ thể như thí dụ đếm từ, chính là nhìn vào các cell A bạn thấy được đến vòng lặp cuối mới bị lỗi. bạn sẽ tập trung vào chỗ đó để tìm nguyên nhân.
Mình coi như thí dụ này là bài tập, sheet 1 là trang giấy nháp ấy mà.
Còn clear, là để bạn thử với nhiều số hoặc nhiều chuỗi, thử lần sau thì phải xóa kết quả lần trước cho khỏi lẫn lộn.
Mục đích thử nhiều lần là để phát hiện lỗi nếu có, nhất là các vòng lặp phức tạp có thể chỉ đúng trong phạm vi nhỏ mà sai ở phạm vi rộng hơn.
Hơn nữa, nếu có bạn nào quên đặt 1 câu lệnh đáng lẽ nằm ngoài For vào trong For, hoặc ngược lại, nhìn vào đó cũng phát hiện ra nguyên nhân.
Những điều này tớ đều ghi chú là nếu muốn nếu vẫn muốn, cảm phiền bạn xem kỹ lại.
Sau khi làm tốt rồi thì bỏ các dòng lệnh ấy đi. Anytime!
Mình đang soạn 1 bài for để đọc và tính kết quả 1 công thức dạng chuỗi thí dụ chuỗi "123+456-789" (không có dấu bằng). Mình phải làm nháp như vậy cả tuần nay mà chưa ra.
 
Lần chỉnh sửa cuối:
Upvote 0
Cảm ơn bạn... Giãi thích thế thì tôi hiểu rồi... (giống giống như khi tôi test công thức, thường hay dùng cột phụ đễ thử name và công thức mãng)... hi... hi... Rất thú vị... Tôi xem đây như là 1 cái mẹo...
Mong bạn post thêm nhiều bài khác nữa... Qua những ví dụ của bạn và các câu đố của anh SA_DQ, tôi đã tự làm dc 1 vòng lập FOR đầu tiên cho mình rồi.. ha.. ha.. Quá đã... Có thể các bạn chuyên gia trong lĩnh vực lập trình sẽ cười thầm "Ối dào, thế mà cũng mừng..." nhưng xin thưa: Cái mình chưa biết luôn là cái mới.. Và từ việc học, đến hiểu dc, rồi làm dc phải trải qua rất nhiều thử thách... Khi 1 sản phẩm đầu tiên ra lò, dù là sản phẩm ko có giá trị với mọi người đi chăng nữa, nhưng là chính tay ta làm ra nó mới cảm thấy thú vị biết dường nào... Và người dạy mình làm ra sản phẩm cũng sẽ thú vị ko kém, có phải ko? (tôi nghĩ vậy)
Một lần nữa chân thành cảm ơn sự trợ giúp của bạn!
Mến
ANH TUẤN
 
Upvote 0
Bài tập ôn

Các bạn đã xem qua 2 thí dụ, 1 về tính toán và 1 về xử lý chuỗi, tôi đã cố hướng dẫn chi tiết từng bước từ nghĩ ra thuật toán đến từng bước thực hiện, cả cách kiểm tra.
Hôm nay các bạn thử sức với 2 bài tập cũng đơn giản như vậy:

Bài 1: Cho 1 chuỗi tại cell A1, đảo ngược chuỗi từng ký tự một cho ra 1 chuỗi mới ở cell B1.

Bài 2: khó hơn 1 tí: Cho 1 chuỗi copy từ word sang gồm cả số (số nguyên thôi), dấu phân cách hàng ngàn (, hoặc . đều phải xử lý như nhau), lại còn kèm cả đơn vị, nhưng đồng VN thì ở cuối chuỗi, USD thì ở đầu chuỗi. vấn đề là bạn phải đổi thành dạng số để tính toán được trong Excel.
Gợi ý cho bạn nào làm chưa được: đọc chuỗi từng ký tự như bài #2 đến hết chuỗi, nếu là số từ 0 đến 9 thì kq = kq x 10 + trích, nếu là ký tự khác thì bỏ qua, kq = kq.

Sau dó chúng ta sẽ sang mức độ 2 là phải sử dụng kết quả trung gian.
 
Upvote 0
Câu 1:
Mã:
Sub DAO()
mystr = Range("sheet1!A1").Value
For i = 0 To Len(mystr)
Ch = Mid(mystr, Len(mystr) - i, 1)
Range("B1").Value = Range("B1").Value & Ch
Next i
End Sub
Tôi làm như trên nó ra kết quả đúng nhưng lại kèm theo lỗi "Invalid Procedure call or argument"
Là sao nhỉ? Sai ở chổ nào?
 
Upvote 0
Web KT
Back
Top Bottom