Python

[Trở thành 1 nhà khoa học máy tính] Bài 2:Biến, biểu thức và câu lệnh

1. Giá trị và kiểu giá trị. giá trị là 1 trong những nền tảng cơ bản nhất của mọi chương trình máy tính.Tương tự như vai trò của các con số trong toán học. Trong bài trước các biến mà chúng ta đã thấy là 2 (kết quả của phép cộng 1+1 ) và… Đọc tiếp [Trở thành 1 nhà khoa học máy tính] Bài 2:Biến, biểu thức và câu lệnh

lập trình · Python

[Trở thành 1 nhà khoa học máy tính] Bài 1: con đường đến chương trình máy tính

Mục tiêu của loạt bài này là biến người đọc (cũng như người dịch) cách suy nghĩ như một nhà khoa học máy tính thực sự (computer scientist). Cách suy nghĩ này là sự kết hợp những gì tốt nhất của những nhà toán học, kĩ sư và khoa học tự nhiên: Sử dụng các công… Đọc tiếp [Trở thành 1 nhà khoa học máy tính] Bài 1: con đường đến chương trình máy tính

Cadvance · lập trình

Con trỏ hàm trong C

Lưu ý: nội dung bài viết chỉ áp dụng đối với ngôn ngữ lập trình C(not C++ vì mình chưa học) 1.Giới thiệu: Trong C,Con trỏ hàm là một biến lưu trữ địa chỉ của một hàm, thông qua biến đó, ta có thể gọi hàm mà nó trỏ tới. con trỏ hàm được khai… Đọc tiếp Con trỏ hàm trong C

Cadvance · lập trình

Lập trình tổng quát(Generic programming) trong C

1. Giới thiệu

Là phương pháp lập trình tạo ra các hàm tổng quát có thể sử dụng lại trong nhiều bài toán khác nhau.

Ví dụ hàm memcpy() trong thư viện string.h:

void* memcpy(void* region1, const void* region2, size_t n);

Chức năng chính của hàm:Tương tự như hàm strncpy, nhưng khi dùng memcpy thì sẽ kiểm soát bộ nhớ, tránh lỗi tràn bộ nhớ.

Hàm memcpy() bên trên được khai báo tổng quát bằng cách sử dụng các con trỏ void* . Điều này giúp cho hàm có thể sử dụng với nhiều kiểu dữ liệu khác nhau. Hay nói cách khác, để sao chép dữ liệu, ta chỉ cần địa chỉ và kích cỡ của chúng.

Ví dụ 1 cách cài đặt của hàm memcpy():

void* memcpy(void* region1, const void* region2, size_t n){
            const char* first = (const char*) region2;
            const char* last = ((const char*) region2) + n;
            char* result = (char*) region1;
            while (first != last) *result++ = *first++;
            return result;
}

2. Tạo Hàm tổng quát

Trong cách hàm tổng quát, dữ liệu được truyền vào 1 cách tổng quát thông qua địa chỉ và kích thước kiểu dữ liệu.Nếu như hàm yêu cầu 1 hàm khác để thực hiện 1 thao tác nào đấy(như so sánh 2 dữ liệu) thì dữ liệu thuật toán so sánh được truyền vào thông qua con trỏ hàm.

Ví dụ: hàm qsort() trong thư viện stdio.h yêu cầu truyền vào hàm so sánh 2 phần tử:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))

(*compar)(const void *, const void*)) trong hàm ở trên chính là 1 con trỏ hàm !

1 hàm tổng quát được tạo ra bằng cách truyền các tham số cơ bản:

  • void *bù: địa chỉ của phần tử đầu tiên của mảng
  • int size: kích thước 1 phần tử trong mảng
  • int total: số phần tử của mảng
  • ….

 

Ví dụ 1 số hàm tổng quát :

  • Tìm kiếm trên mảng:

int search( void* buf,
            int size,
            int l, int r,
            void * item,
            int (*compare)(void*, void*)) {
     if (r < l) return -1;
     i = (l + r)/2;
     res = compare( item, (char*)buf+(size*i) );
     if (res==0)    return i;
     else if (res < 0)   return search(buf, size, l, i-1, item, compare);
     else           return search(buf, size, i+1, r, item, compare);
 }
  •  So sánh 2 phần tử:
int int_compare(void const* x, void const *y) {
 int m, n;
 m = *((int*)x);
 n = *((int*)y);
 if ( m == n ) return 0;
 return m > n ? 1: -1;
 }

3. Kiểu dữ liệu tổng quát

Tạo ra 1 kiểu dữ liệu tổng quát, nơi mà dữ liệu vừa có thể là số nguyên, số thực, kí tự thậm chí là bản ghi ? Thậm chí, kiểu dữ liệu tổng quát này phải thực sự hiệu quả để thực hiện các ADT như : linkedlist, queue, stack hay tree.
Kiểu hợp (Union) có thể là một cách hữu hiệu để thực thi một cấu trúc dữ liệu tổng quát:

 typedef union {
 int i;
 long l;
 float f;
 double d;
 void *v;
 char *s;
 char c;
 } Jval;

Kiểu jval trên có thể ứng dụng để lưu lại các dữ liệu khác nhau:

Jval a, b;
a.i = 5;
b.f = 3.14;

Để sử dụng đơn giản, 1 vài hàm khởi tạo được xây dựng:

  • Jval new_jval_i(int);
  • Jval new_jval_f(float);
  • Jval new_jval_d(double);
  • Jval new_jval_s(char *);

ví dụ về 1 vài cách cài đặt các hàm khởi tạo:

Jval new_jval_i(int i) { Jval j; j.i = i; return j; }
 Jval new_jval_l(long l) { Jval j; j.l = l; return j; }
 Jval new_jval_f(float f) { Jval j; j.f = f; return j; }
 Jval new_jval_d(double d) { Jval j; j.d = d; return j; }
 Jval new_jval_v(void *v) { Jval j; j.v = v; return j; }
 ....
 int jval_i(Jval j) { return j.i; }
 long jval_l(Jval j) { return j.l; }
 float jval_f(Jval j) { return j.f; }
 double jval_d(Jval j) { return j.d; }
 void *jval_v(Jval j) { return j.v; }
 ..
 

Luyện tập 1 chút: bạn đọc thử viết các hàm tổng quát ở phần 2 bằng cách sử dụng jval 😀