Khóa học

Nếu bạn đang cân nhắc bắt đầu sự nghiệp trong khoa học dữ liệu, càng sớm viết mã, bạn sẽ càng có lợi. Bất kể ngôn ngữ lập trình bạn chọn để khởi đầu hành trình học tập là gì, đến một thời điểm bạn sẽ phải làm việc với các hàm.
Hàm là một khái niệm cốt lõi trong hầu hết các ngôn ngữ lập trình hiện đại, bao gồm các ngôn ngữ phổ biến trong khoa học dữ liệu như Python, Julia và dĩ nhiên là R.
Trong hướng dẫn này, chúng ta sẽ tìm hiểu hàm trong R là gì và cách bạn có thể sử dụng chúng. Bằng cách trình bày mục đích, cú pháp và các loại hàm có trong R, bạn sẽ có mọi thứ cần thiết để nắm vững khái niệm quan trọng này trong lập trình. Và hơn thế nữa, bạn sẽ được giới thiệu nghệ thuật tạo hàm.
Hàm trong R là gì?
Trong lập trình, hàm là các chỉ dẫn được tổ chức với nhau để thực hiện một tác vụ cụ thể. Lý do đằng sau việc dùng hàm là tạo ra những chương trình độc lập, chỉ được gọi khi cần.
Nhờ có hàm, lập trình viên không còn phải viết chương trình từ đầu, qua đó tránh lặp lại và cải thiện độ vững chắc cũng như khả năng đọc của mã. Vì vậy, theo kinh nghiệm thực tế, mỗi khi bạn dự kiến chạy một tập hợp chỉ dẫn cụ thể hơn hai lần trong mã, tốt nhất là tạo một hàm.
Hàm có thể được dùng cho vô vàn mục đích và ở nhiều hình thức. Nói chung, đa số hàm sẽ nhận dữ liệu đầu vào, xử lý và trả về kết quả. Dữ liệu mà hàm thao tác được xác định bởi các đối số (arguments), vốn cũng có thể được dùng để kiểm soát hoặc thay đổi cách hàm thực hiện tác vụ.
Tùy vào nguồn gốc, ta có thể phân biệt ba loại hàm chính trong R:
- Hàm dựng sẵn
- Hàm có trong các gói R
- Hàm do người dùng định nghĩa (UDF)
Trong các phần tiếp theo, chúng tôi sẽ giải thích những điểm riêng của từng loại hàm có trong R.
Hàm dựng sẵn trong R
R là một ngôn ngữ lập trình mạnh mẽ, đi kèm danh mục lớn các hàm dựng sẵn có thể gọi bất cứ lúc nào. Là một ngôn ngữ thiên về toán, R có nhiều hàm để thực hiện các phép toán số học. Dưới đây là danh sách một số hàm hữu ích nhất:
- print(). Hiển thị một đối tượng R trên bảng điều khiển R
- min(), max(). Tính giá trị nhỏ nhất và lớn nhất của một vector số
- sum(). Tính tổng của một vector số
- mean(). Tính giá trị trung bình của một vector số
- range(). Tính giá trị nhỏ nhất và lớn nhất của một vector số
- str(). Hiển thị cấu trúc của một đối tượng R
- ncol(). Trả về số cột của một ma trận hoặc dataframe
- length(). Trả về số phần tử trong một đối tượng R, như vector, list và ma trận.
Trong đoạn mã dưới đây, bạn có thể thấy việc dùng các hàm này để tính một số thống kê từ một vector đơn giản như thế nào:
>>> v <- c(1, 3, 0.2, 1.5, 1.7)
>>> print(v)
[1] 1.0 3.0 0.2 1.5 1.7
>>> sum(v)
[1] 7.4
>>> mean(v)
[1] 1.48
>>> length(v)
[1] 5
Bạn có thể tìm hiểu về hàm trong R và nhiều nội dung khác trong lộ trình kỹ năng R toàn diện của chúng tôi, giúp bạn học viết mã như một lập trình viên.
Hàm trong các gói R
Mặc dù phong phú và đa dạng, các hàm dựng sẵn là chưa đủ để làm mọi điều thú vị bạn có thể làm với R, từ vẽ trực quan hóa dữ liệu hấp dẫn đến huấn luyện các mô hình machine learning mạnh mẽ.
Phần lớn các hàm để thực hiện những tác vụ này có trong các gói hoặc thư viện bên ngoài. Gói là tập hợp các hàm R, dữ liệu và mã biên dịch theo định dạng chuẩn nhằm bổ sung chức năng cụ thể. Hầu hết các gói này có thể dùng miễn phí và có thể tìm thấy trong các kho gói phổ biến như CRAN, hiện có gần 20.000 gói do cộng đồng đóng góp.
Để dùng các hàm có trong một gói, trước tiên bạn cần cài đặt nó. Ví dụ, nếu bạn muốn cài stringr, một gói phổ biến để làm việc với biểu thức chính quy, bạn có thể dùng câu lệnh sau:
install.packages('stringr')
Sau khi cài xong, để nạp vào môi trường R của bạn, hãy dùng câu lệnh library
library(stringr)
Giờ bạn đã sẵn sàng dùng mọi hàm có trong gói stringr. Chẳng hạn, hãy thử hàm str_detect(), hàm này trả về một vector logic với TRUE cho mỗi phần tử chuỗi khớp mẫu và FALSE nếu không.
str_detect('DataCamp', "Data")
[1] TRUE
Nếu bạn muốn biết thêm về các gói R và cách sử dụng, hãy xem hướng dẫn về gói R của DataCamp.
Hàm do người dùng định nghĩa
Cách tốt nhất để hiểu hàm trong R hoạt động thế nào là tự tạo hàm của bạn. Các hàm do người dùng định nghĩa (UDF) được lập trình viên thiết kế để thực hiện một tác vụ cụ thể.
Các hàm R thường có cú pháp như sau:
function_name <- function(argument_1, argument_2) {
function body
return (output)
}
Ta có thể phân biệt bốn thành phần chính:
- Tên hàm. Để tạo UDF, trước tiên bạn phải gán cho nó một cái tên và lưu thành một đối tượng mới. Bạn chỉ cần gọi tên đó mỗi khi muốn dùng hàm.
- Đối số (arguments). Các đối số của hàm (còn gọi là tham số) được cung cấp trong dấu ngoặc tròn. Đối số rất quan trọng để hàm biết dữ liệu nào cần lấy làm đầu vào và/hoặc cách điều chỉnh hành vi của hàm.
- Thân hàm. Trong dấu ngoặc nhọn là thân hàm, tức các chỉ dẫn để giải quyết một tác vụ cụ thể dựa trên thông tin do đối số cung cấp.
- Câu lệnh trả về (return). Câu lệnh return là cần thiết nếu bạn muốn hàm lưu kết quả hoặc các kết quả của những phép toán trong thân hàm thành biến.
Ví dụ, nếu bạn muốn tạo một hàm tính trung bình của hai số:
mean_two_numbers <- function(num_1, num_2) {
mean <- (num_1 + num_2) / 2
return (mean)
}
Giờ nếu bạn muốn tính trung bình của 10 và 20, chỉ cần gọi hàm như sau:
>>> mean_two_numbers(10,20)
[1] 15
Các loại đối số trong hàm R
Đối số là thành phần thiết yếu trong mọi hàm. Dù về mặt lý thuyết có thể viết một hàm không có tham số (xem ví dụ dưới), hầu hết các hàm đều có đối số. Điều này hợp lý, vì đối số cho hàm biết dữ liệu nào cần lấy làm đầu vào. Tương tự, nếu ta muốn trang bị cho một hàm nhiều cách thực hiện một tác vụ, các đối số sẽ đảm nhiệm việc đó.
hello <- function() {
print('hello, my friend')
}
>>> hello()
[1] "hello, my friend"
Không có giới hạn về số lượng đối số trong hàm R; bạn có thể thêm chúng trong ngoặc tròn, cách nhau bởi dấu phẩy. Nói chung, hàm có nhiều đối số thường phức tạp hơn.
Sau khi bạn tạo một hàm có tham số, mỗi lần muốn dùng nó, bạn sẽ phải cung cấp giá trị cho các tham số; nếu không, R sẽ báo lỗi. Chẳng hạn, nếu bạn không cung cấp giá trị của hai số để tính trung bình, hàm của chúng ta sẽ không hoạt động.
Tuy nhiên, bạn có thể tránh lỗi này bằng cách dùng đối số mặc định khi định nghĩa hàm. Đối số mặc định cung cấp một giá trị sẽ được dùng nếu bạn gọi hàm mà không truyền đối số đó. Quay lại hàm tính trung bình của hai số. Lần này, ta sẽ định nghĩa để thêm một đối số mặc định cho số thứ hai.
mean_two_numbers <- function(num_1, num_2 = 30) {
mean <- (num_1 + num_2) / 2
return (mean)
}
Nếu giờ ta gọi hàm mà không cung cấp giá trị cho tham số num_2, R sẽ tự động lấy giá trị mặc định (tức là 30):
>>> mean_two_numbers(num_1 = 10)
[1] 20
Hiểu về giá trị trả về trong hàm R
Các hàm thường nhận một số dữ liệu làm đầu vào và cho ra kết quả làm đầu ra. Trong một số ngôn ngữ lập trình, để lưu kết quả của một hàm thành biến, bạn cần đưa rõ câu lệnh return ở cuối thân hàm. Nếu không, hàm chỉ hiển thị một giá trị tồn tại trong phạm vi biến.
Điều này không đúng với R, vốn luôn trả về một giá trị có thể được lưu vào biến. Tuy vậy, vì tính dễ đọc, luôn nên đưa câu lệnh return khi định nghĩa một hàm.
mean_two_numbers <- function(num_1, num_2) {
# Function with return
mean <- (num_1 + num_2) / 2
return (mean)
}
mean_two_numbers_2 <- function(num_1, num_2) {
# Function without return
mean <- (num_1 + num_2) / 2
mean
}
> mean_two_numbers(10,50)
[1] 30
> mean_two_numbers_2(10,50)
[1] 30
Cuối cùng, nếu bạn muốn hàm trả về nhiều giá trị, bạn sẽ phải lưu các kết quả khác nhau vào một list và đưa list đó vào câu lệnh return:
mean_sum <- function(num_1, num_2) {
mean <- (num_1 + num_2) / 2
sum <- num_1 + num_2
return (list(mean, sum))
}
>>> mean_sum (10, 20)
[[1]]
[1] 15
[[2]]
[1] 30
Gọi hàm trong R
Ở các phần trước, chúng ta đã thấy nhiều ví dụ về cách gọi hàm. Tuy nhiên, điều quan trọng là làm rõ R hoạt động bên trong thế nào khi ta truyền các đối số.
R chấp nhận hai cách truyền đối số: theo vị trí và theo tên. Nếu theo cách đầu tiên, ta sẽ phải viết các giá trị theo đúng thứ tự đối số như được định nghĩa trong hàm.
Nếu truyền đối số theo tên, ta cần chỉ rõ tên các đối số và giá trị tương ứng. Vì đã ghép cặp tên và giá trị, thứ tự không còn quan trọng.
Cuối cùng, cũng có thể kết hợp cả hai cách. Khi đó, các đối số có tên sẽ được tách ra và ghép cặp trước, còn lại sẽ được ghép theo vị trí.
hello <- function(name, surname) {
print(paste('Hello', name, surname))
}
# Calling arguments by position
> hello('Greta','Thunberg')
[1] "Hello, Greta Thunberg"
# Calling arguments by name
> hello(surname='Thunberg', name='Greta')
[1] "Hello, Greta Thunberg"
# Calling arguments by position and by name
> hello(surname='Thunberg', 'Greta')
[1] "Hello, Greta Thunberg"
Ghi chú tài liệu cho hàm trong R
Một thực hành tốt khi tạo hàm là cung cấp tài liệu hướng dẫn cách sử dụng, đặc biệt khi hàm phức tạp. Cách không chính thức là thêm chú thích trong thân hàm. Bạn có thể xem phần tài liệu này bằng cách gọi hàm mà không có tham số:
hello <- function(name, surname) {
# Say hello to a person with name and surname
print(paste('Hello', name, surname))
}
>>> hello
function(name, surname) {
# Say hello to a person with name and surname
print(paste('Hello,', name, surname))
Tuy nhiên, nếu hàm của bạn là một phần của gói lớn hơn và bạn muốn ghi chú tài liệu cho nó, bạn nên viết tài liệu chính thức trong một tệp .Rd riêng. Bạn sẽ thấy kết quả của tài liệu này khi xem tệp trợ giúp cho một hàm nào đó, ví dụ ?mean.
Kết luận
Bạn đã đến cuối hướng dẫn. Chúc mừng! Như trong nhiều ngôn ngữ khác, hàm là thành phần thiết yếu trong R. Dù là hàm dựng sẵn, phát triển như một phần của gói bên ngoài, hay do chính bạn tạo ra, việc làm chủ hàm là cột mốc quan trọng trong hành trình lập trình của bạn. Nếu muốn tiếp tục phát triển kỹ năng về hàm trong R, hãy xem các tài nguyên sau!