Menu

In đầu ra và đọc đầu vào trong R (print, cat, readline)

Cách đầu ra hoạt động trong R: tự động in, print() so với cat(), message(), và cách đọc dữ liệu người dùng nhập bằng readline() và readLines().

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Cách in hoạt động trong R

R có một thói quen mà không ngôn ngữ phổ biến nào khác có: ở console, bạn hoàn toàn không cần câu lệnh in. Gõ tên biến, nhấn Enter, và R in ra giá trị của nó. Đó là tự động in (auto-printing), và cũng là lý do các hướng dẫn R đầy những biểu thức trơn.

Nhưng tự động in có một ranh giới nghiêm ngặt: nó chỉ xảy ra với các biểu thức ở cấp cao nhất. Bên trong vòng lặp for hoặc thân hàm, một giá trị trơn được tính rồi lặng lẽ bị bỏ đi:

x đứng một mình được in ra (nó ở cấp cao nhất), vòng lặp thứ nhất không in gì cả, còn vòng lặp thứ hai in 1, 2, 3 vì nó yêu cầu rõ ràng. "Vòng lặp của mình chạy nhưng không hiện gì" là một trong những câu hỏi R được hỏi nhiều nhất, và đây là toàn bộ câu trả lời: bên trong vòng lặp và hàm, in ra luôn là việc của bạn. (Điều tương tự xảy ra khi bạn chạy script bằng Rscript: thứ chạy tốt từng dòng ở console có thể im lặng bên trong vòng lặp.)

print() hiển thị một giá trị theo cách R nhìn thấy nó, nên đây là công cụ đúng để xem xét dữ liệu, và là công cụ sai cho đầu ra trau chuốt:

Có hai điều nổi bật. Chuỗi giữ nguyên dấu ngoặc kép: print() đang cho bạn xem một biểu diễn trung thực, và dấu ngoặc kép cho biết đó là giá trị ký tự. Và mỗi dòng bắt đầu bằng một con số trong ngoặc vuông như [1]: đó là chỉ số của phần tử đầu tiên trên dòng đó, nên khi một vector dài xuống nhiều dòng, ngoặc vuông của mỗi dòng cho bạn biết dòng ấy tiếp tục từ phần tử nào. Đó là thứ giúp đọc vector, không phải nhiễu.

Nếu bạn muốn đầu ra kiểu print() mà không có ngoặc kép, cả print(x, quote = FALSE) lẫn noquote(x) đều làm được, nhưng thường lúc ấy thứ bạn thật sự cần là cat().

cat(): đầu ra cho người đọc

cat() (concatenate and print, nối rồi in) ghi văn bản thô: không ngoặc kép, không tiền tố chỉ số, các đối số được nối bằng dấu cách. Cái giá của sự kiểm soát đó là không có gì được thêm hộ bạn, kể cả ký tự xuống dòng:

Quên "\n" là bài học nhập môn ai cũng trải qua với cat(): không có nó, phần đầu ra tiếp theo dính luôn vào cùng dòng. Hãy tập phản xạ kết thúc các lệnh gọi cat() bằng "\n".

Khác biệt về dấu ngoặc kép giữa hai hàm thật ra là khác biệt về thứ chúng hiển thị. print() hiện các chuỗi thoát (escape sequence) đúng như bạn đã gõ; cat() thì diễn giải chúng:

print() hiện \t và \n nguyên văn bên trong ngoặc kép, rất hữu ích khi bạn gỡ lỗi để biết chính xác một chuỗi chứa gì. cat() biến chúng thành một tab thật và một dấu xuống dòng thật, hữu ích khi bạn tạo đầu ra. Chọn theo mục đích: print() để xem xét, cat() để trình bày.

Báo cáo có định dạng: sprintf() + cat()

Các đối số ngăn cách bằng dấu cách của cat() chỉ đi được đến một mức. Để có đầu ra thẳng hàng, có định dạng (số chữ số thập phân cố định, bộ đếm được đệm), hãy tạo chuỗi bằng sprintf() rồi giao cho cat():

%-8s đệm mỗi tên cho đủ 8 ký tự và %6.2f cho mọi điểm số cùng độ rộng với hai chữ số thập phân, nên các cột thẳng hàng. Cặp này là công cụ chủ lực của base R cho thông báo tiến độ và các báo cáo văn bản nhỏ (các mã định dạng được trình bày trong chuỗi).

message() và warning(): kênh còn lại

cat() và print() ghi ra đầu ra chuẩn (standard output), luồng chứa kết quả của chương trình. message() và warning() ghi ra luồng lỗi chuẩn (standard error), luồng dành cho các ghi chú về lần chạy:

Trên màn hình, hai thứ trông giống nhau, nhưng sự tách biệt trở nên quan trọng ngay khi đầu ra được chuyển hướng: chuyển stdout của script vào một tệp, 42 sẽ nằm trong tệp còn ghi chú trạng thái vẫn ở lại terminal. Đó là quy tắc chung: kết quả ra stdout bằng cat(), ghi chú ra stderr bằng message(). Đó cũng là lý do message() (chứ không phải cat()) là cách lịch sự để hàm và package trò chuyện với người dùng: người gọi có thể tắt nó bằng suppressMessages() mà không mất đầu ra thật.

warning() mạnh hơn, mang nghĩa "có gì đó không ổn, nhưng tôi vẫn chạy tiếp", và khi script chạy bằng Rscript, các cảnh báo được gom lại và hiện cùng lúc ở cuối thay vì ngay tại chỗ (xem gỡ lỗi để biết cách đọc chúng).

Đọc dữ liệu người dùng nhập: readline() và readLines()

Để hỏi người dùng một câu trong phiên tương tác (console R hoặc RStudio), dùng readline(). Các ví dụ này không chạy được trong trình soạn thảo nhúng, vì trình chạy trên trang không có ô nhập liệu, nên hãy thử chúng trên console ở máy bạn:

# Interactive sessions only:
name <- readline("What is your name? ")
cat("Hello,", name, "\n")

age <- as.numeric(readline("Your age? "))
cat("Next year you'll be", age + 1, "\n")

Có hai điều cần nhớ. readline() luôn trả về chuỗi: chuyển bằng as.numeric() trước khi tính toán, và kiểm tra kết quả có phải NA không phòng khi người dùng gõ thứ không phải số. Và trong một script không tương tác, readline() hoàn toàn không chờ đầu vào; nó trả về "" ngay lập tức.

Với script chạy bằng Rscript cần đọc đầu vào được pipe vào hoặc gõ vào, hãy đọc từ đầu vào chuẩn:

# In a script run as: echo "Ada" | Rscript greet.R
line <- readLines(con = "stdin", n = 1)
cat("Hello,", line, "\n")

readLines(con = "stdin") không có n sẽ đọc mọi dòng cho đến khi đầu vào kết thúc, dạng chuẩn cho các script kiểu bộ lọc. Đọc tệp dữ liệu là một việc khác với những công cụ tốt hơn; hãy bắt đầu với read-csv khi đầu vào của bạn là một bộ dữ liệu chứ không phải bàn phím.

Những điều cần nhớ

  • Tự động in chỉ xảy ra ở cấp cao nhất: bên trong vòng lặp và hàm, hãy tự gọi print() hoặc cat().
  • print() để xem xét: chuỗi có ngoặc kép, tiền tố chỉ số [1], chuỗi thoát hiện nguyên văn.
  • cat() để trình bày: văn bản thô, đối số nối bằng dấu cách, và bạn tự thêm "\n".
  • sprintf() + cat() là mẫu base R cho đầu ra có định dạng, thẳng hàng.
  • Kết quả ra stdout (cat), ghi chú ra stderr (message, warning).
  • readline() cho đầu vào tương tác (luôn là chuỗi!); readLines(con = "stdin") cho Rscript.

Tiếp theo: toán tử, gồm toán tử số học, so sánh và các toán tử logic điều khiển mọi điều kiện bạn sẽ viết.

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

print() và cat() trong R khác nhau thế nào?

print() hiển thị một giá trị theo cách R biểu diễn nó: chuỗi giữ nguyên dấu ngoặc kép và mỗi dòng có tiền tố chỉ số kiểu [1]. cat() ghi văn bản thô, không ngoặc kép, không chỉ số và không tự xuống dòng (bạn tự thêm "\n"). Dùng print() để xem xét đối tượng, cat() để tạo đầu ra cho người đọc.

Vì sao vòng lặp R của mình không in ra gì?

R chỉ tự động in với các biểu thức ở cấp cao nhất. Bên trong vòng lặp for hoặc thân hàm, một x trơn được tính rồi lặng lẽ bị bỏ đi. Hãy bọc nó một cách rõ ràng: print(x) hoặc cat(x, "\n").

Đọc dữ liệu người dùng nhập trong R như thế nào?

Trong một phiên tương tác, readline("prompt: ") đọc một dòng người dùng gõ vào dưới dạng chuỗi (chuyển bằng as.numeric() nếu bạn cần số). Trong script chạy bằng Rscript, readline() không chờ: hãy đọc từ đầu vào chuẩn bằng readLines(con = "stdin", n = 1).

In ra không có dấu ngoặc kép trong R như thế nào?

Dùng cat("hello\n"): nó không bao giờ thêm dấu ngoặc kép. Nếu bạn muốn đúng kiểu print() nhưng không có ngoặc kép, print("hello", quote = FALSE) hoặc noquote("hello") cũng được.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu