Home Blog Page 140

Python: Cách in mà không cần dòng mới

Bài viết được sự cho phép của tác giả Phạm Văn Nguyên

Python là một trong những ngôn ngữ lập trình dễ học nhất.

Một trong những chương trình đầu tiên bạn viết khi bạn bắt đầu học bất kỳ ngôn ngữ lập trình mới nào là  chương trình hello world .

Một  chương trình hello world trong python trông như thế này

# hello world in python
print("Hello World!")

Thật dễ dàng!

Chỉ cần một dòng và tada bạn có chương trình hello world của bạn.

Trong Python 3, print () là một hàm in ra những thứ trên màn hình (print là một câu lệnh trong Python 2) .

Như bạn có thể thấy, nó là một chức năng rất đơn giản.

Tuy nhiên, có một điều thực sự khó chịu về chức năng này.

Nó tự động in một dòng mới ‘\ n’ ở cuối dòng!

Hãy xem ví dụ này

print("Hello World!")
print("My name is Nguyenpv")
# output:# Hello World!
# My name is Nguyenpv

Như bạn có thể nhận thấy, hai chuỗi không được in lần lượt từng chuỗi trên cùng một dòng mà thay vào đó là các dòng riêng biệt.

Mặc dù đây có thể là những gì bạn thực sự muốn, nhưng không phải lúc nào cũng như vậy.

nếu bạn đến từ một ngôn ngữ khác, bạn có thể thoải mái hơn khi đề cập rõ ràng liệu một dòng mới có nên được in ra hay không.

Ví dụ: trong Java, bạn phải thể hiện rõ ràng mong muốn in một dòng mới bằng cách sử dụng chức năng println hoặc nhập ký tự dòng mới (\ n) bên trong chức năng in của bạn:

// option 1
System.out.println("Hello World!")
// option 2
System.out.print("Hello World!\n")

Vậy chúng ta nên làm gì nếu chúng ta không muốn có các ký tự dòng mới trong python?

Và đây là giải pháp!

  Các vòng lặp trong Python

Giải pháp

In không có dòng mới trong Python 3

Python 3 cung cấp giải pháp đơn giản nhất, tất cả những gì bạn phải làm là cung cấp thêm một đối số cho hàm print.

# use the named argument "end" to explicitly specify the end of line string
print("Hello World!", end = '')
print("My name is Nguyenpv")
# output:
# Hello World!My name is Nguyenpv

Bạn có thể sử dụng đối số end để đề cập rõ ràng chuỗi cần được nối ở cuối dòng.

Bất cứ điều gì bạn cần làm là thêm đối số end sẽ là chuỗi kết thúc.

Vì vậy, nếu bạn cung cấp một chuỗi trống, thì sẽ không có ký tự dòng mới và không có khoảng trắng nào sẽ được thêm vào đầu vào của bạn.

Tuyển dụng lập trình viên python mới nhất

In không có dòng mới trong Python 2

Trong python 2, cách dễ nhất để tránh dòng mới kết thúc là sử dụng dấu phẩy ở cuối câu lệnh in của bạn

# no newlines but a space will be printed out
print "Hello World!",
print "My name is Nguyenpv"
# output
# Hello World! My name is Nguyenpv

Như bạn có thể thấy, mặc dù không có dòng mới, chúng tôi vẫn có một ký tự khoảng trắng giữa hai câu lệnh in.

Nếu bạn thực sự cần một không gian, thì đây là cách đơn giản nhất và đơn giản nhất để đi.

Nhưng nếu bạn muốn in mà không có dấu cách hoặc dòng mới thì sao?

Trong trường hợp này, bạn có thể sử dụng hàm sys.stdout.write từ module sys .

Hàm này sẽ chỉ in bất cứ thứ gì bạn nói rõ ràng để in.

Không có chuỗi kết thúc.

Không có phép thuật!

Hãy lấy một ví dụ

import sys

sys.stdout.write("Hello World!")
sys.stdout.write("My name is Nguyenpv")
# output
# Hello World!My name is Nguyenpv

Haiz, đoạn này thấy nhì nhằng, phức tạp hơn ngôn ngữ lập trình khác rồi đấy, ví dụ PHP, ở PHP thì chỉ cẩn dùng 1 hàm ví dụ “echo“, rồi muốn in như nào thì in  : ))

  Một số đoạn code Python phổ biến bạn nên thuộc lòng

Phần kết luận

Trong Python 3, bạn có thể sử dụng đối số end  được đặt tên  trong hàm in và gán một chuỗi trống cho đối số này để ngăn chặn dòng mới kết thúc.

Trong Python 2, bạn có thể sử dụng dấu phẩy sau tuyên bố in của bạn nếu bạn không nhớ không gian, hoặc bạn chỉ có thể sử dụng hàm  sys.stdout.write()

Chúc các bạn học tốt.

Bài viết gốc được đăng tải tại nguyenpv.com

Xem thêm các vị trí tuyển dụng IT hấp dẫn tại TopDev

Danh sách liên kết đơn và các biến thể — Singly linked list and its variants

Bài viết được sự cho phép của tác giả Hùng Lê

Danh sách liên kết (DSLK) đơn (Singly linked list) là một cấu trúc dữ liệu cơ bản và có cực kì nhiều ứng dụng. Ý tưởng của DSLK đơn khá đơn giản. Tuy nhiên, để cài đặt cấu trúc DSLK đơn một cách ngắn gọn, hiệu quả thì không phải là điều hiển nhiên. Trong bài này, chúng ta sẽ thảo luận các cách cài đặt danh sách liên kết (trong C) sao cho hiệu quả và ngắn gọn.

Cài đặt DSLK trong C không thể tránh khỏi sử dụng con trỏ (pointer) và struct. Con trỏ là một kiểu dữ liệu cực kì mạnh của ngôn ngữ C. Nó cho phép người lập trình thao tác bộ nhớ rất hiệu quả. Những lập trình viên giỏi luôn là những lập trình viên biết thao tác con trỏ một cách thuần thục. Tuy nhiên, điểm yếu của con trỏ là khó học, khó nắm bắt và cũng khó debug các chương trình có con trỏ, nhất là đối với những người lần đầu tiếp xúc với khái niệm con trỏ.

Trong bài này, mình cũng không có gắng giải thích ý nghĩa, mặc dù có nhắc lại, con trỏ. Mình khuyến khích các bạn tự tìm hiểu, vì có rất nhiều nguồn, kể cả tiếng Anh và tiếng Việt. Phần cuối bài mình sẽ liên kết một số bài viết hay về con trỏ. Mục tiêu của bài này là minh họa tối đa khả năng kì diệu của con trỏ trong thao danh sách liên kết. Các bài khác trong blog mình thường minh họa bằng giả mã. Tuy nhiên, bài này mình sẽ trực tiếp sử dụng code C.

Con trỏ và struct

Một con trỏ (pointer) là một biến dùng để lưu trữ địa chỉ của một biến khác. Biến khác ở đây có thể là một biến thông thường như biến số nguyên, biến số thực, hoặc có thể là một mảng, một hàm, và có khi cũng chính là một con trỏ khác. Vì con trỏ là một biến nên nó cũng cần phải được lưu trữ ở đâu đó trong bộ nhớ; có nghĩa là bản thân biến con trỏ cũng có một địa chỉ trong bộ nhớ. Do đó, ta có thể dùng con trỏ đề lưu địa chỉ của một con trỏ khác. Tính chất này cũng chính là sự kì diệu của con trỏ mà mình sẽ minh họa trong bài này.

Cú pháp khai báo con trỏ trong C, nói một cách đơn giản (nhưng không hoàn toàn chính xác), gồm 3 phần: (i) kiểu của biến khác mà con trỏ lưu trữ địa chỉ, (2) dấu * và (3) tên của con trỏ. Con trỏ hàm thì khai báo hơi khác một chút, nhưng đây không phải vấn đề trọng tâm của bài viết. Ví dụ, khai báo một số con trỏ:

int *a; // pointer to an integer
float *b; // pointer to a float
char *c; // pointer to character

Khai báo thì như vậy, nhưng ý nghĩa của con trỏ thì không phải lúc nào cũng nhất quán. Ví dụ con trỏ int *a vừa có thể hiểu là con trỏ tới một biến số nguyên, vừa có thể hiểu là con trỏ tới phần tử đầu tiên của một mảng số nguyên.

Struct trong C cho phép chúng ta tập hợp một hoặc một vài kiểu dữ liêu khác nhau thành một kiểu dữ liệu mới. Các kiểu dữ liệu thành phần của một struct có thể là kiểu dữ liệu sẵn có như số nguyên, số thực, con trỏ, hoặc một kiểu struct đã định nghĩa trước đó. Tóm lại, struct cho phép chúng ta “gộp” một số kiểu dữ liệu đã có lại với nhau để tiện thao tác.

Trong DSLK, struct được sử dụng để khai báo một “mắt xích” của danh sách. Ví dụ ta muốn khai báo một mắt xích của một danh sách liên kết các biến kiểu int thì ta có thể khai báo như sau:

typedef struct llnode{
    int x;
    struct llnode *next;
} llnode;

Biến  trong khai báo trên là dữ liệu của mỗi mắt xích và nó có kiểu int. Kiểu của  còn có thể là con trỏ, hay một struct. Mỗi mắt xích thường được minh họa như trong Figure 1, trong đó, mũi tên ám chỉ con trỏ lưu địa chỉ của mắt xích tiếp theo (con trỏ next). Con trỏ này trong mắt xích cuối cùng của danh sách thường là Null.
Danh sách liên kết đơn và các biến thể -- Singly linked list and its variants
Figure 1: Biểu diễn một mắt xích của danh sách liên kết.

Để khởi tạo một mắt xích, ta sẽ dùng hàm malloc (viết tắt của memory allocation). Malloc là một hàm quản lí bộ nhớ của C. Bản thân hàm này cũng khá phức tạp. Bạn đọc xem thêm tại liên kết ở cuối bài.

llnode * create_new_node(int datum){
  llnode * node = (llnode *)malloc(sizeof(llnode)); // allocate memory for a new linked list node
  node->x = datum;
  node->next = NULL;
  return node;
}

Danh sách liên kết đơn

Danh sách liên kết đơn, về mặt trực quan, là cấu trúc dữ liệu tuyến tính giống như một cái xích dài liên kết các “mắt xích” với nhau. Mỗi mắt xích có dạng khai báo llnode ở trên. Figure 2 minh họa một danh sách liên kết với 5 phần tử.

Danh sách liên kết đơn và các biến thể -- Singly linked list and its variants

Figure 2: Một danh sách liên kết với 5 phần tử.

Để theo dõi danh sách, ta sẽ dùng một con trỏ đặc biệt, gọi là con trỏ head. Con trỏ này lưu trữ địa chỉ của mắt xích đầu tiên của danh sách. Như đã nói ở trên, con trỏ của mắt xích cuối cùng của danh sách sẽ có giá trị Null. Ta có thể duyệt qua danh sách này bằng cách bắt đầu từ phần tử đầu tiên, đi theo con trỏ liên kết để đi tới nút tiếp theo. Đến khi ta gặp con trỏ Null thì ta đã duyệt xong danh sách. Đoạn code dưới đây là thủ tục walk_down để duyệt danh sách.

void walk_down(llnode *head){
   while(head->next != NULL){
      printf("%d," head->x);
      head = head->next;   // walk to the next node
  }
}

Trong đoạn code trên, liệu ta có bị mất con trỏ head? Câu trả lời là không vì mỗi lần gọi một hàm walk_down như trên, một bản “copy” của con trỏ head sẽ được tạo ra, và hàm walk_down sẽ thay đổi bản copy này. Bản gốc vẫn không thay đổi. Nếu bạn thay đổi con trỏ head trong hàm main (hàm mà bạn tạo ra con trỏ này) hoặc con trỏ head là một biến toàn cục, thì con trỏ head sẽ bị thay đổi, hay bị mất. Điều gì sẽ xảy ra nếu như ta “làm mất” con trỏ head? Bạn sẽ mất dấu của danh sách và do đó, sẽ không thể thao tác được với danh sách nữa. Phần bộ nhớ của danh sách lúc này sẽ vẫn bị chiếm bởi danh sách, do đó, tạo ra rác (garbage) trong hệ thống. Phần rác này tồn tại cho đến khi chương trình kết thúc. Bài học rút ra: khi nào bạn không còn cần danh sách nữa thì sử dụng hàm free để giải phóng bộ nhớ, đừng bao giờ để mất dấu con trỏ head vì nó sẽ tạo ra rác.

Thêm phần tử mới vào danh sách liên kết

Khi thêm phần tử mới vào danh sách, nếu bài toán không có yêu cầu gì đặc biệt, thì bạn nên thêm vào đầu của danh sách. Nếu bạn thêm vào đuôi của danh sách (theo tư duy thông thường), thì ngoài phải trả thêm bộ nhớ để lưu trữ con trỏ đuôi của danh sách (nếu không lưu con trỏ này thì sẽ phải duyệt rất tốn thời gian), bạn phải xét trường hợp khi danh sách rỗng (con trỏ đuôi là Null), i.e, thêm if-then trong code. Điều này làm code vừa chậm vừa “tối sủa” (xem code ví dụ dưới đây).

llnode * head; // head is a global variable
llnode* insert_to_tail(llnode *tail, int datum){
   llnode *node = create_new_node(datum); // create a new node
   if( tail == NULL) { // the list is empty
       tail = node;
       head = tail;
   } else{
       tail->next = node;
       tail= node;
   }
   return tail;
}

Tóm lại, luôn thêm vào đầu danh sách nếu có thể. Đoạn code sau minh họa thao tác chèn vào đầu. Rõ ràng đoạn code dưới đây đẹp hơn rất nhiều.

llnode * head; // head is a global variable
void insert_to_head(int datum){
   llnode *node = create_new_node(datum); // create a new node
   node->next = head; // dont need to care whether head is null or not
   head = node;
}

Figure 3: Các bước chèn một mắt xích có giá trị 6 vào đầu DSLK trong Figure 2.

Xóa một phần tử khỏi danh sách liên kết

Giả sử bạn muốn xóa một mắt xích có trường dữ liệu có giá trị  ra khỏi danh sách, bạn có thể làm như sau (xem minh họa trong Figure 4):

  1. Duyệt danh sách (từ đầu) để tìm ra mắt xích có giá trị . Trong quá trình duyệt, bạn sẽ lưu trữ con trỏ prev, để trỏ tới mắt xích cha của mắt xích hiện tại. Mắt xích cha được hiểu là mắt xích có con trỏ trỏ vào nút hiện tại.
  2. Cập nhật con trỏ prev để trỏ vào mắt xích con của mắt xích có dữ liệu .

Danh sách liên kết đơn và các biến thể -- Singly linked list and its variants
Figure 4: Các bước xóa mắt xích có giá trị  ra khỏi danh sách.
Code:

// Warning: this piece of code is BUGGY
void buggy_remove(int datum){
   llnode *prev = NULL;
   llnode *iterator = head;
   while(iterator->x != datum){
      prev = iterator;
      head = iterator->next;
   }
   prev->next = iterator->next;
   free(iterator); // officially remove the node having value datum
}

Đoạn code trên chỉ minh họa ý tưởng, và nó là đoạn code có bug, nghĩa là nó chỉ thành công trong một số trường hợp. Điều gì sẽ xảy ra nếu ta chạy đoạn code trên trong trường hợp mắt xích đầu tiên có giá trị datum? Khi đó đoạn code trong vòng while sẽ không thực hiện, và kết quả là prev vẫn là Null sau vòng lặp while. Đến đây bạn có thể gặp lỗi Segmentation fault. Bạn có thể sửa đổi code trên như sau:

// Warning: this piece of code is possibly buggy
void good_but_not_clean_remove( int datum){
   llnode *prev = NULL;
   llnode *iterator = head;
   while(iterator->x != datum){
      prev = iterator;
      iterator = iterator->next;
   }
   if( prev == NULL){ // datum is the head
       head = head->next;
   } else {
       prev->next = iterator->next;
   }
   free(iterator); // officially remove the node having value datum
}

Đoạn code này ok, có thể không có lỗi, nhưng trông không được “sạch sẽ” và chậm vì có if-then. Cách tốt hơn, không có if-then, đó là thao tác trên địa chỉ của con trỏ. Phương pháp này minh họa cách sử dụng con trỏ rất thông minh. Hiểu đoạn code này có thể khó nhưng một khi đã hiểu thì bạn sẽ thấy cái hay của nó.

void remove( int datum){
   llnode **iterator = &(head); // take the address of the head pointer
   while(*(iterator)->x != datum){
      iterator = &((*iterator)->next);
   }
   *iterator = (*iterator)->next;
}

Nhắc lại, toán tử & là toán tử lấy địa chỉ của một biến, còn toán tử * là toán tử lấy giá trị của biến có địa chỉ lưu trong con trỏ. Nhìn vào đoạn code trên, sự khác biệt so với đoạn code good_but_not_clean_remove là chúng ta thao tác trên địa chỉ của con trỏ, thay vì thao tác trên con trỏ. Đoạn code trên được sửa đổi từ bài viết ở đây.

Đôi khi trong một số trường hợp, ta muốn xóa một nút khỏi danh sách khi mà ta biết trước địa chỉ của nút đó. Cụ thể, ta muốn đoạn code tương tự như sau:

void remove_current_node(llnode * current_node){
   // delete currrent_node
}

Nếu chỉ đơn giản gán current_node = Null thì đoạn code trên sẽ không thực hiện đúng như mong muốn (bạn đọc nên thử và giải thích tại sao). Có hai cách để làm. Cách làm không mấy sạch sẽ là copy dữ liệu từ nút mắt xích sau đó vào nút hiện tại và ta xóa nút sau đó.

void remove_by_copy_data(llnode * current_node){
   llnode *next_node = current_node->next;
   current_node->x = next_node->x;  // copy data from next to current
   current_node->next = next_node->next;
   free(next_node); // delete next_node
}

Tuy nhiên, cách này không áp dụng được nếu current_node là mắt xích cuối cùng của DSLK (bạn đọc có thể thử để kiểm tra). Cách tốt hơn, lấy ý tưởng hàm remove ở trên sử dụng địa chỉ của con trỏ, ta sẽ truyên vào hàm địa chỉ của current_node.

void remove_by_copy_address(llnode **current_node){
   *current_node = (*current_node)->next;
}

Cách này áp dụng được ngay cả khi current_node là mắt xích cuối cùng của DSLK.

Một số biến thể của DSLK đơn

Danh sách liên kết đôi

Biến thể đầu tiên là DSLK đôi (doubly linked list), trong đó, mỗi mắt xích có 2 con trỏ. Con trỏ đầu tiên trỏ vào mắt xích trước nó trong DSLK và con trỏ thứ hai trỏ vào mắt xích sau đó trong DSLK.

typedef struct dllnode{
    int x;
        struct dllnode *prev;
    struct dllnode *next;
} dllnode;

Danh sách liên kết đơn và các biến thể -- Singly linked list and its variants
Figure 5: Một DSLK đôi với 4 mắt xích.

DSLK đôi so với DSLK đơn cần nhiều bộ nhớ hơn để lưu trữ thêm một con trỏ. Các thao tác xóa và thêm mới cũng lâu hơn vì ta còn phải cập nhật cả con trỏ prev mỗi khi ta cần xóa hoặc thêm vào DSLK. Về điểm mạnh, DSLK đôi mềm dẻo hơn vì từ một nút, ta có thể đi đến nút trước nó mà không phải duyệt từ đầu DSLK. Tính mềm dẻo này được Knuth [2] khai thác triệt để trong thiết kế cấu trúc Dancing Links, một cấu trúc dữ liệu hỗ trợ các thuật toán quay lui cho bài toán liệt kê tổ hợp.

Ngoài ra, ta có thể không cần con trỏ head để xác định nút đầu tiên của danh sách như DSLK đơn (tại sao?). Nếu con trỏ head bị mất, ta vẫn có thể tìm được phần tử đầu tiên của DSLK nếu ta được phép truy nhập đến một phần tử bất kì của DSLK. DSLK đôi cũng chính là cấu trúc đằng sau LinkedList của Java.

Danh sách liên kết vòng

Danh sách liên kết vòng (circularly linked list) là một biến thể khác của DSLK đơn, trong đó con trỏ next của mắt xích cuối cùng của danh sách trỏ vào mắt xích đầu tiên của danh sách để tạo thành một vòng tròn (xem Figure 6). Với cấu trúc này, khái niệm đầu và cuối không thực sự có ý nghĩa. Do đó, ta thường chỉ lưu một con trỏ đặc biệt để trỏ vào một nút nào đó trong danh sách.

Danh sách liên kết đơn và các biến thể -- Singly linked list and its variants

Figure 6: Một DSLK vòng với 5 mắt xích.

Danh sách liên kết vòng thường được dùng trong các ứng dụng trong đó các thành phần tham gia được thực thi theo lượt. Ví dụ trong các game đánh bài khi mỗi người chơi được phép đi một lượt theo vòng. Một bài tập hay ứng dụng danh sách liên kết vòng là bài toán Josephus.

Liên kết về con trỏ trong C

Chú ý: Phần này sẽ liên tục được cập nhật. Bạn nào có tài liệu hay về con trỏ cũng như quản lí bộ nhớ thì comment xuống dưới để mình liên kết tới bạn đọc.

Tutorial về con trỏ và ý nghĩa của con trỏ: http://www.tutorialspoint.com/cprogramming/c_pointers.htm.
Malloc làm việc như thế nào: http://www.inf.udec.cl/%7Eleo/Malloc_tutorial.pdf
Con trỏ hàm: http://www.learncpp.com/cpp-tutorial/78-function-pointers/

Tham khảo

[1] T.H Cormen, C.E. Leiserson, R. Rivest, C. Stein. Introduction to Algorithms (2nd ed.), Chapter 10 . MIT Press and McGraw-Hill (2001). ISBN 0-262-03293-7.
[2] D.E. Knuth, Donald E. Dancing links. arXiv preprint cs/0011047 (2000).

Bài viết gốc được đăng tải tại giaithuatlaptrinh.com

Có thể bạn quan tâm:

Xem thêm các việc làm Developer hấp dẫn tại TopDev

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh

Nếu bạn biết về ARC là gì và tại sao lại rò rỉ bộ nhớ trong ứng dụng IOS thì bạn có thể chuyển sang phần 2. Vì đây là 1 bài viết rất dài, đề cập đến các nội dung sau:

  1. Thế nào là rò rỉ bộ nhớ trong IOS(Memory Leaks)
  2. Tại sao lại Memory Leaks
  3. Cách mà ARC không thể giải phóng bộ nhớ
  4. Memory Leaks trong hàm Closure
  5. Giải pháp khả thi
  6. Những tình huống đặc biệt(Singleton và static Classes Memory leaks)
  7. Khác nhau giữa weak và unowned
  8. Xác định leaks sử dụng Memory Graph Debugger
  9. Một số quy tắc Thumbs

Swift sử dụng Automatic Reference Counting(ARC) để theo dõi và quản lý bộ nhớ ứng dụng. Trong đa số trường hợp, điều này nghĩa là sự quản lý chỉ làm việc trong Swift framework 1 cách tự động, và bạn không cần phải hiểu về quản lý bộ nhớ như nào. ARC tự động giải phóng bộ nhớ bởi việc khi 1 instances của class sẽ giải phóng khi không cần thiết nữa.

Phần 1: Memory leaks trong IOS

Một memory leak xảy ra khi bộ nhớ giữ chiếm giữ và không thể giải phóng bằng ARC bởi vì nó không phân biệt được thực sự có được sử dụng hay không. Và vấn đề phổ biến nhất là chúng ta tạo ra memory leaks khi tạo ra 1 vòng retained cycles – nắm giữ lẫn nhau xảy ra! Chúng ta sẽ trình bày kỹ hơn ở dưới.

Tìm việc làm ios hấp dẫn làm online tại nhà

Khi nào thì ARC không thể giải phóng bộ nhớ?

Mỗi khi bạn tạo ra 1 đối tượng(object) từ class, ARC sẽ lưu thông tin về vùng bộ nhớ mà đối tượng này sử dụng. Khi 1 object cần được giải phóng, thì làm thế nào ARC biết được điều đó. Để hiểu về vấn đề này chúng ta sẽ tìm hiểu cách ARC làm việc.

Mọi biến khởi tạo sẽ mặc định là kiểu strong. (strong là kiểu ràng buộc mạnh, weak yếu). Khi bạn tạo ra instance của nó với kiểu strong, thì ARC sẽ tăng biến đếm lên 1. Hiểu nôm na là ARC tạo ra 1 biến count = 0, khi obj tạo ra thì tăng lên 1, khi giải phóng thì trừ đi 1. Bao giờ biến count này = 0 nghĩa là nó không cần nữa, và ARC sẽ tự động giải phóng biến này ra. Cùng xem ví dụ sau:

var referenceOne: Person? = Person()

chúng ta tạo ra 1 referenceOne của lớp Person. ARC sẽ tự động cấp phát bộ nhớ cho nó kiểu strong và tăng count của biến này thành 1.

Khi khởi tạo object
var reference2 = referenceOne

Sau khi thực hiện câu lệnh trên, chúng ta tạo 1 liên kết mạnh tới object Person bằng việc gán địa chỉ sang reference2. Như bạn thấy ở hình sau, biến count = 2 do 2 thằng cùng nắm giữ địa chỉ này.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

cả 2 cùng gán tới object đã khởi tạo
referenceOne = nil

Chúng ta loại bỏ tham chiếu strong tới biến referenceOne bằng cách gán nil cho nó. Như hình sau biến count lại quay về là 1.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

reference2 = nil

Tương tự, chúng ta bỏ tham chiếu strong tới biến reference2 và hiện tại biến count = 0.

Như vậy object đã tạo không còn ai tham chiếu, ARC sẽ xóa ra khỏi bộ nhớ. Đấy là cách mà ARC làm việc.

Thế tại sao bộ nhớ lại rò rỉ?

Như chúng ta hiểu ARC sẽ tự động xóa biến khỏi bộ nhớ khi count = 0, nhưng vì lý do nào đó mà count không bao giờ = 0 được, nên không giải phóng được bộ nhớ. Hãy đọc tiếp nhé!

Khi nào ARC cố gắng kiểm tra count của 1 biến RC(reference count)

Khi bạn làm việc với cocoa hay cocoa touch thì đơn giản là nó kiểm tra khi một hàm thoát khỏi vòng lặp trên thread. Luật kiểm tra như sau:

  • Nếu không ai tham chiếu tới 1 object thì ARC giải phóng
  • Nếu 1 object không có tham chiếu mạnh tới nó.

Sau đây là ví dụ cụ thể:

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

var user: User? = User() //1 tham chiếu tới user
var todo: Todo? = Todo() //1 tham chiếu tới todo

Như hình dưới, ARC tạo ra 1 tham chiếu strong tới user và todo.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

user?.todo = todo //2 tham chiếu mạnh tới todo
todo?.associatedUser = user// 2 tham chiếu mạnh tới user

2 câu lệnh trên hoạt động như sau:

  • Tăng RC của todo lên 2, todo có 2 chủ sở hữu, 1 lần tạo ra và 1 lần gán.
  • Tăng RC của user lên 2, tương tự như trên.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

user = nil //giảm RC của user còn 1
todo = nil //giảm RC của todo còn 1

Lý tưởng nhất là khi đặt user về nil thì đáng ra RC phải là 0. Tuy nhiên lúc này trong mỗi object lại tham chiếu mạnh tới nhau nên không thể giải phóng được. Và do vậy chúng ta tạo ra strong reference cycle – vòng tròn tham chiếu mạnh lẫn nhau không thể giải phóng được.

Giải pháp

Có 2 giải pháp, dùng tham chiếu yếu weak hoặc tham chiếu unowned.

Sửa lại code như sau:

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

var user: User? = User() //RC = 1 tới user
var todo: Todo? = Todo() //RC = 1 tới todo

Câu lệnh trên sẽ tăng RC cho mỗi biến lên 1.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

user?.todo = todo //RC = 2 tới todo
todo?.associatedUser = user //RC = 1 tới user

RC = 2 tham chiếu mạnh tới todo, còn user có RC = 1 do associatedUser là tham chiếu yếu.

user = nil

Câu lệnh làm RC của user về 0.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

ARC kiểm tra và xóa user ra khỏi bộ nhớ. Vậy todo còn 1 tham chiếu mạnh tới nó.

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

todo = nil

Lúc này todo có RC = 0 và bị xóa khỏi bộ nhớ.

Quy tắc: Khi 2 đối tượng tham chiếu lẫn nhau thì biến tham chiếu trở về weak hoặc unowned.

Memory Leaks trong Closure

Memory Leak in Closure = self refers to → object refers to → self

Closure là một hàm được tạo ra từ bên trong một hàm khác (hàm cha), nó có thể sử dụng các biến toàn cục, biến cục bộ của hàm cha và biến cục bộ của chính nó. Và khi sử dụng thì nó capture(chụp lấy) các biến hay hằng và ném vào trong scope của nó.

Thế nào là capturing?

Mọi thứ bạn nên biết về Memory Leaks trong IOS (phần 1)

Cách hoạt động:

  1. Chúng ta tạo 2 biến a,b với 2 giá trị khác nhau 20 và 30.
  2. Chúng ta tạo hà someClosure và captures a, b bởi tham chiếu mạnh.
  3. Khi phương thức someMethodThatTakeClosure gọi closure và nó sẽ trả về 2 giá trị tổng của a, b captures từ hàm viewDidLoad. Giá trị là 50

Bài viết gốc được đăng tải tại codetoanbug.com

Có thể bạn quan tâm:

Xem thêm tuyển it lương cao hấp dẫn tại TopDev

Hướng dẫn inspect animation với Chrome DevTools

Bài viết được sự cho phép của tác giả Lưu Bình An

Ấn Ctrl + Shift + I để mở Chrome DevTools. Nhìn lên góc phải, chỗ có 3 dấu chấm, chọn vào More tools > Animations

Hướng dẫn inspect animation với Chrome DevTools

F5 lại trang để bắt đầu xem phân tích animation

Hướng dẫn inspect animation với Chrome DevTools

Di chuyển nút tròn màu đỏ (gọi là playhead) trên timeline đến vị trí tương ứng với thời gian của animation

Hướng dẫn inspect animation với Chrome DevTools

Mỗi element đang chạy animate sẽ được đại diện bằng một màu line khác nhau. Trên mỗi line này sẽ có các keyframe

Hướng dẫn inspect animation với Chrome DevTools

Delay và Duration

2 thay đổi này sẽ update lại css trên tab Styles

Hướng dẫn inspect animation với Chrome DevTools

Để thay đổi delay, rê chuột lên đường line đến khi nào nhìn thấy trỏ chuột chuyển thành hình bàn tay, kéo rê qua lại để thấy giá trị css được update

Hướng dẫn inspect animation với Chrome DevTools

Tăng duration của animation, rê chuột lên trên keyframe cuối cùng đến khi thấy chuyển thành mũi tên 2 đầu, kéo rê qua lại để tăng hoặc giảm thời gian.

Cubic bezier

Inspect đến element có animation, phía trước thuộc tính animation, có một ô vuông nhỏ màu tím, click vào để mở popup chỉnh cubic bezier.

Hướng dẫn inspect animation với Chrome DevTools

Có thể chọn theo preset có sẵn, hoặc dùng chuột kéo theo ý thích, sau đó copy lại css đã được thay đổi

Hướng dẫn inspect animation với Chrome DevTools

Xoay layer

Một tính năng cũng khá hay khác là visualize các layer được sử dụng, bao gồm tùy chọn xoay và xem trên nhiều góc độ để hiểu rõ hơn cách mọi thứ chạy

Để mở bảng Layer, vào More Tools > Layers

Hướng dẫn inspect animation với Chrome DevTools

Dùng công cụ này để xoay các layer theo góc độ mong muốn

Hướng dẫn inspect animation với Chrome DevTools Hướng dẫn inspect animation với Chrome DevTools

https://webdesign.tutsplus.com/articles/quick-tip-chrome-animation-dev-tools–cms-31505

Bài viết gốc được đăng tải tại vuilaptrinh.com

Có thể bạn quan tâm:

Xem thêm các việc làm Developer hấp dẫn tại TopDev

Biến toàn cục (global), biến cục bộ (local), biến nonlocal trong Python

Bài viết được sự cho phép của tác giả Nguyễn Chí Thức

Trong bài Python này bạn sẽ học về biến toàn cục (global), biến cục bộ (local), biến nonlocal trong Python và trường hợp sử dụng các biến này.

Biến toàn cục trong Python

Trong ngôn ngữ lập trình Python, một biến được khai báo bên ngoài hàm hoặc trong phạm vi toàn cục được gọi là biến toàn cục hay biến global. Biến toàn cục có thể được truy cập từ bên trong hoặc bên ngoài hàm.

Tuyển dụng lập trình python mới nhất trên TopDev

Hãy xem ví dụ về cách tạo biến toàn cục trong Python.

x = "Biến toàn cục" #khai báo biến x
#Gọi x từ trong hàm vidu()
def vidu():
    print("x trong hàm vidu() :", x)

vidu()
#Gọi x ngoài hàm vidu()
print("x ngoài hàm vidu():", x)

Trong ví dụ trên, ta khai báo biến x là biến toàn cục, và định nghĩa hàm vidu() để in biến x. Cuối cùng ta gọi hàm vidu() để in giá trị của biến x. Chạy code trên ta sẽ được kết quả là:

x trong hàm vidu(): Biến toàn cục
x ngoài hàm vidu(): Biến toàn cục

Chuyện gì sẽ xảy ra nếu bạn thay đổi giá trị của x trong hàm?

x = 2 
def vidu():
   x=x*2    
   print(x)

vidu()

Nếu chạy code này bạn sẽ nhận được thông báo lỗi:

UnboundLocalError: local variable 'x' referenced before assignment

Lỗi này xuất hiện là do Python xử lý x như một biến cục bộ và x không được định nghĩa trong vidu().

Để thay đổi biến toàn cục trong một hàm bạn sẽ phải sử dụng từ khóa global. Chúng tôi sẽ nói kỹ hơn trong bài về từ khóa global.

  Flask python là gì? - Những điều cần biết

Biến cục bộ trong Python

Biến được khai báo bên trong một hàm hoặc trong phạm vi cục bộ được gọi là biến cục bộ hay biến local.

def vidu():
 y = "Biến cục bộ"
vidu()
print(y)

Khi chạy code trên bạn sẽ nhận được thông báo lỗi:

NameError: name 'y' is not defined

Lỗi này xuất hiện là do chúng ta đã cố truy cập vào biến cục bộ y trong phạm vi toàn cục, nhưng y chỉ làm việc trong hàm vidu() hoặc phạm vi cục bộ.

Thông thường, để tạo một biến cục bộ, chúng ta sẽ khai báo nó trong một hàm như ví dụ dưới đây:

def vidu():
 y = "Biến cục bộ"
 print(y)
vidu()

Chạy code trên ta sẽ được kết quả

Biến cục bộ

Chúng ta quay trở lại xem xét vấn đề trước đó, lúc x là một biến toàn cục và chúng ta muốn thay đổi x trong vidu().

Tuyển dụng python Hồ Chí Minh mới nhất trên TopDev

Biến cục bộ và biến toàn cục

Ở đây, chúng ta sẽ học cách dùng biến cục bộ và toàn cục trong cùng một code.

x = 2

def vidu():
 global x
 y = "Biến cục bộ"
 x = x * 2
 print(x)
 print(y)
#Viết bởi uCode.vn 
vidu()

Chạy code trên ta sẽ có đầu ra:

4
Biến cục bộ

Trong code trên, chúng ta khai báo x là biến toàn cục và y là biến cục bộ trong vidu() và dùng toán tử * để thay đổi biến toàn cục và in cả giá trị của x và y. Sau khi gọi hàm vidu() giá trị của x sẽ thành 4 vì được nhân đôi.

Ví dụ sử dụng biến toàn cục và cục bộ trùng tên:

x = 5

def vidu():
 x = 10
 print("Biến x cục bộ:", x)

vidu()
print("Biến x toàn cục:", x)

Sau khi chạy code trên ta có đầu ra:

Biến x cục bộ: 10
Biến x toàn cục: 5

Trong code trên, chúng ta sử dụng cùng tên x cho cả biến cục bộ và biến toàn cục. Khi in cùng biến x chúng ta nhận được hai kết quả khác nhau vì biến được khai báo ở cả hai phạm vi, cục bộ (bên trong hàm vidu()) và toàn cục (bên ngoài hàm vidu()).

Khi chúng ta in biến trong hàm vidu() nó sẽ xuất ra Biến x cục bộ: 10, đây được gọi là phạm vi cục bộ của biến. Tương tự khi ta in biến bên ngoài hàm vidu() sẽ cho ra Biến x toàn cục: 5, đây là phạm vi toàn cục của biến.

  Tuple Python là gì? Tìm hiểu về tuple python

Biến nonlocal trong Python

Từ nonlocal này mình không biết dịch sang tiếng Việt sao cho chuẩn. Trong Python, biến nonlocal được sử dụng trong hàm lồng nhau nơi mà phạm vi cục bộ không được định nghĩa. Nói dễ hiểu thì biến nonlocal không phải biến local, không phải biến global, bạn khai báo một biến là nonlocal khi muốn sử dụng nó ở phạm vi rộng hơn local, nhưng chưa đến mức global.

Để khai báo biến nonlocal ta cần dùng đến từ khóa nonlocal.

Ví dụ:

def ham_ngoai():
    x = "Biến cục bộ"
 
    def ham_trong():
       nonlocal x
       x = "Biến nonlocal"
       print("Bên trong:", x)
 
    ham_trong()
    print("Bên ngoài:", x)

hamngoai()

Chạy code trên bạn sẽ có đầu ra:

Bên trong: Biến nonlocal
Bên ngoài: Biến nonlocal

Trong code trên có một hàm lồng là ham_trong(), ta dùng từ khóa nonlocal để tạo biến nonlocal. Hàm ham_trong() được định nghĩa trong phạm vi của ham_ngoai().

Lưu ý: Nếu chúng ta thay đổi giá trị của biến nonlocal, sự thay đổi sẽ xuất hiện trong biến cục bộ.

Bài viết gốc được đăng tải tại ucode.vn

Xem thêm các việc làm công nghệ thông tin hấp dẫn tại TopDev

Thuật toán Heap Sort

Bài viết được sự cho phép của tác giả Nguyễn Hữu Đồng

Trước khi nói tới heap, hãy nhớ lại kiến thức cây nhị phân

Cây nhị phân là một cây, mỗi nút trên cây có tối đa hai nhánh con, nút thứ i sẽ có 2 con là 2i và 2i+1.

Hướng dẫn inspect animation với Chrome DevTools

Cây nhị phân

Heap là một câu trúc cây nhị phân đầy đủ, mỗi nút trên cây đu chứa một nhãn có độ ưu tiên cao hơn các con của nó, nút gốc (root) là nút có độ ưu tiên cao nhất. Ví dụ heap min là cây là mọi con của nút i đều có giá trị >= heap[i], heap max thì mọi nút con đều nhỏ hơn nút cha.

Heap nhị phân được ứng dụng rộng rãi dùng để cài đặt một hàng đợi ưu tiên, hay là trong thuật toán Distra tìm đường đi ngắn nhất, và trong bài này ta sẽ sử dụng Binary Heap để sắp xếp mảng.

Các thao tác thường dùng trên Heap là

  • Tìm nút có độ ưu tiên cao nhất
  • Thêm một nút vào heap
  • Xóa bỏ nút gốc, nút có độ ưu tiên cao nhất.
  • Xây dựng heap từ tập có n phần tử

Để tìm nút có độ ưu tiên cao nhất, ta chỉ cần lấy nút gốc.

Để thêm một nút vào heap

  1. Nếu heap rỗng thì ta chỉ cần thay gốc bằng nút đó
  2. Nếu heap không rỗng
  3. Chọn vị trí để thêm nút.
  4. Giả sử heap có độ cao là h, và mọi nút ở độ cao h-1 có một nút nào đó chưa đủ 2 con (tổng số nút ở độ cao đó < 2^h) — thì gắn nút vào phía bên phải của nút ngoài cùng
  5. Nếu ở độ cao h đã đầy đủ nút thì thêm nút vào độ cao h+1
  6. Tiến hành vun đống nút dưới lên, nếu nút cha có độ ưu tiên thấp hơn nút con thì tiến hành đổi chỗ nút con và nút cha, sau đó lại xét tiếp nút cha đó cho tới khi nào thỏa mãn nút cha có độ ưu tiên hơn nút chon hoặc nút đang xét là nút cha thì thôi.

Để xóa nút gốc

  1. Nếu cây đó chỉ có 1 nút thì chỉ xóa nút đó
  2. Nếu cây có nhiều hơn 1 nút là tiến hành lấy nút dưới dùng bên phải thế vào nút gốc sau đó tiến hành quá trình down heap.
  3. Quá trình down heap, so sánh độ ưu tiên với cả hai nút con(nếu có) , nếu độ ưu tiên thấp hơn 1 trong 2 nút con hoặc thấp hơn cả hai nút con thì tiến hành chọn nút có độ ưu tiên cao nhất trong 2 nút con và đổi vị trị với nó, cho tới khi nào nút đang xét là nút lá ( không có nút con)

Độ phức tạp.

  • Thao tác lấy nút gốc O(1)
  • Thao tác thêm nút mới vào cây O(log N)
  • Thao tác xóa nút gốc : Tổng O(log N)
  • Thao tác xóa nút gốc O(1)
  • Thao tác down heap O(log N)

Áp dụng tính chất của Heap để sắp xếp

Trong phần này mình sẽ triển khai thuật toán sắp xếp nhanh bằng cách sử dụng tính chất, nút gốc là nút có độ ưu tiên cao nhất, mình sẽ xây dựng một heap từ mảng a có n phần tử sau đó, lấy lần lượt các phần tử trong heap ra thì mình có có các giá trị lấy ra có độ ưu tiên giảm dần.

Ngôn ngữ thực hiện. : Go

Cấu trúc Heap : struct Heap có field Leng, là độ dài của Heap và field Value là giá trị của các phần tử trong heap.

Thao tác khởi tạo Heap : Mình sẽ xây dựng heap Min thiết lập Leng = 0, heap rỗng

Thao tác thêm một phần tử vào Heap : Thêm vào và sau đó vun đống từ dưới lên qua function UpHeap.

Thao tác UpHeap

Thao tác xóa nút gốc : Sau khi loại bỏ nút gốc, ta lấy phần tử ngoài cùng ở độ cao cao nhất thế vào nút gốc sau đó thực hiện quá trình DownHeap từ nút gốc

Thao tác DownHeap

Hàm Main : Tạo mảng a sau đó đẩy mỗi phần tử của a vào Heap, sau đó lấy trong heap ra dần dần ta có kết quả theo độ ưu tiên giảm giần

Kết Quả

Full file heap.go

Thuật toán Heap Sort ứng dụng tính chất khá đơn giản của Heap nhưng nếu là mình khi sort thì mình khi sort mình sẽ sử dụng QuickSort để hạn chế việc mất đi một mớ mem cho cái heap.

Mình sẽ thực hiện tiếp Quick Sort trong phần sau của bài, cảm ơn các bạn đã đọc 😀

Happy Coding ^_^

Bài viết gốc được đăng tải tại medium.com

Có thể bạn quan tâm:

Xem thêm các việc làm Developer hấp dẫn tại TopDev

Destroy Specific Session ID via PHP

Bài viết được sự cho phép của BBT Kinh nghiệm lập trình

Chào các bạn, hôm nay mình xin chia sẻ 1 mẹo nhỏ trong PHP: Destroy session_id của user khác từ server.

Tại sao cần làm việc này?

Bài toán đặt ra: Tại một thời điểm, chỉ cho phép người dùng có duy nhất 1 phiên đăng nhập trên hệ thống.
Mô tả chi tiết: Thực tế hiện tay rất nhiều hệ thống web ứng dụng bán license theo số lượng tài khoản sử dụng. Do vậy, nếu không có biện pháp ngăn chặn việc người dùng sử dụng chung tài khoản để làm việc trên hệ thống thì việc thất thoát doanh thu là khó tránh khỏi. Chính vì thế, hệ thống cần có phương án để ngăn chặn việc này, tại một thời điểm, chỉ cho phép người dùng có 1 phiên đăng nhập trên 1 thiết bị và thao tác trên hệ thống.

Cách thực hiện trên với PHP?

Mình sẽ nói tóm tắt các bước thực hiện, logic này có thể áp dụng tương tự với ngôn ngữ khác. Mục đích là sử dụng tài khoản hiện tại, để destroy session id khác trên server.
Bước 1:Commit session ID nếu nó đã tồn tại
Bước 2: Store current session id
Bước 3: Destroy session specified
Bước 4: Restore current session id

Ứng tuyển ngay các vị trí PHP tuyển dụng mới nhất trên TopDev

Code demo!

<?php
$session_id_to_destroy 
‘nill2if998vhplq9f3pj08vjb1’;
// 1. commit session if it’s started.
if (session_id()) {
session_commit();
}

// 2. store current session id
session_start();
$current_session_id session_id();
session_commit();

// 3. hijack then destroy session specified.
session_id($session_id_to_destroy);
session_start();
session_destroy();
session_commit();

// 4. restore current session id. If don’t restore it, your current session will refer to the session you just destroyed!
session_id($current_session_id);
session_start();
session_commit();

?>

Xong, một mẹo khá nhỏ nhưng đôi khi lại rất cần thiết cho hệ thống của bạn. Chúc các bạn áp dụng thành công!

Tham khảo

https://www.php.net/manual/tr/function.session-destroy.php

Bài viết gốc được đăng tải tại kinhnghiemlaptrinh.com

Có thể bạn quan tâm:

Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev

Python: Giải thích hàm Sleep

Bài viết được sự cho phép của tác giả Phạm Văn Nguyên

Trong Python hoặc bất kỳ ngôn ngữ lập trình nào khác, đôi khi bạn muốn thêm thời gian trễ trong chương trình của mình trước khi bạn tiếp tục đến phần tiếp theo của mã.

Nếu đây là những gì bạn muốn làm, thì bạn nên sử dụng chức năng sleep từ module time .

Đầu tiên, tôi sẽ bắt đầu bằng cách thảo luận về cách sử dụng chức năng sleep của Python . Sau đó tôi sẽ nói nhiều hơn về một số câu hỏi thường gặp và cách thức thực hiện của function(hàm) sleep.

Hàm sleep

Giống như tôi đã đề cập, Sleep là một hàm tích hợp Python trong mô-đun time .

Vì vậy, để sử hàm sleep , bạn sẽ cần import module time trước.

Hàm sleep có  một đối số là khoảng thời gian tính bằng giây mà bạn dừng.

Sử dụng hàn sleep trong Python 2 và Python 3 hoàn toàn giống nhau, vì vậy bạn sẽ không cần phải lo lắng về phiên bản Python nào mà mã của bạn đang chạy.

Nó thực sự rất đơn giản và dễ hiểu. Chúng ta hãy đi qua một số ví dụ.

  Python: Tạo một máy chủ HTTP đơn giản

Python 2

Giả sử bạn muốn hàn sleep để thêm độ trễ 10 giây.

Trong ví dụ sau, tôi in ra thời gian đã trôi qua giữa việc gọi hàm sleep và sau khi nó trả về .

import time# record start timestart_time = time.time()# sleep for 10 secondstime.sleep(10)# print elapsed timeelapsed_time = time.time() - start_timeprint(elapsed_time)# output: 10.000149965286255

Như bạn có thể thấy, thời gian trôi qua rất gần với 10 giây, đó là những gì chúng ta mong đợi.

Python 3

Không có thay đổi trong Python 3.

Bạn có thể sử dụng hàm sleep trong Python 2 chính xác như bạn làm trong Python 2.

  Làm app giao diện đồ hoạ với Python

Các câu hỏi thường gặp

Câu hỏi: Làm thế nào để sử dụng hàm sleep để sleep trong một khoảng thời gian ít hơn một giây, ví dụ như mili giây hoặc micro giây?

Câu hỏi tuyệt vời.

Hàm sleep thực sự lấy số dấu phẩy động làm đối số, không phải là số nguyên.

Và điều đó có nghĩa là, bạn có thể truyền tham số đầu vào là số thập phân.

Ví dụ:

import time# sleep for half a secondtime.sleep(0.5)# sleep for 1 millisecondtime.sleep(1 * 10**(-3))# sleep for 1 microsecondtime.sleep(1 * 10**(-6))

Câu hỏi: Làm thế nào để sử dụng hàm sleep để sleep cho đến một thời gian cụ thể?

Đây là một câu hỏi rất phổ biến.

Tùy thuộc vào những gì bạn đang cố gắng làm, có hai cách khác nhau để đạt được điều này.

Cách đơn giản là tính toán trước độ trễ (tính bằng giây) cho đến thời gian cụ thể đó trước khi bạn gọi hàm sleep.

Một cách khác là sử dụng hàm event.wait từ module event .

Nếu những gì bạn đang cố gắng làm là chặn một luồng cho đến khi một điều kiện(condition) xảy ra, thì tốt hơn là sử dụng Wait () .

Tìm việc làm python Hồ Chí Minh lương cao

Câu hỏi: [Nâng cao] Sleep thực sự được thực hiện như thế nào?

Đây là một câu hỏi tuyệt vời.

sleep không thực sự được thực hiện bởi Python.

Vâng, nó là, nhưng nó chỉ đơn giản là một trình bao bọc xung quanh một cái gì đó khác được Hệ điều hành triển khai.

Tất cả các hệ điều hành đều có lệnh gọi hệ thống(system call) là Sleep () .

Nếu bạn không biết System Call là gì, thì bạn nên làm!

Đây là cuốn sách mà tôi khuyên bạn nên tìm hiểu về các khái niệm Hệ điều hành.

Nhưng dù bằng cách nào, bạn có thể nghĩ về Cuộc gọi hệ thống như một API hoặc giao diện mà HĐH cung cấp cho các chương trình không gian người dùng để tương tác với HĐH.

Vậy HĐH sẽ làm gì khi nhận được System call sleep?

Về cơ bản, hệ điều hành sẽ làm là nó sẽ tạm dừng quá trình (chương trình của bạn) được lên lịch trên CPU trong khoảng thời gian mà bạn đã chỉ định.

Lưu ý rằng điều này hoàn toàn khác với việc thêm độ trễ bằng cách sử dụng vòng lặp giả không làm gì cả (đó là điều bạn KHÔNG BAO GIỜ nên làm).

Trong trường hợp giả cho vòng lặp, quy trình của bạn thực sự vẫn đang chạy trên CPU.

Nhưng trong trường hợp Sleep () , quy trình của bạn sẽ không hoạt động trong một thời gian cho đến khi HĐH bắt đầu lập lịch lại trên CPU.

Bài viết gốc được đăng tải tại nguyenpv.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng IT hấp dẫn tại TopDev

Khai báo hàm khởi tạo trong Java – Constructor Declarations

Bài viết được sự cho phép của tác giả Trần Hữu Cương

Constructor Declarations

Trong Java, các đối tượng được khởi tạo thông qua hàm khởi tạo (constructor). Mỗi lần bạn tạo mới một đối tượng sẽ có ít nhất một hàm khởi tạo được thực thi.

Mỗi class đều có một hàm khởi tạo, nếu bạn không khai báo thì complier sẽ tự động khai báo một hàm khởi tạo không tham số cho bạn.

Có rất nhiều nguyên tắc khác nhau liên quan đến hàm khởi tạo (sẽ tìm hiểu ở bài sau). Ở bài này chúng ta sẽ chỉ tập trung vào các nguyên tắc khai báo cơ bản.

Hàm khởi tạo cũng được coi như một hàm với kiểu dữ liệu trả về của hàm khởi tạo chính là một thể hiện của class chứa nó. Hàm khởi tạo có thể đi kèm với các access modifier

Tìm việc làm Java cho sinh viên ít kinh nghiệm

Ví dụ ta có class sau:

public class Person {

private String name;
private String address;

}

Các hàm khởi tạo hợp lệ:

Person() {
}

public Person() {
}

protected Person() {
}

private Person() {
}

public Person(String name, String address) {
this.name = name;
this.address = address;
}

public Person(String name) {
this.name = name;
}

Các hàm khởi tạo không hợp lệ:

void Person() {} // đây là một method, không khải constructor

static Person() {} // không thể đi kèm với từ khóa static

final Person() {} // không thể đi kèm với từ khóa final

abstract Person() {} // không thể đi kèm với từ khóa abstract

Một class có thể có nhiều hàm khởi tạo (tương tự như một class có thể có nhiều hàm cùng tên – tính đa hình) nhưng tham số đầu vào của các hàm khởi tạo phải khác nhau.

Khi bạn gọi lệnh new thì đầu tiên nó sẽ chạy vào hàm khởi tạo của đối tượng rồi sau đấy mới chạy các khối static, method…

Bài viết gốc được đăng tải tại stackjava.com

Có thể bạn quan tâm:

Xem thêm các việc làm java Developer hấp dẫn tại đây

Tìm kiếm thông minh với Typeahead trong ứng dụng Laravel

Bài viết được sự cho phép của tác giả Kien Dang Chung

Trong những website lớn, các phần tìm kiếm hay nhập liệu rất cần những tính năng thông minh như gợi ý dựa trên các từ nhập vào, nó giúp cho nâng cao trải nghiệm người dùng, giúp tìm kiếm và nhập liệu trở lên đơn giản hơn. Google là một minh chứng không cần phải bàn cãi cho vấn đề này. Những năm đầu của thế kỉ 21, Google xuất hiện với chỉ duy nhất một ô tìm kiếm và tính năng gợi ý ngay lập tức khi từ khóa được đánh vào, nó đã giúp người dùng định hướng được ngay khi chỉ gõ vào 1-2 từ trong từ khóa.

Ở thời điểm đó, gợi ý khi tìm kiếm là một tính năng phức tạp và xa xỉ, nhưng khi công nghệ phần mềm phát triển, đặc biệt với phần mềm mã nguồn mở, những tính năng như vậy thật đơn giản để thực hiện. Bài viết này sẽ hướng dẫn bạn thực hiện các tìm kiếm thông minh hay gợi ý nhập liệu sử dụng thư viện Typeahead là một gói phần mềm mã nguồn mở của Twitter trong các ứng dụng Laravel.

  Các Laravel route tips giúp bạn cải thiện routing
  Cách sử dụng Laravel với Socket.IO

Tìm kiếm thông minh với Typeahead trong ứng dụng Laravel

Trong bài viết này chúng ta sẽ thực hiện một ví dụ tìm kiếm thông minh thông tin khách hàng bằng cách tạo ra dữ liệu mẫu khoảng 10,000 khách hàng. Như bạn thấy khi tìm kiếm hệ thống sẽ gợi ý các bản ghi trong database rất nhanh giúp người dùng có thể lựa chọn luôn chính xác người dùng. Typeahead sử dụng kết hợp Bloodhound cho tốc độ rất nhanh mặc dù dữ liệu là 10k bản ghi.

1. Giới thiệu về Typeahead

  • Typeahead.js là một thư viện Javascript rất linh hoạt, nó có thể làm nền tảng tốt để xây dựng các tính năng tìm kiếm gợi ý thông minh. Typeahead bao gồm hai thành phần: Typeahead: Phần chuyên xử lý giao diện người dùng
    • Hiển thị gợi ý đến người dùng ngay khi họ nhập liệu
    • Hiển thị các gợi ý ngay trên ô nhập liệu
    • Hỗ trợ các tùy chỉnh giao diện linh hoạt
    • Highlight các từ khóa trùng khớp trong phần gợi ý
    • Kích hoạt các sự kiện tùy chỉnh cho phép mở rộng các xử lý
  • Bloodhound: Bộ máy gợi ý nâng cao
    • Cho phép các dữ liệu được hardcode
    • Lấy dữ liệu từ trước để giảm độ trễ khi gợi ý
    • Sử dụng Local Storage giảm số lượng các request đến máy chủ.
    • Sử dụng rate limit và bộ đệm cho các request đến máy chủ làm giảm nhẹ tải dữ liệu

Bộ máy gợi ý Bloodhound sẽ được sử dụng để tính toán kết quả với các truy vấn cho trước và Typeahead sẽ sử dụng để render ra mã HTML. Cả hai thành phần này là độc lập, trong bài viết này chúng ta sẽ sử dụng cả hai để xây dựng công cụ tìm kiếm gợi ý thông minh.

1.1 Cài đặt Typeahead

Trước khi đi vào sử dụng Typeahead chúng ta cần cài đặt gói thư viện này, có ba cách thức để cài đặt.

Sử dụng npm

npm i typeahead

Tải gói thư viện dạng file zip

Vào đường dẫn Github của Typeahead chọn Clone or download và click vào Download zip. Giải nén ra chúng ta sẽ thấy trong thư mục dist có những file như sau:

  • bloodhound.js (chỉ có thành phần Bloodhound).
  • typeahead.bundle.js (Bao gồm cả Typeahead và Bloodhound).
  • typeahead.jquery.js (chỉ có thành phần Typeahead).

Các file có thêm min.js là các file được tối ưu hóa dung lượng.

Tích hợp thông qua CDN

Sử dụng CDN giúp file có thể được tải về nhanh hơn, tuy nhiên cũng gặp phải một vấn đề là khi cá mập cắn cáp, internet chập chờn nếu các CDN không có server ở gần Việt Nam thì các thư viện này rất khó để tải về. jQuery: https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js Typeahead: https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js

Chú ý: Typeahead yêu cầu jquery phiên bản từ 1.9 trở lên. Trong bài viết này chúng ta sẽ sử dụng cách thứ ba cho nhanh, với dự án lớn nên sử dụng cách 1 để tối ưu hóa các tài nguyên với Laravel Mix.

1.2 Khởi tạo Typeahead

  • Typeahead có nhiều cách khởi tạo và sau đây là cách khởi tạo hay dùng nhất jQuery#typeahead(options, [*datasets]). Tính năng typeahead được áp dụng cho các input dạng text input[type=”text”] có hai tham số cho khởi tạo: options là các tùy chọn cấu hình, một số giá trị cần quan tâm như sau:
    • highlight: thêm thẻ <strong> vào các từ trùng khớp trong phần gợi ý. Mặc định là false.
    • hint: hiển thị cả từ gợi ý trong ô nhập liệu, mặc định true.
    • minLength: Số ký tự tối thiểu cần nhập khi tính năng gợi ý được bắt đầu, mặc định là 1.
    • classNames: Cho phép sử dụng tên class khác với mặc định.
  • dataset: một typeahead có thể có nhiều dataset, ví dụ khi bạn tìm kiếm trong một trang bán hàng có thể trả về gợi ý cho cả sản phẩm và các tin tức liên quan đến sản phẩm. Các dataset có một số các tùy chọn cấu hình như sau:
    • name: tên của dataset.
    • source: nguồn dữ liệu dùng cho gợi ý, có thể là một instance của Bloodhound, như ở phần đầu chúng ta có nói Typeahead chỉ xử lý giao diện người dùng và Bloodhound với là bộ máy thực hiện các gợi ý.
    • limit: Số gợi ý tối đa sẽ được hiển thị, mặc định là 5.
$("#navbar-search-input").typeahead({
    hint: true,
    highlight: true,
    minLength: 1
});

1.3 Khởi tạo Bloodhound

Bloodhound là bộ máy gợi ý cho Typeahead.js, sử dụng thành phần này mang lại nhiều tính năng nâng cao hơn vì nó có thể lấy dữ liệu từ một nguồn remote và sử dụng bộ đệm để tăng tốc.

var engine = new Bloodhound({
    remote: {
        url: 'api/customer?q=%QUERY%',
        wildcard: '%QUERY%'
    },
    datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
    queryTokenizer: Bloodhound.tokenizers.whitespace
});

Chúng ta sẽ thiết lập đường dẫn /find?q= trong phần Laravel, datumTokenizer cần một mảng JSON. Như vậy, chúng ta đã có dữ liệu và có thể sử dụng nó cho thiết lập source của typeahead như sau:

source: engine.ttAdapter()

1.4 Tạo mẫu cho các gợi ý

Typeahead cho phép sử dụng các template để thay đổi kiểu mẫu cho các gợi ý, bạn cũng có thể sử dụng bootstrap để style:

templates: {
    empty: [
        '<div class="list-group search-results-dropdown"><div class="list-group-item">Không có kết quả phù hợp.</div></div>'
    ],
    header: [
        '<div class="list-group search-results-dropdown">'
    ],
    suggestion: function (data) {
         return '<a href="' + data.id + '" class="list-group-item">' + data.name + '</a>'
    }
}

1.5 Code hoàn chỉnh cho Typeahead.js

jQuery(document).ready(function($) {
    var engine = new Bloodhound({
        remote: {
            url: 'api/customer?q=%QUERY%',
            wildcard: '%QUERY%'
        },
        datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
        queryTokenizer: Bloodhound.tokenizers.whitespace
    });

    $(".search-input").typeahead({
        hint: true,
        highlight: true,
        minLength: 1
    }, {
        source: engine.ttAdapter(),
        name: 'usersList',
        templates: {
            empty: [
                '<div class="list-group search-results-dropdown"><div class="list-group-item">Không có kết quả phù hợp.</div></div>'
            ],
            header: [
                '<div class="list-group search-results-dropdown">'
            ],
            suggestion: function (data) {
                return '<a href="' + data.id + '" class="list-group-item">' + data.name + '</a>'
      }
        }
    });
});

2. Xây dựng backend với Laravel

Phần 2 chúng ta sẽ xây dựng ứng dụng Laravel

2.1 Cài đặt môi trường Laravel

Tham khảo công cụ Laragon cài đặt nhanh môi trường Laravel, chúng ta tạo ra một môi trường test có tên là typeahead và Laragon tự động tạo ra tên miền ảo typeahead.dev. Laragon cũng tự động tạo ra database tên typeahead cho chúng ta. Việc đầu tiên là thiết lập file .env:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=typeahead
DB_USERNAME=root
DB_PASSWORD=secret

2.2 Tạo bảng Customer và dữ liệu mẫu

Tạo ra Model Customer cùng với file migrate thông qua câu lệnh artisan make:model (Xem thêm Laravel Artisan là gì?)

D:\Laragon\www\typeahead
λ php artisan make:model Customer -m
Model created successfully.
Created Migration: 2017_09_04_084521_create_customers_table

Chỉnh sửa file migrate xxx_create_customers_table.php trong thư mục database\migrations:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCustomersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name', 255);
            $table->string('address', 255);
            $table->string('phone', 255);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('customers');
    }
}

Thực hiện tạo ra bảng customers trong database với câu lệnh artisan migrate. (Tìm hiểu thêm về Laravel Migration và Laravel Seeding tạo database và dữ liệu test)

D:\Laragon\www\typeahead
λ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table
Migrating: 2017_09_04_084521_create_customers_table
Migrated:  2017_09_04_084521_create_customers_table

Có nhiều các bảng khác được tạo ra do các bảng này được sử dụng cho xác thực người dùng (xem Laravel Authentication xác thực người dùng thật đơn giản). Tiếp theo chúng ta sẽ sử dụng Laravel Seeding để tạo ra 10000 dữ liệu customer mẫu trong database.

D:\Laragon\www\typeahead
λ php artisan make:seeder CustomersTableSeeder
Seeder created successfully.

Tạo file CustomerFactory.php trong thư mục database\factories:

<?php
use Faker\Generator as Faker;

$factory->define(App\Customer::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'address' => $faker->address,
        'phone' => $faker->phoneNumber
    ];
});

Class này sử dụng Faker để tạo ra dữ liệu test. Tiếp theo chúng ta tạo file Seeder:

D:\Laragon\www\typeahead
λ php artisan make:seeder CustomersTableSeeder
Seeder created successfully.

Và thêm đoạn mã sau vào file CustomersTableSeeder.php trong thư mục database\seeds:

<?php
use Illuminate\Database\Seeder;

class CustomersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(Customer::class, 10000)->create();
    }
}

và khai báo sử dụng Seeder này trong file database\seeds\DatabaseSeeder.php:

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $this->call(CustomersTableSeeder::class);
    }
}

Thực hiện tạo ra 10000 dữ liệu khách hàng mẫu với câu lệnh artisan db:seed

D:\Laragon\www\typeahead
λ php artisan db:seed
Seeding: CustomersTableSeeder

2.3 Route

Chúng ta đã tạo ra bảng customer với 10 nghìn dữ liệu khách hàng mẫu được đưa vào, tiếp theo chúng ta cần tạo ra một đường dẫn để thực hiện tìm kiếm khách hàng và trả về dữ liệu dạng JSON cho truy vấn Bloodhound, thêm route sau đây vào file routes\api.php:

Route::get('find', 'SearchController@find');

Tạo thêm một route trong routes\web.php để hiển thị thông tin chi tiết của khách hàng, ở đây chỉ thực hiện in ra màn hình thông tin mà không có tạo view, coi như bài tập thêm cho các bạn :).

Route::get('customer/{id}', function() {
   $customer = Customer::find($id);
   return $customer->name . '@' . $customer->phone . '-' . $customer->address;
});

2.4 Controller

Tạo controller SearchController bằng lệnh artisan:

D:\Laragon\www\typeahead
λ php artisan make:controller SearchController
Controller created successfully.

Thêm phương thức find vào SearchController.php trong app\Http\Controllers:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Customer;

class SearchController extends Controller
{
    public function find(Request $request) {
      $customer = Customer::where('name', 'like', '%' . $request->get('q') . '%')->get();
      return response()->json($customer);
    }
}

Vào đường dẫn http://typeahead.dev/customer?q=jo chúng ta sẽ có kết quả là dữ liệu dạng JSON:

Tìm kiếm thông minh với Typeahead trong ứng dụng Laravel

2.5 View

Tiếp đến chúng ta thay đổi welcome view nằm trong thư mục resources\views:

<!DOCTYPE html>
<html lang="vi">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="Tìm kiếm thông minh sử dụng Typeahead trong ứng dụng Laravel">
        <meta name="author" content="FirebirD ['www.allaravel.com']">
        <title>Tìm kiếm thông minh trong Laravel sử dụng Typeahead - Allaravel.com</title>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->
        <style type="text/css">html{position:relative;min-height:100%;}body{margin-bottom:60px;}.footer{position:absolute;bottom:0;width:100%;height:60px;background-color:#f5f5f5;}body>.container{padding:60px 15px 0;}.container.text-muted{margin: 20px 0;}.footer>.container{padding-right:15px;padding-left:15px;}code{font-size:80%;}</style>
    </head>
    <body>
        <!-- Fixed navbar -->
        <nav class="navbar navbar-default navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="https://allaravel.com">Typeahead</a>
                </div>
                <div id="navbar" class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="#">Home</a></li>
                        <li><a href="#about">About</a></li>
                        <li><a href="#contact">Contact</a></li>
                    </ul>
                </div><!--/.nav-collapse -->
            </div>
        </nav>

        <!-- Begin page content -->
        <div class="container">
            <div class="page-header">
                <h3>Ví dụ tìm kiếm thông minh sử dụng typeahead.js trong ứng dụng Laravel - Allaravel.com</h3>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <form class="form-inline typeahead">
                        <div class="form-group">
                            <input type="name" class="form-control search-input" id="name" autocomplete="off" placeholder="Nhập tên khách hàng">
                        </div>
                        <button type="submit" class="btn btn-default">Tìm kiếm</button>
                    </form>
                </div>
            </div>
        </div>

        <footer class="footer">
            <div class="container">
                <p class="text-muted">Example in <a href="https://allaravel.com">allaravel.com</a></p>
            </div>
        </footer>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script>
        <script>
            jQuery(document).ready(function($) {
                var engine = new Bloodhound({
                    remote: {
                        url: 'api/customer?q=%QUERY%',
                        wildcard: '%QUERY%'
                    },
                    datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
                    queryTokenizer: Bloodhound.tokenizers.whitespace
                });

                $(".search-input").typeahead({
                    hint: true,
                    highlight: true,
                    minLength: 1
                }, {
                    source: engine.ttAdapter(),
                    name: 'usersList',
                    templates: {
                        empty: [
                            '<div class="list-group search-results-dropdown"><div class="list-group-item">Không có kết quả phù hợp.</div></div>'
                        ],
                        header: [
                            '<div class="list-group search-results-dropdown">'
                        ],
                        suggestion: function (data) {
                            return '<a href="customer/' + data.id + '" class="list-group-item">' + data.name + '</a>'
                        }
                    }
                });
            });
        </script>
    </body>
</html>

Kết quả khi vào http://typeahead.dev và thực hiện tìm kiếm khách hàng chúng ta được như sau:

Tìm kiếm thông minh với Typeahead trong ứng dụng Laravel

3. Lời kết

Với việc sử dụng Typeahead trong ứng dụng Laravel, trải nghiệm người dùng được nâng cao hơn. Typeahead không chỉ sử dụng trong các phần tìm kiếm mà chúng ta có thể sử dụng trong các form nhập liệu giúp gợi ý thông tin nhập liệu. Hi vọng bài viết sẽ giúp ích cho các bạn trong các dự án riêng sử dụng Laravel, có bất kỳ thắc mắc hoặc góp ý các bạn comment cuối bài nhé.

Bài viết gốc được đăng tải tại allaravel.com

Có thể bạn quan tâm:

Xem thêm việc làm lập trình viên laravel hấp dẫn tại TopDev

PHP 8: match hay là switch?

Bài viết được sự cho phép của tác giả Nguyễn Trần Chung

PHP 8 sẽ giới thiệu một biểu thức mới “match“. Một tính năng mạnh mẽ và sẽ là sự lựa chọn tốt hơn so với “switch“. Vậy chính xác sự khác biệt là gì?

  10 PHP Instagram Scripts & Widgets tốt nhất
  10 Frameworks tốt nhất hiện nay cho PHP

Hãy bắt đầu bằng cách so sánh cả hai. Đây là một ví dụ về “switch” cổ điển:

switch ($statusCode) {
    case 200:
    case 300:
        $message = null;
        break;
    case 400:
        $message = 'not found';
        break;
    case 500:
        $message = 'server error';
        break;
    default:
        $message = 'unknown status code';
        break;
}

Đoạn code ở dưới đây sẽ tương đương với ở trên khi dùng biểu thức “match“:

$message = match ($statusCode) {
    200, 300 => null,
    400 => 'not found',
    500 => 'server error',
    default => 'unknown status code',
};

Trước hết, biểu thức khớp ngắn hơn đáng kể:

  • nó không yêu cầu break statement
  • nó có thể kết hợp các trường hợp giống nhau thành một bằng dấu phẩy
  • nó trả về một giá trị, vì vậy bạn chỉ phải gán giá trị một lần

Nhưng thậm chí còn nhiều hơn thế!

Không ép kiểu

match sẽ kiểm tra loại nghiêm ngặt thay vì kiểm tra lỏng lẻo. Giống như sử dụng === thay vì ==. Mọi người có thể sẽ không đồng ý liệu đó có phải là điều tốt hay không, nhưng đó là một chủ đề riêng chúng ta sẽ bàn sau.

$statusCode = '200';

$message = match ($statusCode) {
    200 => null,
    default => 'unknown status code',
};

// Kết quả trả về
// $message = 'unknown status code'

Giá trị không xác định gây ra lỗi

Nếu bạn quên kiểm tra giá trị và khi không có nhánh default được chỉ định, PHP sẽ đưa ra ngoại lệ UnhandledMatchError. Tuy kiểm tra chặt chẽ, nhưng nó sẽ ngăn chặn các lỗi nhỏ nhặt không được chú ý.

$statusCode = 400;

$message = match ($statusCode) {
    200 => 'perfect',
};

// UnhandledMatchError sẽ throw

Hiện tại chỉ có các biểu thức một dòng

Bạn chỉ có thể viết một biểu thức trên một dùng. Các khối biểu thức có thể sẽ được thêm vào tại một thời điểm nào đó, nhưng vẫn chưa rõ chính xác khi nào .

Ứng tuyển ngay các vị trí PHP tuyển dụng mới nhất trên TopDev

Kết hợp điều kiện

match có thể kết hợp nhiều điều kiện lại với nhau và viết trên 1 dòng ngăn cách bởi dấu phẩy

$message = match ($statusCode) {
    200, 300, 301, 302 => 'combined expressions',
};

Throwing exceptions

Trong PHP 8 có thay đổi về cách dùng throw trước đây throw chỉ bắt đầu từ câu lệnh thì giờ có thể bắt đầu từ biểu thức, nghĩa là bạn có thể throw exception ở giá trị 500 mà k phải cần đưa vào trong hàm

$message = match ($statusCode) {
    200 => null,
    500 => throw new ServerError(),
    default => 'unknown status code',
};

Pattern matching

Ok, có một điều nữa: Pattern matching. Đây là một kỹ thuật được sử dụng trong các ngôn ngữ lập trình khác, để cho phép kết hợp phức tạp hơn các giá trị đơn giản. Hãy nghĩ về nó như regex, nhưng đối với các biến thay vì văn bản.

Pattern matching không được hỗ trợ ngay bây giờ, vì đây là một tính năng khá phức tạp, nhưng Ilija Tovilo (tác giả RFC) đã đề cập đến nó như là một tính năng có thể có trong tương lai.

Vậy thì, switch hay là match?

match với phiên bản chặt chẽ và hiện đại hơn so với người anh switch

Trong một số trường hợp switch sẽ cung cấp linh hoạt hơn so với match, đặc biệt là với các khối code có nhiều dòng. Tuy nhiên, sự nghiêm ngặt của toán tử match là hấp dẫn và pattern matching sẽ là một yếu tố thay đổi cuộc chơi cho PHP.

Tôi thừa nhận tôi chưa bao giờ viết một switch statement trong những năm qua vì nhiều điều kỳ quặc của nó. Vì vậy, trong khi nó chưa hoàn hảo, nhưng có những trường hợp sử dụng được thì match sẽ là một sự lựa chọn tốt đối với tôi.

Ý kiến của bạn là gì, hãy để lại bình luận nhé!

Dịch từ stitcher.io

Bài viết gốc được đăng tải tại chungnguyen.xyz

Có thể bạn quan tâm:

Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev

Cover Letter cho Dev là gì? Tips nâng cấp Cover Letter dành cho Developer

Cover Letter chuẩn Dev sẽ như thế nào? Nhiều ứng viên ngành IT cảm thấy khó khăn trong việc tìm kiếm một Cover Letter cho Dev với một format chuyên nghiệp. Vậy Cover Letter cho Developer là gì và đâu là những điểm cần lưu ý? Cùng TopDev điểm qua bài viết sau để giải đáp các thắc mắc đấy

Thế nào là Cover Letter cho Dev?

Cover Letter (hay còn gọi là Thư xin việc), là một loại văn bản được xây dựng dưới hình thức một trang. Cover Letter cho Dev truyền tải các thông tin về cá nhân, năng lực, trình độ, mục tiêu phát triển ngành lập trình đến với nhà tuyển dụng. 

Cover Letter
thư xin việc – Cover Letter  được hiểu như thế nào?

Chất lượng của một Cover Letter cho Developer sẽ quyết định 30-50% kết quả ứng tuyển. Điều đó cho thấy, cần đầu tư cho chiếc Cover Letter của mình sao cho chỉn chủ nhất. Đừng xem thường vai trò của Cover Letter cho Dev. Vì biết đâu bạn sẽ bị “out” vì sự hời hợt đối với nó đấy!

Cover Letter không chỉ phản ánh bạn là ứng viên Developer phù hợp mà còn cho trong nhiều trường hợp, nó còn thể hiện bạn là người không phù hợp với vị trí đó.

Tips nâng cấp Cover Letter cho Developer

Hãy lưu tâm các tips sau để Cover Letter của bạn không trở nên quá tệ hại trong mắt nhà tuyển dụng.

CV ngành IT
Tips viết CV giúp ứng viên có thêm bí quyết hoàn thiện Cover Letter Developer

Cover Letter bám sát tính trọng tâm về nội dung

Ứng viên IT nên chú trọng đến việc chia sẻ về các thông tin quan trọng. Tránh liệt kê những nội dung thừa thải, không liên quan. Điều đó chứng tỏ bạn là người thiếu sự nhìn nhận, sàng lọc và phân tích thông tin.

Đồng thời, viết CV chuẩn đẹp giúp bạn định hình và có những trải nghiệm tốt hơn. Trường hợp bạn ứng tuyển các vị trí khác như freelancer it hay Senior Developer đều sẽ đạt hiệu quả ứng tuyển cao hơn.

Nội dung cần được triển khai theo format chuẩn và đúng định hướng. Không dài dòng, dàn trải với nhiều thông tin lan man, thiếu tính kết nối. Sự rời rạc về nội dung khiến giá trị của Cover Letter cho Developer bị giảm đi. 

Trọng tâm còn nằm ở chỗ vấn đề ứng viên chia sẻ các kỹ năng cần thiết cho vị trí ứng tuyển. Đừng nói quá và phô trương quá nhiều kỹ năng. Bạn cần nhớ là nhà tuyển dụng cần chất lượng hơn số lượng.

Số liệu thực minh chứng cho các trải nghiệm trong Cover Letter 

Những minh chứng rất quan trọng. Nhà tuyển dụng sẽ có ấn tượng hơn với nhiều ứng viên trình bày rõ ràng các minh chứng về các trải nghiệm của mình. 

Đó có thể là:

+ Số liệu cho thấy tốc độ tăng trưởng dưới sự phồi hợp giải quyết các công việc giữa bạn và team trong một dự án.

+Lời đánh giá chuyên môn.

+ Các thành tích, giải thưởng từ quá trình phân tích, nghiên cứu chuyên sâu.

Kiểm tra lại nội dung Cover Letter cho Dev

Khó có thể nói có một công thức chung nào để được gọi là sự ấn tượng. Thế nhưng, việc rà soát lại các lỗi trong Cover Letter sẽ giúp cho thư xin việc IT được nâng cấp một cách toàn diện hơn.

Xem thêm các vị tri tuyển dụng IT fresher nổi bật

Định dạng Cover Letter đã đúng hay chưa? Một điều lưu ý là Cover Letter cho Dev không quá nhiều chữ. Khoảng cách lề chuẩn là từ 1’’- 1,5’’.

Dù cho bạn viết CV ngành IT, CV IT tiếng anh (CV English IT), CV cho sinh viên IT mới ra trường (CV IT student), CV IT Developer hay CV dành cho Senior Developer,… bạn vẫn nên quan tâm đến vấn đề văn phong và giọng điệu.  

Không nên dùng những từ ngữ quá phức tạp, có sắc thái biểu đạt quá cao. Điều này làm giảm đi tính hiệu quả của Cover Letter cho Developer . Thay vào đó hãy ưu tiên dùng các từ ngữ đơn giản, dễ hiểu. Ứng viên cần lượt bỏ đi những thông tin không quan trọng. Vì đôi khi, quá nhiều nội dung sẽ khiến nhà tuyển dụng khó nắm bắt được những điều bạn muốn truyền tải trong Cover Letter cho Dev.

Gợi ý các mẫu template Cover letter/mẫu thư xin việc cho Dev

Tải ngay những mẫu CV đẹp dành cho lập trình viên tại đây

CV cho sinh viên IT mới ra trường
M1 – Cover Letter
Cover Letter
M2 – Cover Letter

Khi đã đủ tự tin, bạn hãy chuẩn bị một chiếc CV IT thật chất lượng. Đừng chần chờ mà không nắm bắt cơ hội trải nghiệm việc làm IT tại TopDev.

Có thể bạn quan tâm:

Xem thêm Jobs Developer trên TopDev

“Quyền năng” dữ liệu trong Kỷ nguyên mới?! | VWS2020

Vietnam Web Summit 2020 (VWS2020) với hàng loạt chủ đề hấp dẫn về tính “quyền năng” của dữ liệu đặc biệt trong thời đại Digital Transformation! Sự phát triển mạnh mẽ và ứng dụng công nghệ đa dạng vào trong đời sống tạo ra một lượng lớn dữ liệu khách hàng, tạo nên những câu chuyện và rồi lại tác động trở lại đến quyết định của nhiều tổ chức, doanh nghiệp. Tuy nhiên trong thực tế, liệu những câu chuyện được kể bởi dữ liệu có phản ánh toàn bộ thực tế? Dữ liệu có phải là một ‘đấng toàn năng’ và chi phối mọi quyết định của doanh nghiệp?

Hãy để các diễn giả của Vietnam Web Summit 2020 giúp bạn ‘tỏ tường’ hơn về quyền năng của dữ liệu và lý do dữ liệu không phải là toàn năng. Ai sẽ thực hiện điều này?

  Lộ diện những "nhân vật" đầu tiên góp mặt tại Vietnam Web Summit 2020 | VWS2020
◸Mr. Nguyễn Tấn Triều
CEO @USPA Technology Company◿

Câu trả lời mà bạn đang tìm kiếm có thể sẽ được tìm thấy dưới góc nhìn của “dataism”. Thông qua topic “Thinking with Dataism and LEO Customer Data Platform”, anh Nguyễn Tấn Triều sẽ cho bạn một góc nhìn tổng quan về ‘tôn giáo’ này cùng phương pháp LEO CDP.

◸Ms. Đặng Huỳnh Mai Anh
Data Science Manager @Amanotes◿

Tự thân dữ liệu không thể tạo ra câu chuyện nếu không có bàn tay của con người. Trong topic “Data Quality Control System – what it is and how it is employed in a music-tech company”, chị Đặng Huỳnh Mai Anh sẽ “bật mí” cách tận dụng triệt để loại ‘quyền năng’ này với một Data Quality Control System – một ‘cỗ máy’ quản trị chất lượng dữ liệu của một công ty đã chạm đến con số 1 tỷ lượt tải ứng dụng và với 95 triệu MAU.

◸Mr. Nghiêm Xuân Bách
Vietnam Country Manager @Cinnamon AI◿

“Data never sleep” – dữ liệu luôn được sản sinh liên tục, vì lẽ đó một Data Harvest Loop sẽ là giải pháp cho các doanh nghiệp. Để hiểu rõ hơn Data Harvest Loop là gì, doanh nghiệp sẽ ứng dụng như thế nào trong quá trình chuyển đổi, hãy để anh Nghiêm Xuân Bách tiết lộ cho bạn.

◸Ms. Kelly Tran
Client Partner – Monetization @Unity Technologies◿

Ở giai đoạn cuối cùng của quá trình chuyển hóa dữ liệu, chị Kelly Tran sẽ giải đáp lý do vì sao đôi lúc dữ liệu lại ‘nói dối’ với bạn, đặc biệt trong lĩnh vực gaming, thông qua topic “Why A/B testing by data go wrong in gaming”.

Với lượng topic lên đến con số hàng trăm, xoay quanh 6 nhóm chủ đề liên quan đến công nghệ web, VWS2020 hứa hẹn là điểm hẹn công nghệ hoành tráng nhất cuối năm 2020, nơi các tech-guys gặp gỡ và chia sẻ về những ứng dụng công nghệ mới và đón đầu xu hướng trong giai đoạn 5 năm tiếp theo!

==

Vietnam Web Summit 2020: LEAD THE AGE OF REVOLUTION TECHNOLOGIES

Vào tháng 12/2020, Vietnam Web Summit trở lại tại 2 thành phố TP.HCM và HN – nơi những ý tưởng sẽ gặp nhau và cùng đón đầu những xu hướng, công nghệ mới trong chặng đường 5 năm tiếp theo – một kỷ nguyên mới của công nghệ!

► Tìm hiểu thêm: www.vietnamwebsummit.com
► Hồ Chí Minh: 11/12/2020 | Hà Nội: 18/12/2020

Giới thiệu IDE phổ biến trong lập trình Python

Bài viết gốc được sự cho phép của tác giả Nguyễn Chí Thức

IDE là gì?

IDE là viết tắt của Integrated Development Environment (môi trường phát triển tích hợp) được định nghĩa là một công cụ mã hóa giúp tự động hóa quá trình chỉnh sửa, biên dịch, kiểm thử mã nguồn và nó giúp nhà phát triển dễ dàng chạy, viết và debug code.

Nó được thiết kế đặc biệt để phát triển phần mềm bao gồm một số công cụ được sử dụng để phát triển và kiểm thử phần mềm.

Giới thiệu IDE phổ biến trong lập trình Python như sau:

  • PyCharm
  • Spyder
  • PyDev
  • Atom
  • Wing
  • Jupyter Notebook
  • Thonny
  • Rodeo
  • Microsoft Visual Studio
  • Eric
  Python là gì? Tổng hợp kiến thức cho người mới bắt đầu

PyCharm

PyCharm được phát triển bởi Jet Brains và đây là môi trường phát triển tích hợp đa nền tảng (IDE) được thiết kế đặc biệt cho Python. Đây là IDE được sử dụng rộng rãi nhất và có sẵn ở cả phiên bản trả phí và nguồn mở miễn phí.

PyCharm là một IDE Python hoàn hảo với một các tính năng phong phú như tự đồng hoàn thiện code, điều hướng project nhanh, test và debug nhanh, hỗ trợ phát triển từ xa, khả năng truy cập cơ sở dữ liệu, v.v.

Tính năng, đặc điểm:

  1. Điều hướng mã thông minh
  2. Đánh dấu lỗi
  3. Trình gỡ lỗi (debug) mạnh mẽ
  4. Hỗ trợ các framework phát triển web Python, ví dụ, Angular JS, Javascript

Tuyển python lương cao không yêu cầu kinh nghiệm

Spyder

Spyder là một công cụ mã nguồn mở có sự công nhận cao trong thị trường IDE và phù hợp nhất với khoa học dữ liệu. Tên đầy đủ của Spyder là môi trường phát triển Python khoa học. Nó hỗ trợ tất cả các nền tảng quan trọng Linux, Windows và MacOS X.

Nó cung cấp một tập hợp các tính năng như trình soạn thảo mã cục bộ, trình xem tài liệu, trình thám hiểm biến, bảng điều khiển tích hợp, v.v. và hỗ trợ các mô-đun khoa học như NumPy, SciPy, v.v.

Tính năng, đặc điểm:

Làm nổi bật cú pháp đúng và hoàn thành mã tự động
Tích hợp mạnh mẽ với Python console
Hoạt động tốt trong chế độ chỉnh sửa đa ngôn ngữ và chế độ hoàn thành mã tự động
PyDev
PyDev được định nghĩa là một trong những IDE Python thường được sử dụng, là một plugin bên ngoài cho Eclipse. Đó là một lựa chọn tự nhiên của các nhà phát triển Python đến từ nền tảng Java và rất phổ biến trên thị trường với tư cách là trình thông dịch Python.

Pydev có một tính năng bao gồm tích hợp Django, hoàn thành mã tự động, thụt lề thông minh, v.v.

Tính năng, đặc điểm:

  1. Các tham số mạnh như tái refactor, debug, phân tích mã và chức năng bao phủ mã.
  2. Nó hỗ trợ các môi trường ảo, Mypy và định dạng màu đen.
  3. Cũng hỗ trợ tích hợp PyLint, trình debug từ xa, tích hợp unit test, v.v.

Atom

Atom được phát triển bởi GitHub, ban đầu được bắt đầu như một nguồn mở, đa nền tảng. Nó dựa trên một framework, nghĩa là nó cho phép ứng dụng máy tính để bàn cross-platform sử dụng Chromium và Node.js và thường được gọi là “Text Editor Hack cho thế kỷ 21 st

Tính năng, đặc điểm:

  1. Trực quan hóa kết quả trên Atom mà không cần mở bất kỳ cửa sổ nào khác.
  2. Một plugin có tên “Markdown Preview Plus” cung cấp hỗ trợ tích hợp để chỉnh sửa và hiển thị các tệp Markdown.
  Một số thủ thuật hay trong Python

Wing

Nó được định nghĩa là một IDE đa nền tảng được tích hợp các tính năng cần thiết và hỗ trợ phát triển tốt. Phiên bản cá nhân của nó là miễn phí. Phiên bản pro đi kèm bản dùng thử 30 ngày.

Nó có một số tính năng bao gồm tự động hoàn thành code, highlight cú pháp, thụt lề và debug.

Tính năng, đặc điểm:

  1. Có phần tùy chình và cũng có thể mở rộng.
  2. Hỗ trợ phát triển từ xa, test-driven development cùng với kiểm thử đơn vị.

Jupyter Notebook

Jupyter là một trong những trình soạn thảo sổ ghi chép IPython được sử dụng nhiều nhất được sử dụng trong ngành Khoa học dữ liệu. Nó là một ứng dụng web dựa trên cấu trúc máy chủ-máy khách và cho phép bạn tạo và thao tác với các tài liệu sổ ghi chép.

Tính năng, đặc điểm:

  1. Hỗ trợ đánh dấu
  2. Dễ dàng tạo và chỉnh sửa mã
  3. Lý tưởng cho người mới bắt đầu trong khoa học dữ liệu

Thonny

Thonny là một IDE khác phù hợp nhất cho việc học và dạy lập trình. Nó là một phần mềm được phát triển tại Đại học Tartu và hỗ trợ hoàn thành mã và đánh dấu các lỗi cú pháp.

Tính năng, đặc điểm:

  1. Trình debug đơn giản
  2. Hỗ trợ đánh dấu lỗi và hoàn thành mã tự động

Rodeo

Rodeo được định nghĩa là một trong những IDE tốt nhất cho python được sử dụng rộng rãi nhất cho các dự án khoa học dữ liệu như lấy dữ liệu và thông tin từ các tài nguyên khác nhau.

Nó hỗ trợ chức năng đa nền tảng và cung cấp tự động hoàn thành mã.

Tính năng, đặc điểm:

  1. Cho phép các chức năng so sánh dữ liệu, tương tác, vẽ đồ thị và kiểm tra dữ liệu.
  2. Hoàn thành mã tự động, highlight cú pháp, trình điều hướng tệp trực quan, v.v.

Microsoft Visual Studio

Microsoft Visual Studio là một trình soạn thảo mã nguồn mở phù hợp nhất để phát triển và gỡ lỗi các dự án web và đám mây mới nhất. Nó có thị trường riêng cho các phần mở rộng.

Tính năng, đặc điểm:

  1. Hỗ trợ mã hóa Python trong Visual studio
  2. Có sẵn ở cả phiên bản trả phí và miễn phí

Eric Python

Eric Python là một trình soạn thảo được phát triển bằng chính Python và có thể được sử dụng cho cả công việc chuyên nghiệp và không chuyên nghiệp.

Tính năng, đặc điểm:

  1. Cung cấp bố trí cửa sổ cấu hình, editor
  2. Khả năng quản lý dự án nâng cao, kiểm soát phiên bản
  3. Trình debug tích hợp và hỗ trợ quản lý tác vụ

Bài viết gốc được đăng tải tại ucode.vn

Xem thêm các việc làm developer hấp dẫn tại TopDev

MySQL: case vs. if vs. if function

Bài viết được sự cho phép của tác giả Nguyễn Văn Minh

Bạn đã bao giờ tự hỏi: Trong MySQL, câu lệnh CASE, câu lệnh IF và hàm IF khác nhau thế nào? Bạn có thấy phân vân khi chọn một trong ba thứ trên để viết query? Đây không phải câu hỏi mới nhưng nhiều bạn sẽ bỡ ngỡ khi tiếp xúc với nó. Nhất là khi bạn vừa bắt đầu tìm hiểu về MySQL và cơ sở dữ liệu quan hệ.

  Kinh nghiệm vận hành MySQL - Chú ý khi chọn MySQL làm database
  PHP & MySQL: Novice to Ninja, 5th Edition

Nếu bạn muốn tìm hiểu cơ sở dữ liệu là gì và có những loại nào, hãy tham khảo bài viết này.

Cú pháp

Câu lệnh CASE

CASE value WHEN [compare_value] THEN result [WHEN [compare_value] THEN result ...] [ELSE result] END
CASE WHEN [condition] THEN result [WHEN [condition] THEN result ...] [ELSE result] END

Câu lệnh IF

IF condition1 THEN
   {...statements to execute when condition1 is TRUE...}

[ ELSEIF condition2 THEN
   {...statements to execute when condition1 is FALSE and condition2 is TRUE...} ]

[ ELSE
   {...statements to execute when both condition1 and condition2 are FALSE...} ]

END IF;

Hàm IF

IF(expr1,expr2,expr3)

Đào sâu hơn

Nhìn vào cú pháp trên đây, ta có thể thấy, dường như hàm IF ít linh hoạt hơn câu lệnh CASE. Nếu bạn viết thế này:

SELECT IF(movie = 'The Matrix', 'high', 'low') AS suggestion

Thì bạn hoàn toàn có thể dùng CASE như thế này:

SELECT CASE WHEN movie = 'The Matrix' THEN 'high' ELSE 'low' END AS suggestion

Khá là giống nhau đúng không? Trừ việc hàm IF trông gọn gàng hơn chút ít. Nhưng nếu có nhiều hơn hai nhánh thì sao? Có lẽ bạn sẽ không muốn viết thế này:

SELECT IF(movie = 'The Matrix', 'high', IF(movie = 'Endgame', 'medium', 'low')) AS suggestion

Mà nên là thế này:

SELECT CASE movie 
    WHEN = 'The Matrix' THEN 'high'
    WHEN = 'Endgame'    THEN 'medium'
    ELSE 'low'
END AS suggestion

Nó cũng tương tự như khi ta dùng switch để rẽ nhánh vậy, tự nhiên vào thoải mái hơn rất nhiều.

Có một điều bạn phải chú ý. Trong khi câu lệnh CASE là câu lệnh chuẩn của SQL thì hàm IF lại hoàn toàn không phải. Điều đó có nghĩa gì? Nếu bạn có ý định chuyển sang dùng SQL Server hay PostgreSQL chẳng hạn, hàm IF sẽ không còn hoạt động nữa.

Ở một diễn biến khác, câu lệnh IF là cái gì đó rất lạ lẫm với hai thứ trên. Nó được dùng khi viết thủ tục (procedure). Ví dụ:

CREATE FUNCTION get_suggestion (movie varchar(50))
RETURNS varchar(20)
BEGIN
   IF movie = 'The Matrix' THEN
      return 'high';
   ELSEIF movie = 'Endgame' THEN
      return 'medium';
   ELSE
     return 'low';
   END IF;
END;

Do đó, chớ nên nhầm lẫn mục đích sử dụng của câu lệnh IF với hàm IF, hay thậm chí câu lệnh CASE. Chúng sinh ra vì những “sứ mệnh” khác nhau.

Generate fake UUID cực kì đơn giản

Bài viết được sự cho phép của tác giả Nguyễn Hữu Đồng

UUID là viết tắt của Universally Unique IDentifier. Mình chỉ hiểu đơn giản đây là một mã định danh duy nhất là một số gồm 128bit. Tổng cộng có 32 kí tự chia thành 5 phần cách nhau bởi 4 dấu gạch nối đơn cử

123e4567-e89b-12d3-a456-426655440000

Để generate ra một UUID thì bạn hoàn toàn có thể dùng 1 package nào đó mà khỏi cần nghĩ nhiều, nhưng mình sẽ tự generate ra 1 fake uuid bằng cách random ra một slice byte gồm 16 phần tử (8 bit) và sau đó để cho đẹp mắt thì mình sẽ convert hệ thập phân về base16.

  10 kênh Youtube học lập trình không thể bỏ qua dành cho Junior Web Developer / Designer

  10 câu nói cực hay về lập trình

Vì sao là 16 phần tử và base 16. Thứ nhất mình muốn các kí tự trong uuid là chữ hoặc số A-Z 0–9, cộng thêm cty mình đang làm 1 dự án mang tên là HexSafe, mà hex là hexadecimal thập lục phân là 16 luôn. Và nếu số 8bit phân sang base16 thì chỉ tốn hai char nên mình sẽ random 16 số 8 bit.

b := make([]byte, 16)
_, err := rand.Read(b)	
if err != nil {
   return "", err
}

Sau đó mình sẽ lấy 4 số cho phần đầu, tiếp theo là 2,2,2 và cuối cùng là 6 số,

để chuyển qua hex thì quá easy luôn. Có sẵn fmt.Sprintf(“%X”,n) để convert n về base16 dạng in hoa.

fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil

Mình có kết quả sau 3 lần generate

CB0C7CF2-B9C0-9133-0C6B-0EB0380305D8 <nil>
FA18D239-BF14-4D82-B2E6-1000DA7870AF <nil>
EDB05EC8-8AB1-E994-6E7E-67AB30708872 <nil>

Mặc dù là fake uuid, trên lý thuyết là có thể trùng lẫn nhau xác xuất 1/(256¹⁶)
và nếu nó xảy ra thì thôi ý trời :)) đến UUID còn có thể trùng nhau cơ mà.

Đây là full đoạn code gen uuid của mình.

package main
import (
   "crypto/rand"
   "fmt"
)
func main() {
   f.mt.Println(uuid())
   fmt.Println(uuid())
   fmt.Println(uuid())
}
func uuid() (string, error) {
   b := make([]byte, 16)
   _, err := rand.Read(b)
   if err != nil {
    return "", err
   }
   return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8],b[8:10], b[10:]), nil
}

Happy coding ^_^

Nguồn : https://twitter.com/pliutau

Bài viết gốc được đăng tải tại medium.com

Có thể bạn quan tâm:

Xem thêm các việc làm Developer hấp dẫn tại TopDev

Các cách khai báo function trong JS

Bài viết được sự cho phép của tác giả Lưu Bình An

Khai báo tường minh

Cách khai báo function “vở lòng” mà ai cũng phải biết, sau từ khóa function là tên function chúng ta muốn khai báo

  5 kinh nghiệm khi viết arrow function
  5 điểm khác nhau giữa function thường và arrow function
function foo() {
	console.log(‘vui lap trinh’)
}

Gán function vào một biến

Chúng ta khai báo một biến, gán giá trị của biến này là một function

const foo = function () {
	console.log(‘vui lap trinh’)
}

Với cách này, lưu ý là không dùng function trước khi khai báo, giống như chúng ta không thể dùng một biến mà chưa được khai báo vậy.

foo(); // Uncaught ReferenceError: foo is not defined

const foo = function() {
   console.log(‘vui lap trinh’)

}

Arrow function

Khai báo function bằng dấu => mũi tên, bên trái là params của function, bên phải là phần ruột function

() => console.log(‘vui lap trinh’)

// hoặc bỏ luôn ngoặc kép
[‘An’, ‘luckyluu’, ‘vui’, ‘laptrinh’]
  .filter(name => name.length > 5)
  .map(name => name.toLowerCase())

Lưu ý cho arrow function, sẽ không có

  • object đặc biệt arguments, các function khác sẽ có cái object tên là arguments chứa các tham số truyền vào cho function đó
  • Ko có gọi new (() => {})
  • Không có thissupernew.target

constructor

Ko được khuyến khích sử dụng, đọc cho vui

const myStrangeFunc = new Function("a", "console.log(a + ' with Functions')");
myStrangeFunc("Fun"); // logs --> "Fun with Functions"

3 thằng còn lại, cũng không phổ biến, chưa thấy giá trị sử dụng

  • generator function expression
  • generator function declaration
  • GeneratorFunction constructor

    Nếu có đam mê bạn có thể tìm hiểu thêm

Bài viết gốc được đăng tải tại vuilaptrinh.com

Có thể bạn quan tâm:

Xem thêm các việc làm Developer hấp dẫn tại TopDev

Tổng hợp 15+ CV Template IT đẹp và chuyên nghiệp

CV đóng một vai trò rất quan trọng đối với việc thu hút nhà tuyển dụng. Một CV Template IT đúng chuẩn lại giúp gia tăng nhiều hơn các lợi thế ở ứng viên. Vậy liệu dân lập trình IT đã có những hình dung chính xác về CV online IT hay chưa? Cùng TopDev điểm qua top các mẫu CV Template IT (IT CV Template) đẹp chuẩn.

Những lưu ý ban đầu về CV Template IT

Dù đó là CV cho sinh viên IT mới ra trường hay CV ứng tuyển các vị trí Junior Developer, Senior Developer,… bạn vẫn phải đầu tư cho CV IT Developer của mình. Nó là tài liệu quan trọng ghi dấu ấn trong sự nghiệp phát triển của bạn. Đồng thời, dựa trên các thông tin bạn truyển tải, nhà tuyển dụng có thể đánh giá được các tiêu chí về trình độ, năng lực, tiềm năng phát triển,…

  Cách viết CV giúp lập trình viên ghi điểm với nhà tuyển dụng
  5 mẹo và mẫu CV IT để gây ấn tượng với nhà tuyển dụng!

Hãy lưu tâm vấn đề này để CV Template IT của bạn chuẩn format, Phải nhớ nó không giống sơ yếu lý lịch đơn thuần. CV ngành IT nhấn mạnh các giá trị nổi bật về kỹ năng, trình độ chuyên môn, quan điểm – mục tiêu,… 

Đồng thời, viết CV giúp bạn định hình và có những trải nghiệm tốt hơn. Trường hợp bạn ứng tuyển các vị trí khác như freelancer it hay Senior Developer đều sẽ đạt hiệu quả ứng tuyển cao hơn.

Lợi thế điểm nhấn có thể đến từ các lưu ý về kỹ năng và các khoa học chuyên sâu dưới đây:

Kỹ năng (Skills)

Kỹ năng duy trì các ứng dụng phần mềm khoa học hiện. Phát triển các ứng dụng mới hơn.

Kết nối và chia sẻ kinh nghiệm áp dụng các nguyên tắc, chiến thuật phát triển dựa trên các nền tảng lý thuyết.

Kỹ năng phân tích, xử lý – giải quyết vấn đề.

Kỹ năng giao tiếp 

Đây là các kỹ năng khá quan trọng mà bạn cần bổ sung vào IT CV Template.

Tạo CV IT chuẩn ATS miễn phí trên TopDev

Các khóa học chuyên sâu (Specialize Courses)

+ Phát triển phần mềm (Software Development)

+ Coding cơ bản và nâng cao (Coding Basics, Advanced)

+ Quản lý dự án (IT Project Management)

+ An ninh Mạng (Cyber Security)

+ Quản lý bảo trì thiết bị (CMMS)

+ Xử lý sự cố (Troubleshooting)

Và các khóa học khác…

15+ IT CV Template chuẩn chuyên nghiệp nhất

Mẫu Template IT chuẩn 

CV cho sinh viên IT mới ra trường
M1 – CV Template đa nhiệm
Senior Developer
M2 – CV Template đa nhiệm
CV ngành IT
M3- CV Template đa nhiệm
CV cho sinh viên IT mới ra trường
CV Pro 1
Senior Developer
CV Pro 2
cv ngành IT
M1 – CV cổ điển
CV cho sinh viên IT mới ra trường
M2 – CV cổ điển

IT Programmer CV 

Senior Developer
IT Programmer CV

Data Scientist

CV ngành IT
Data Scientist

Fullstack Developer

CV cho sinh viên IT mới ra trường
Fullstack Developer

CV IT Manager

Senior Developer
CV IT Manager

IT Project Manager

CV ngành IT
IT Project Manager

Java Developer

CV cho sinh viên IT mới ra trường
Java Developer

Networking Engineer

Senior Developer
Networking Engineer

PHP Developer

CV ngành IT
PHP Developer

Product Manager

CV cho sinh viên IT mới ra trường
Product Manager

Data Analyst

Senior Developer
Data Analyst

UX Designer

CV ngành IT
UX Designer

Có thể bạn quan tâm:

Xem thêm Top Việc làm Developer trên TopDev

Toán tử instanceof trong Java

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Toán tử instanceof trong Java là một toán tử được sử dụng để kiểm tra xem đối tượng này có phải là instance của một class hay interface nào đó hay không? Kết quả trả về của toán tử này sẽ là true nếu đối tượng đó là thể hiện của class mà các bạn đang check, ngược lại thì false.

Tuyển lập trình viên Java nhiều công ty HOT

Ví dụ, mình có một class Application như sau:

package com.huongdanjava.java;

public class Application {

public static void main(String[] args) {
Application application = new Application();
System.out.println(application instanceof Application);
}

}

Trong hàm main() của class này, mình initialize một đối tượng của class Application và sử dụng toán tử instanceof để kiểm tra xem đối tượng này có phải là instance của class Application này hay không? Các bạn sẽ thấy kết quả sẽ như sau:

Toán tử instanceof trong Java

Nếu các bạn viết code như sau:

package com.huongdanjava.java;

public class Application {

public static void main(String[] args) {
Application application = new Application();
System.out.println(application instanceof String);
}

}

thì IDE sẽ báo lỗi ngay:

Toán tử instanceof trong Java

Đây là trong trường hợp quá tường minh, quá rõ ràng, IDE có thể báo lỗi cho các bạn biết ngay.

Nhưng nếu bạn có một interface với hai implementation như sau:

package com.huongdanjava.java;

public interface Shape {

}
package com.huongdanjava.java;

public class Rectangle {

}

thì lúc này nếu các bạn initialize đối tượng của class Triangle nhưng lại đi kiểm tra đối tượng này có phải là thể hiện của class Rectangle,

package com.huongdanjava.java;

public class Application {

public static void main(String[] args) {
Shape shape = new Triangle();

System.out.println(shape instanceof Rectangle);
}

}

IDE sẽ không thể detect lỗi lúc compile time nhưng khi chạy các bạn sẽ thấy kết quả như sau:

Toán tử instanceof trong Java

Chúng ta sẽ thường sử dụng toán tử instanceof trong trường hợp kiểm tra xem tham số truyền vào của một phương thức có phải là instance của một class nào đó hay không? Ví dụ như:

private void check(Shape shape) {
    if (shape instanceof Triangle) {
        Triangle triangle = (Triangle) shape;

        System.out.println("This is triangle: " + triangle.toString());
    }
}

Kết quả:

package com.huongdanjava.java;

public class Application {

private void check(Shape shape) {
if (shape instanceof Triangle) {
Triangle triangle = (Triangle) shape;

System.out.println("This is triangle: " + triangle.toString());
}
}

public static void main(String[] args) {
Application application = new Application();
application.check(new Triangle());
}

}

Toán tử instanceof trong Java

Từ Java 14, các bạn có thể viết lại phương thức check() sử dụng pattern matching instanceof, đơn giản như sau:

private void check(Shape shape) {
    if (shape instanceof Triangle triangle) {
        System.out.println("This is triangle: " + triangle.toString());
    }
}

Với cách viết mới, chúng ta không cần viết thêm một dòng code để cast instance về đối tượng mà chúng ta muốn nữa. Tất cả sẽ được thực hiện trong dòng lệnh if.

Kết quả vẫn như vậy:

Toán tử instanceof trong Java

Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:

Hướng dẫn sử dụng Factory trong Design Pattern

Bài viết được sự cho phép của tác giả Nguyễn Thảo

Xin chào các bạn, bài viết hôm nay mình sẻ giới thiệu đến các bạn Factory Design Pattern dùng thế nào trong lập trình C#, Winform.

[C#] How to using Factory Design Pattern

Factory Pattern  là một design pattern thuộc nhóm khởi tạo (Creational patterns).

Pattern này được sinh ra nhằm mục đích khởi tạo một đối tượng mới mà không cần thiết phải chỉ ra một cách chính xác class nào sẽ được khởi tạo.

  30 tiện ích Chrome cho designer và dev
  9 công cụ siêu tiện lợi cho cả Developer và Designer

Factory Method Pattern giải quyết vấn đề này bằng cách định nghĩa một factory method cho việc tạo đối tượng, và các lớp con thừa kế có thể override phương thức này để chỉ rõ đối tượng nào sẽ được khởi tạo.

Hướng dẫn sử dụng Factory trong Design Pattern

Bây giờ, mình sẽ có một ví dụ, cho các bạn dễ hiểu về factory design pattern này.

Mình có yêu cầu viết một ứng dụng, nạp tiền điện thoại cho các mạng điện thoại Việt Nam: Mobifone, VinaPhone, Viettel, Vietnamobile, Gmobile.

Request yêu cầu gởi đến ứng dụng của chúng ta gồm 2 tham số: số thoại cần nạp tiền và số tiền.

Thường trong bài viết này, chúng ta sẽ sử dụng một thiết bị modern GMS và sử dụng tập lệnh AT-Command xài lệnh USSD để nạp tiền vào tài khoản cho người dùng.

Nhưng có rắc rối ở đây là mỗi nhà mạng, đều có cấu trúc kiểm tra số dư tài khoản, hay cú pháp nạp tiền đều khác nhau.

Vậy làm sao khi gởi đến chúng ta sẽ điều hướng cho từng class nhà mạng.

Sơ đồ Design Pattern:

Hướng dẫn sử dụng Factory trong Design Pattern

Đầu tiên, mình sẽ tạo một InterFace INetwork.cs C#:

namespace FactoryPatternDemo
{
    public interface INetwork
    {
        string GetNameNetWork();
        string CheckAccountMoney();
        string GetCarrierNumber();

    }
}
C#

Trong này mình khai báo ba phương thức sẵn: lấy tên nhà mạng, kiểm tra số dư tài khoản, và lấy đầu số của mỗi nhà mạng.

Tiếp đến mình sẽ tạo 5 class cho mỗi nhà mạng: viettel.cs, mobifone.cs, vinaphone.cs, vietnamobile.cs, gmobile.cs

Mỗi class này mình đều implement đến interface INetwork

  1. Viettel.cs
class Viettel : INetwork
{
    public string CheckAccountMoney()
    {
        return "*101#";
    }

    public string GetCarrierNumber()
    {
        return "086, 096, 097, 098, 032, 033, 034, 035, 036, 037, 038, 039";
    }

    public string GetNameNetWork()
    {
        return "VIETTEL";
    }
    
}
C#

2. Mobifone.cs

class Mobifone : INetwork
{
    public string CheckAccountMoney()
    {
        return "*101#";
    }

    public string GetCarrierNumber()
    {
        return "090, 093, 0120, 0121, 0122, 0126, 0128, 089";
    }

    public string GetNameNetWork()
    {
        return "MOBIFONE";
    }
}
C#

3. Vinaphone.cs

public class Vinaphone : INetwork
{
    public string CheckAccountMoney()
    {
        return "*101#";
    }

    public string GetCarrierNumber()
    {
        return "091, 094, 083, 084, 085, 081, 082";
    }

        public string GetNameNetWork()
    {
        return "VINAPHONE";
    }
}
C#

4. Vietnamobile.cs

public class Vietnamobile : INetwork
{
    public string CheckAccountMoney()
    {
        return "*101#";
    }

    public string GetCarrierNumber()
    {
        return "092, 056, 058";
    }

    public string GetNameNetWork()
    {
        return "VIETNAMOBILE";
    }
}
C#

5. Gmobile.cs

public class Gmobile : INetwork
{
    public string CheckAccountMoney()
    {
        return "*101#";
    }

    public string GetCarrierNumber()
    {
        return "099, 059";
    }

    public string GetNameNetWork()
    {
        return "GMOBILE";
    }
}
C#

Tiếp đến mình sẽ tạo một Enum NetworkType.cs

public enum NetworkType
{
    VIETTEL,
    MOBIFONE,
    VINAPHONE,
    VIETNAMOBILE,
    GMOBILE,
}
C#

Tạo một class abstract NetworkFactory.cs:

public abstract class NetworkFactory
{
    public abstract INetwork Create(NetworkType type);
}
C#

Tiếp đến là một class ConcreteCreator.cs để điều hướng cho từng nhà mạng:

class ConcreteCreator : NetworkFactory
{
    public override INetwork Create(NetworkType type)
    {
        switch (type)
        {
            case NetworkType.VIETTEL:
                return new Viettel();
               
            case NetworkType.MOBIFONE:
                return new Mobifone();

            case NetworkType.VINAPHONE:
                return new Vinaphone();

            case NetworkType.VIETNAMOBILE:
                return new Vietnamobile();

            case NetworkType.GMOBILE:
                return new Gmobile();
            default: 
                throw new ArgumentException("Invalid type", "type");

        }
    }
}
C#

Và bây giờ chúng ta sẽ sử dụng hàm trong form program.cs

namespace FactoryPatternDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConcreteCreator();
            INetwork viettel = factory.Create(NetworkType.VIETTEL);
            Console.WriteLine(viettel.GetNameNetWork());
            Console.WriteLine(viettel.GetCarrierNumber());
            Console.WriteLine("===========================================");

            INetwork vinaphone = factory.Create(NetworkType.VINAPHONE);
            Console.WriteLine(vinaphone.GetNameNetWork());
            Console.WriteLine(vinaphone.GetCarrierNumber());
            Console.WriteLine("===========================================");

            INetwork mobiphone = factory.Create(NetworkType.MOBIFONE);
            Console.WriteLine(mobiphone.GetNameNetWork());
            Console.WriteLine(mobiphone.GetCarrierNumber());
            Console.WriteLine("===========================================");

            INetwork vietnamobile = factory.Create(NetworkType.VIETNAMOBILE);
            Console.WriteLine(vietnamobile.GetNameNetWork());
            Console.WriteLine(vietnamobile.GetCarrierNumber());
            Console.WriteLine("===========================================");

            INetwork gmobile = factory.Create(NetworkType.GMOBILE);
            Console.WriteLine(gmobile.GetNameNetWork());
            Console.WriteLine(gmobile.GetCarrierNumber());

            Console.ReadLine();
        }
    }
}
C#

Và dưới đây là kết quả khi chúng ta chạy ứng dụng:

Hướng dẫn sử dụng Factory trong Design Pattern

Bây giờ, các bạn muốn viết hàm gì chung xử lý cho từng nhà mạng điện thoại, các bạn chỉ cần khai báo tên hàm vào Interface INetwork.cs.

Và sau đó các bạn vào từng class nhà mạng Override lại phương thức để thực hiện theo mong muốn của mình.

Hy vọng bài viết sẽ giúp ích được cho các bạn.

Thanks for watching!

Bài viết gốc được đăng tải tại laptrinhvb.net

Có thể bạn quan tâm:

Xem thêm các việc làm designer hấp dẫn tại TopDev