2013-10-29 21 views
38

Tôi tự hỏi liệu có ai có thể minh họa cho tôi cách R thực hiện cuộc gọi C từ lệnh R được nhập vào dấu nhắc của bảng điều khiển hay không. Tôi đặc biệt bối rối bởi các đối số chức năng a) của R và b) gọi hàm.Tìm hiểu cách thức. Các hàm C bên trong được xử lý trong R

Ví dụ: trong trường hợp này là set.seed(). Tự hỏi làm thế nào nó hoạt động Tôi gõ tên tại dấu nhắc, lấy nguồn (look here for more on that), xem có cuối cùng là .Internal(set.seed(seed, i.knd, normal.kind), do đó, dutifully tìm kiếm tên chức năng có liên quan trong phần .Internals của /src/names.c, tìm thấy nó được gọi là do_setseed và là trong RNG.c đó dẫn tôi đến ...

SEXP attribute_hidden do_setseed (SEXP call, SEXP op, SEXP args, SEXP env) 
{ 
    SEXP skind, nkind; 
    int seed; 

    checkArity(op, args); 
    if(!isNull(CAR(args))) { 
    seed = asInteger(CAR(args)); 
    if (seed == NA_INTEGER) 
     error(_("supplied seed is not a valid integer")); 
    } else seed = TimeToSeed(); 
    skind = CADR(args); 
    nkind = CADDR(args); 
    //... 
     //DO RNG here 
    //... 
    return R_NilValue; 
} 
  • CAR, CADR, CADDR? Nghiên cứu của tôi dẫn tôi tin rằng chúng là cấu trúc có liên quan đến ảnh hưởng đến Lisp liên quan đến danh sách nhưng ngoài ra tôi không hiểu những chức năng này làm gì hoặc lý do tại sao chúng cần thiết.
  • checkArity() làm gì?
  • SEXP args có vẻ tự giải thích, nhưng đây có phải là danh sách các đối số được chuyển trong lệnh gọi hàm không?
  • SEXP op đại diện cho điều gì? Tôi lấy điều này để có nghĩa là toán tử (giống như trong các hàm nhị phân như +), nhưng sau đó là SEXP call là gì?

Có ai có thể chảy qua những gì xảy ra khi tôi gõ

set.seed(1) 

tại R console nhanh chóng, lên đến điểm mà tại đó skindnkind được định nghĩa? Tôi thấy tôi không thể hiểu rõ mã nguồn ở cấp độ này và đường dẫn từ trình thông dịch đến hàm C.

+6

Bạn có thể bắt đầu tại http://adv-r.had.co.nz/C-interface.html - Tôi sẽ sớm viết thêm một số chi tiết cho ví dụ cụ thể này. – hadley

+0

@hadley cảm ơn, tôi sẽ trải qua tối nay. Tôi thực sự chờ đợi cuốn sách của bạn được phát hành! Chẳng bao lâu tôi hy vọng. :-) –

+0

@ SimonO101: Tôi muốn thêm "... trong R" vào tiêu đề câu hỏi –

Trả lời

23

CARCDR là cách bạn truy cập các đối tượng danh sách cặp, như được giải thích trong section 2.1.11 of R Language Definition. CAR chứa phần tử đầu tiên và CDR chứa các phần tử còn lại. Một ví dụ được đưa ra trong section 5.10.2 of Writing R Extensions:

#include <R.h> 
#include <Rinternals.h> 

SEXP convolveE(SEXP args) 
{ 
    int i, j, na, nb, nab; 
    double *xa, *xb, *xab; 
    SEXP a, b, ab; 

    a = PROTECT(coerceVector(CADR(args), REALSXP)); 
    b = PROTECT(coerceVector(CADDR(args), REALSXP)); 
    ... 
} 
/* The macros: */ 
first = CADR(args); 
second = CADDR(args); 
third = CADDDR(args); 
fourth = CAD4R(args); 
/* provide convenient ways to access the first four arguments. 
* More generally we can use the CDR and CAR macros as in: */ 
args = CDR(args); a = CAR(args); 
args = CDR(args); b = CAR(args); 

Ngoài ra còn có một macro TAG để truy cập tên đặt cho các đối số thực tế.

checkArity đảm bảo rằng số đối số được truyền cho hàm là chính xác. args là các đối số thực tế được truyền cho hàm. op là con trỏ bù trừ "được sử dụng cho các hàm C xử lý nhiều hơn một hàm R" (được trích dẫn từ src/main/names.c, cũng chứa bảng hiển thị độ lệch và độ chẵn cho từng hàm).

Ví dụ: do_colsum xử lý col/rowSumscol/rowMeans.

/* Table of .Internal(.) and .Primitive(.) R functions 
* =====  =========  ========== 
* Each entry is a line with 
* 
* printname c-entry  offset eval arity pp-kind precedence rightassoc 
* --------- -------  ------ ---- ----- ------- ---------- ---------- 
{"colSums", do_colsum, 0,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 
{"colMeans", do_colsum, 1,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 
{"rowSums", do_colsum, 2,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 
{"rowMeans", do_colsum, 3,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 

Lưu ý rằng arity trong bảng trên là 4 vì (mặc dù rowSums et al chỉ có 3 đối số) do_colsum có 4, mà bạn có thể nhìn thấy từ .Internal cuộc gọi trong rowSums:

> rowSums 
function (x, na.rm = FALSE, dims = 1L) 
{ 
    if (is.data.frame(x)) 
     x <- as.matrix(x) 
    if (!is.array(x) || length(dn <- dim(x)) < 2L) 
     stop("'x' must be an array of at least two dimensions") 
    if (dims < 1L || dims > length(dn) - 1L) 
     stop("invalid 'dims'") 
    p <- prod(dn[-(1L:dims)]) 
    dn <- dn[1L:dims] 
    z <- if (is.complex(x)) 
     .Internal(rowSums(Re(x), prod(dn), p, na.rm)) + (0+1i) * 
      .Internal(rowSums(Im(x), prod(dn), p, na.rm)) 
    else .Internal(rowSums(x, prod(dn), p, na.rm)) 
    if (length(dn) > 1L) { 
     dim(z) <- dn 
     dimnames(z) <- dimnames(x)[1L:dims] 
    } 
    else names(z) <- dimnames(x)[[1L]] 
    z 
} 
+0

+1 Cảm ơn! Điều đó thật tuyệt. Vì vậy, 'checkArity' có hiệu quả đếm số lượng đối số trong hàm gọi, tra cứu hàm và đếm số lượng đối số mà hàm đang mong đợi và nếu chúng không phù hợp, nó sẽ kiểm soát lỗi để thoát hàm một cách duyên dáng thay vì gọi hàm với số lượng các đối số sai và có thể là sự phân tách? –

+2

@ SimonO101: có, nó tìm kiếm hàm từ bảng trong 'names.c' để xem có bao nhiêu args được yêu cầu hoặc cho phép. Bạn có thể thấy lỗi bằng cách gọi một cái gì đó như: 'x <- 1; .Internal (rowSums (x)) '. –

18

Các hàm trích xuất danh sách cặp C cơ bản là CARCDR. (Danh sách cặp rất giống với danh sách nhưng được triển khai dưới dạng danh sách liên kết và được sử dụng nội bộ cho danh sách đối số). Chúng có tương đương R đơn giản: x[[1]]x[-1]. R cũng cung cấp rất nhiều sự kết hợp của hai:

  • CAAR(x) = CAR(CAR(x)) tương đương với x[[1]][[1]]
  • CADR(x) = CAR(CDR(x)) tương đương với x[-1][[1]], tức là x[[2]]
  • CADDR(x) = CAR(CDR(CDR(x)) tương đương với x[-1][-1][[1]], tức là x[[3]]
  • và do đó trên

Truy cập phần tử thứ n của cặp đôi là một hoạt động O(n), không giống như truy cập phần tử thứ n của danh sách là O(1). Đây là lý do tại sao không có chức năng đẹp hơn để truy cập phần tử thứ n của một danh sách cặp.

Chức năng nội bộ/nguyên thủy không khớp với tên, chúng chỉ sử dụng đối sánh vị trí, đó là lý do tại sao chúng có thể sử dụng hệ thống đơn giản này để trích xuất đối số.

Tiếp theo, bạn cần phải hiểu đối số của hàm C là gì. Tôi không chắc chắn nơi đây là những tài liệu, vì vậy tôi có thể không hoàn toàn đúng về cấu trúc, nhưng tôi nên các mảnh chung:

  • call: cuộc gọi hoàn chỉnh, như thể được chụp bởi match.call()

  • op: chỉ mục hàm .Internal được gọi từ R. Điều này là cần thiết vì có ánh xạ nhiều đối tượng từ các hàm .Internal đến C. (ví dụ: do_summary thực hiện tổng hợp, trung bình, min, max và prod). Con số này là mục thứ ba trong names.c - nó luôn luôn 0 cho do_setseed và do đó không bao giờ sử dụng

  • args: một danh sách cặp của các đối số cung cấp cho các chức năng.

  • env: môi trường mà từ đó hàm được gọi.

checkArity là một macro trong đó kêu gọi Rf_checkArityCall, mà về cơ bản nhìn lên số lượng đối số (cột thứ năm trong names.c là arity) và chắc chắn rằng số lượng cung cấp phù hợp. Bạn phải làm theo một vài macro và hàm trong C để xem điều gì đang xảy ra - nó rất hữu ích để có một bản sao R-source cục bộ mà bạn có thể grep thông qua.

+0

+1 Cảm ơn! Vì vậy, việc sử dụng câu nói 'CADDR (x) 'có hiệu quả giữ thời gian truy cập vào' O (1) '(tôi đã nghĩ rằng hình phạt thời gian cho một hoạt động như thế này sẽ là tối thiểu, vì tôi không thể nghĩ ra một chức năng mà danh sách đối số sẽ nhận được * dài * Điều đó làm cho * nhiều * ý nghĩa hơn với tôi bây giờ, đọc lại ví dụ của tôi ở trên (kết hợp với chương sách của bạn mà tôi hiện đang đọc) –

+0

@ SimonO101 Không, 'CADDR (x) 'sẽ chậm hơn' CDR (x) ', nhưng bạn có thể sử dụng' CAR' và 'CDR' với nhau như trong ví dụ của Joshua để tránh chi phí. – hadley

Các vấn đề liên quan