Đây là trình tự hoặc là các mẹo để giải quyết các vấn đề(ví dụ) đó, hy vọng sẽ có ích với bạn.
1. Đọc vấn đề ít nhất 3 lần(hoặc cho đến khi nào bạn cảm thấy thoải mái)
Bạn không thể giải quyết vấn đề nếu bạn không hiểu(đừng thử vận may bằng cách sửa hàm này, sửa biến kia). Có sự khác nhau giữa vấn đề thực sự và vấn đề mà bạn nghĩ là mình đang sửa. Đọc vài line code đầu và bạn sẽ dễ dàng lâm vào sai lầm là giả định phần còn lại của nó cũng tương tự. Nếu làm một cái game nổi tiếng như Hangman, chắc chắn rằng mình đã đọc qua tất cả các rule ngay cả khi bạn đã từng chơi nó trước đó. Một lần tôi làm game giống Hangman và nhận ra rằng mình chỉ là “Hangman Quỷ Dữ” vì chỉ đọc hướng dẫn mà không đọc tất cả các rule. Vì vậy hãy đọc nốt pần code còn lại. Đừng hiểu một nửa, sẽ khiến bạn hiểu nhầm vấn đề, bỏ thêm thời gian lúc đầu này sẽ có giá trị, càng hiểu nhiều thì sẽ càng giải quyết vấn đề dễ dàng.
Giả vờ như chúng ta đang tạo một function đơn giản selectEvenNumbers
là trả về một mảng các số chẵn evenNumbers
trong một mảng hỗn hợp các con số chẵn lẻ. Nếu không có số chẵn nào cả thì trả về mảng rỗng evenNumbers
1 2 3 |
function selectEvenNumbers() { // your code here } |
Sau đây là vài câu hỏi chạy qua trong đầu tôi:
- Làm thế nào mà máy tính biết số nào là chẵn? Chia nó cho 2 và xem phần còn lại có là 0
- Tôi sẽ gán cái gì vào function này? Một array
- Mảng đó chứa cái gì? Một hoặc vài con số One or more numbers
- Kiểu dữ liệu của từng element trong array? Các con số
- Mục đích của function này là gì? Tôi sẽ trả về những gì vào cuối function? Mục tiêu là lướt qua các phần tử và chọn ra các số chẵn rồi cho nó vào array và trả về ở cuối function, nếu không có số chẵn nào thì trả về array rỗng.
2. Thử bằng tay ít nhất 3 set dữ liệu mẫu khi giải quyết vấn đề
Lấy một mớ giấy viết và thử bằng tay ít nhất 3 set dữ liệu mẫu. Thử các trường hợp góc cũng như là cạnh
Ví dụ, sau đây là một vài mẫu data có thể dùng:
1 2 3 4 5 |
[1] [1, 2] [1, 2, 3, 4, 5, 6] [-200.25] [-800.1, 2000, 3.1, -1000.25, 42, 600] |
Khi mới bắt đầu, rất dễ bị bỏ qua các bước. Bởi vì não của bạn đã quen với các số chẵn, bạn sẽ nhìn vào các dữ liệu mẫu trên và tập trung vào các số như 2
, 4
, 6
và từ đó trở đi không còn cẩn thận làm toàn bộ các bước để khắc phục vấn đề. Nếu đây là thử thách, “thà một lần đau” thử sử dụng một lương dữ liệu mẫu thật lớn để tập cho não bạn thói quen xử lý chi tiết từng bước một
Bây giờ hãy nhìn vào array [1]
- Soi vào element duy nhất của array
[1]
- Xác định nó là chẵn hoặc lẻ
- Chú ý rằng không còn element nào khác trong array
- Thấy rằng không có số chẵn nào trong array này
- Trả về array rỗng
Bây giờ hãy nhìn vào array [1, 2]
- Tập trung vào element đầu tiên của array
[1, 2]
- Nó là
1
- Xác định nó là chẵn hay lẻ
- Tập trung vào element tiếp theo trong array
- Nó là
2
- Đúng nó là chẵn rồi
- Tạo một array
evenNumbers
và thêm2
vào array này - Chú ý rằng không còn element nào trong array này
- Trả về array
evenNumbers
trong đó có[2]
Tôi làm như vậy vài lần. Chú ý là các bước tôi viết ra cho [1]
có thay đổi một chút từ [1, 2]
. Đó là lý do vì sao tôi thử với nhiều set dữ liệu mẫu khác nhau. Có vài set có 1 element, có set thì số thập phân thay vì số nguyên, một vài thì số có nhiều chữ số, một số thì là số âm…
3. Đơn giản hóa và tối ưu hóa các bước
Tìm các mẫu chung và xem có thể rút ngắn cái gì hay không, để có thể giảm một số bước lặp đi lặp lại
- Tạo một function
selectEvenNumbers
- Tạo một array rỗng
evenNumbers
để lưu các số chẵn nếu có - Lướt qua các element trong array
[1, 2]
- Tìm element đầu tiên
- Xác định nếu nó là chẵn bằng cách chia hết cho 2 rồi add nó vào
evenNumbers
- Tìm element tiếp theo
- Lặp lại bước #4
- Lặp lại bước #5 và #4 cho đến khi không còn element nào trong array
- Trả về array
evenNumbers
, không cần quan tâm có cái gì trong đó
4. Viết code giả
Ngay cả sau khi hoàn thành các bước chung chung, viết ra các mã để chuyển hóa ý tưởng sẽ giúp xác định được cấu trúc của code và làm cho việc viết code dễ dàng hơn nhiều. Viết code giả bằng tay từng dòng một. Bạn có thể làm trên giấy hoặc trên editor. Tôi đề xuất bạn viết trên giấy để tập trung tốt hơn.
Mã giả nhìn chung không thực sự có quy tắc nào cụ thể nhưng đôi lúc, tôi vẫn áp dụng các syntax từ ngôn ngữ lập trình vì tôi đủ quen thuộc với ngôn ngữ đó. Nhưng đừng quá chú trọng syntax mà hãy tập trung vào logic và các bước chạy code trên kia
Đối với ví dụ trên, có rất nhiều cách khác nhau. Ví dụ bạn có thể dùng filter
nhưng để cho đơn giản và dễ theo dõi, chúng ta dùng vòng lặp for
cơ bản(nhưng sẽ dùng filter
sau, khi cấu trúc lại code).
Đây là ví dụ của mã giả viết một cách dài dòng:
1 |
function <code class="markup--code markup--pre-code hljs">selectEvenNumbers</code> <span class="hljs-keyword">create</span> an <span class="hljs-built_in">array</span> evenNumbers <span class="hljs-keyword">and</span> <span class="hljs-keyword">set</span> that equal <span class="hljs-keyword">to</span> an <span class="hljs-keyword">empty</span> <span class="hljs-built_in">array</span> <span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> <span class="hljs-keyword">element</span> <span class="hljs-keyword">in</span> that <span class="hljs-built_in">array</span> see <span class="hljs-keyword">if</span> that <span class="hljs-keyword">element</span> <span class="hljs-keyword">is</span> even <span class="hljs-keyword">if</span> <span class="hljs-keyword">element</span> <span class="hljs-keyword">is</span> even (<span class="hljs-keyword">if</span> there <span class="hljs-keyword">is</span> a <span class="hljs-keyword">remainder</span> <span class="hljs-keyword">when</span> divided <span class="hljs-keyword">by</span> <span class="hljs-number">2</span>) <span class="hljs-keyword">add</span> <span class="hljs-keyword">to</span> that <span class="hljs-keyword">to</span> the <span class="hljs-built_in">array</span> evenNumbers <span class="hljs-keyword">return</span> evenNumbers |
Đây là ví dụ của mã giả viết một cách ngắn gọn hơn:
1 |
function selectEvenNumbers evenNumbers = [] for i = 0 to i = length of evenNumbersf if (element % 2 === 0) add to that to the array evenNumbers return evenNumbers |
Dù bằng cách nào cũng được miễn là bạn viết ra từng dòng một và hiểu logic của từng dòng
5. Chuyển mã giả sang code và debug
Sau khi xong mã giả, chuyển đổi từng dòng sang code thực bằng ngôn ngữ mà bạn đang làm việc. Tôi dùng Javascript cho ví dụ này.
Nếu bạn đã viết nó ra giấy, gõ lại nó trên editor rồi replace từng dòng
Sau đó tôi gọi function và truyền vào các dữ liệu mẫu mà chúng ta dùng lúc trước. Tôi dùng chúng để xem kết quả trả về có đúng ý muốn hay không. Bạn cũng có thể viết các test case đề kiểm tra output có đúng mong đợi hay không.
1 2 3 4 5 |
selectEvenNumbers([1]) selectEvenNumbers([1, 2]) selectEvenNumbers([1, 2, 3, 4, 5, 6]) selectEvenNumbers([-200.25]) selectEvenNumbers([-800.1, 2000, 3.1, -1000.25, 42, 600]) |
Tôi dùng console.log()
sau mỗi biến hoặc dòng để theo dõi biết hay code có hoạt động như mong đợi trước khi tiếp tục bước tiếp theo, bằng cách này tôi tóm được bất kỳ lỗi nào trước khi sai quá xa. Dưới đây là ví dụ
1 2 3 4 5 |
function selectEvenNumbers(arrayofNumbers) { let evenNumbers = [] console.log(evenNumbers) // I remove this after checking output console.log(arrayofNumbers) // I remove this after checking output } |
Sau khi bước qua từng dòng mã giả, tôi để lại //
. Còn text đậm là code thực sự bằng JavaScript.
1 2 |
// function selectEvenNumbers function selectEvenNumbers(arrayofNumbers) { |
1 2 |
// evenNumbers = [] let evenNumbers = [] |
1 2 |
// for i = 0 to i = length of evenNumbers for (var i = 0; i < arrayofNumbers.length; i++) { |
1 2 |
// <span class="hljs-keyword">if</span> (element % 2 === 0) <strong class="markup--strong markup--pre-strong"><span class="hljs-keyword">if</span> (arrayofNumbers[i] % 2 === 0) {</strong> |
1 2 3 4 |
// add to that to the array evenNumbers evenNumbers.push(arrayofNumbers[i]) } } |
1 2 |
// <span class="hljs-built_in">return</span> evenNumbers <code class="markup--code markup--pre-code hljs bash"><strong class="markup--strong markup--pre-strong"><span class="hljs-built_in"><span class="hljs-built_in">return</span></span> evenNumbers</strong></code><strong class="markup--strong markup--pre-strong"> }</strong> |
Sau đó bỏ mã giả:
1 2 3 4 5 6 7 8 |
function selectEvenNumbers(arrayofNumbers) { let evenNumbers = [] for (var i = 0; i < arrayofNumbers.length; i++) { if (arrayofNumbers[i] % 2 === 0) { evenNumbers.push(arrayofNumbers[i]) } } return evenNumbers } |
Đôi khi các developer mới vào nghề sẽ bị mắc kẹt với syntax làm cản trở việc tiến lên phía trước. Hãy nhớ rằng syntax chỉ trở nên tự nhiên và thân quen sau khi làm việc lâu năm và không có gì xấu hổ khi tra lại các tài liệu để hoàn chỉnh cú pháp sau này, khi code thực sự.
Trong ví dụ này, một cách tối ưu hóa nó bằng việc lọc ra các item từ array, trả về array mới sử dụng filter
. Theo đó, chúng ta không cần phải define một biến mới evenNumbers
bởi vì filter
sẽ trả về array mới với các bản sao của các element đã lọc phù hợp, không khác gì cách ban đầu, cũng không cần phải dùng vòng lặp for
. filter
sẽ duyệt đến từng item, trả về true
, để có element đó trong array hoặc về false
để bỏ qua nó.
1 2 3 4 |
function selectEvenNumbers(arrayofNumbers) { let evenNumbers = arrayofNumbers.filter(n => n % 2 === 0) return evenNumbers } |
7. Debug
Khi tôi tìm kiếm các bug, tôi tra từng dòng code để xem việc gì không đúng đã diễn ra. Sau đây là một số kỹ thuật tôi dùng:
- Kiềm tra console xem các thông điệp lỗi. Đôi lúc nó sẽ chỉ ra line nào và mình bắt đầu từ chỗ đó, những cũng có khi lỗi thực sự lại nằm ở line khác.
- Comment vô hiệu hóa một số dòng code và output ra những gì mình cần để nhanh chóng có được những thông tin về hành động của code.
- Dùng các dữ liệu mẫu khác và xem nó hoạt động ra sao
- Lưu thành nhiều phiên bản khác nhau và thử từng cách tiếp cận riêng biệt. Kiểu như nhiều mẫu thử trong phòng thí nghiệm để tránh đánh mất một kết quả quan trọng hoặc đúng đắn nào đó trong suốt quá trình thử nghiệm
8. Viết các comment hữu ích
Bạn không thể nào luôn nhớ được chức năng tuyệt vời của từng dòng code trong một tháng tới, và người khác lại càng không. Đó là lý do vì sao viết comment hữu ích rất quan trọng để tránh những vấn đề và tiết kiệm thời gian khi bạn quay trở lại với nó sau này.
Nhưng cũng tránh những comment như sau:
// Đây là mảng nhe. Duyệt qua nó.
// Đây là biến
Tôi cố gắng viết tóm tắt để người đọc comment có thể hiểu những gì đang diễn ra nếu nó không thực sự rõ ràng. Đặc biệt là ở những đoạn code phức tạp, giải thích rõ ràng từng function để làm gì và tại sao. Cũng nên sử dụng những tên biến, tên function và các comment rõ ràng để bạn và người khác có thể hiểu:
- Code này để làm gì?
- Code này đang làm gì?
9. Nhận phản hồi thông qua các code review
Nhận phản hồi từ các đồng nghiệp, các chuyên gia hoặc các developer khác. Xem cách mà những người khác hỏi về vấn đề đó và học từ nó. Đôi khi một vấn đề có nhiều cách tiếp cận. Làm rõ nó là gì sẽ nhanh và tốt hơn cho bạn
10. Thực hành, thực hành, thực hành
Ngay cả các developer nhiều kinh nghiệm vẫn luôn thực hành và học hỏi. Nếu nhận được phản hồi hữu ích, hãy tích hợp nó, tái hiện lại problem hoặc fix problem tương tự, thúc đẩy bạn tiến lên, mỗi problem bạn giải quyết xong là một bước tiến tốt hơn cho bạn trên con đường làm developer. Hãy ăn mừng mỗi thành công và nhớ rằng bạn đã tiến xa như thế nào.