Chính xác thì R phân tích cú pháp `->`, toán tử gán phải như thế nào?


76

Vì vậy, đây là một loại câu hỏi tầm thường, nhưng tôi không thể trả lời được khiến tôi khó chịu, và có lẽ câu trả lời sẽ giúp tôi biết thêm chi tiết về cách hoạt động của R.

Tiêu đề nói lên tất cả: R phân tích cú pháp như thế nào ->, hàm gán bên phải tối nghĩa?

Các thủ thuật thông thường của tôi để tìm hiểu điều này đã thất bại:

`->`

Lỗi: ->không tìm thấy đối tượng

getAnywhere("->")

không có đối tượng có tên ->được tìm thấy

Và chúng tôi không thể gọi nó trực tiếp:

`->`(3,x)

Lỗi: không thể tìm thấy chức năng "->"

Nhưng tất nhiên, nó hoạt động:

(3 -> x) #assigns the value 3 to the name x
# [1] 3

Có vẻ như R biết cách đơn giản đảo ngược các lập luận, nhưng tôi nghĩ các cách tiếp cận ở trên chắc chắn sẽ giải quyết được trường hợp:

pryr::ast(3 -> y)
# \- ()
#   \- `<- #R interpreter clearly flipped things around
#   \- `y  #  (by the time it gets to `ast`, at least...)
#   \-  3  #  (note: this is because `substitute(3 -> y)` 
#          #   already returns the reversed version)

So sánh điều này với toán tử gán thông thường:

`<-`
.Primitive("<-")

`<-`(x, 3) #assigns the value 3 to the name x, as expected

?"->", ?assignOpsĐịnh nghĩa ngôn ngữ R đều chỉ đề cập đến nó khi chuyển nó như một toán tử gán đúng.

Nhưng rõ ràng có một cái gì đó độc đáo về cách ->được sử dụng. Nó không phải là một hàm / toán tử (như các cuộc gọi đến getAnywherevà trực tiếp `->`dường như để chứng minh), vậy nó là gì? Nó hoàn toàn nằm trong một lớp học của riêng nó?

Có điều gì để học từ điều này ngoài " ->ngôn ngữ R hoàn toàn độc đáo trong cách nó được diễn giải và xử lý; ghi nhớ và tiếp tục"?


2
Trên thực tế, câu hỏi liên quan được liên kết này có liên quan hơn nhiều: stackoverflow.com/questions/23309687/…
MichaelChirico

1
Bạn chỉ cần đặt cho nhãn một giá trị. Điều này không có nghĩa rằng họ là giống nhau
Ole Petersen

Câu trả lời:


71

Hãy để tôi mở đầu điều này bằng cách nói rằng tôi hoàn toàn không biết gì về cách hoạt động của trình phân tích cú pháp. Phải nói rằng, dòng 296 của gram.y xác định các mã thông báo sau để đại diện cho phép gán trong trình phân tích cú pháp (YACC?) R sử dụng:

%token      LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB

Sau đó, trên các dòng 5140 đến 5150 của gam.c , mã này trông giống như mã C tương ứng:

Cuối cùng, bắt đầu từ dòng 5044 của gram.c , định nghĩa của install_and_save2:


Vì vậy, một lần nữa, không có kinh nghiệm làm việc với trình phân tích cú pháp, có vẻ như ->->>được dịch trực tiếp sang <-<<-tương ứng, ở mức rất thấp trong quá trình thông dịch.


Bạn đã đưa ra một điểm rất tốt khi hỏi làm thế nào trình phân tích cú pháp "biết" để đảo ngược các đối số thành ->- coi điều đó ->dường như được cài đặt vào bảng biểu tượng R là <-- và do đó có thể diễn giải chính xác x -> yy <- xkhông x <- y . Điều tốt nhất tôi có thể làm là cung cấp thêm suy đoán khi tôi tiếp tục tìm thấy "bằng chứng" để hỗ trợ tuyên bố của mình. Hy vọng rằng một số chuyên gia YACC nhân từ sẽ vấp phải câu hỏi này và cung cấp một chút hiểu biết; Tuy nhiên, tôi sẽ không nín thở về điều đó.

Quay lại dòng 383 và 384 của gram.y , điều này trông giống như một số logic phân tích cú pháp khác liên quan đến các ký hiệu LEFT_ASSIGNRIGHT_ASSIGNký hiệu đã nói ở trên :

|   expr LEFT_ASSIGN expr       { $$ = xxbinary($2,$1,$3);  setId( $$, @$); }
|   expr RIGHT_ASSIGN expr      { $$ = xxbinary($2,$3,$1);  setId( $$, @$); }

Mặc dù tôi không thể thực sự hiểu được cú pháp điên rồ này, nhưng tôi đã nhận thấy rằng các đối số thứ hai và thứ ba xxbinaryđược hoán đổi thành WRT LEFT_ASSIGN( xxbinary($2,$1,$3)) và RIGHT_ASSIGN( xxbinary($2,$3,$1)).

Đây là những gì tôi đang hình dung trong đầu:

LEFT_ASSIGN Tình huống: y <- x

  • $2 là "đối số" thứ hai cho trình phân tích cú pháp trong biểu thức trên, tức là <-
  • $1là người đầu tiên; cụ thể lày
  • $3 là thứ ba; x

Do đó, kết quả gọi (C?) Sẽ là xxbinary(<-, y, x).

Áp dụng logic này RIGHT_ASSIGN, tức là x -> y, kết hợp với phỏng đoán trước đó của tôi về <-->bị hoán đổi,

  • $2được dịch từ ->sang<-
  • $1x
  • $3y

Nhưng vì kết quả là xxbinary($2,$3,$1)thay vì xxbinary($2,$1,$3), kết quả vẫnxxbinary(<-, y, x).


Dựa trên điều này xa hơn một chút, chúng tôi có định nghĩa xxbinarytrên dòng 3310 của gram.c :

Đáng tiếc là tôi không thể tìm thấy một định nghĩa đúng lang3(hoặc biến thể của nó lang1, lang2, vv ...) trong mã nguồn R, nhưng tôi giả định rằng nó được sử dụng để đánh giá chức năng đặc biệt (ví dụ ký tự) trong một cách mà được đồng bộ với thông dịch viên.


Nội dung cập nhật Tôi sẽ cố gắng giải quyết một số câu hỏi bổ sung của bạn trong phần nhận xét tốt nhất có thể khi cung cấp kiến ​​thức (rất) hạn chế của tôi về quy trình phân tích cú pháp.

1) Đây có thực sự là đối tượng duy nhất trong R hoạt động như thế này không ?? (Tôi đã ghi nhớ câu trích dẫn của John Chambers qua cuốn sách của Hadley: "Mọi thứ tồn tại đều là một đối tượng. Mọi thứ xảy ra đều là một lệnh gọi hàm." Điều này rõ ràng nằm ngoài miền đó - còn điều gì khác như thế này không?

Đầu tiên, tôi đồng ý rằng điều này nằm ngoài miền đó. Tôi tin rằng trích dẫn của Chambers liên quan đến Môi trường R, tức là các quá trình đều diễn ra sau giai đoạn phân tích cú pháp mức thấp này. Tuy nhiên, tôi sẽ đề cập đến vấn đề này nhiều hơn một chút bên dưới. Dù sao, ví dụ khác duy nhất về loại hành vi này mà tôi có thể tìm thấy là **toán tử, là một từ đồng nghĩa với toán tử lũy thừa phổ biến hơn ^. Đối với phép gán đúng, **dường như không được trình thông dịch "công nhận" là một lệnh gọi hàm, v.v.:

R> `->`
#Error: object '->' not found
R> `**`
#Error: object '**' not found 

Tôi tìm thấy điều này bởi vì đó là trường hợp duy nhất khác install_and_save2 được sử dụng bởi trình phân tích cú pháp C :


2) Chính xác thì điều này xảy ra khi nào? Tôi ghi nhớ rằng phép thay thế (3 -> y) đã lật ngược biểu thức; Tôi không thể tìm ra từ nguồn thay thế nào có thể đã ping đến YACC ...

Tất nhiên tôi vẫn đang suy đoán ở đây, nhưng vâng, tôi nghĩ chúng ta có thể giả định một cách an toàn rằng khi bạn gọi substitute(3 -> y), từ quan điểm của hàm thay thế , biểu thức luôn là y <- 3 ; ví dụ như hàm hoàn toàn không biết rằng bạn đã gõ 3 -> y. do_substitute, giống như 99% các hàm C được R sử dụng, chỉ xử lý các SEXPđối số - tôi tin là an EXPRSXPtrong trường hợp 3 -> y(== y <- 3). Đây là những gì tôi đã ám chỉ ở trên khi tôi phân biệt giữa Môi trường R và quá trình phân tích cú pháp. Tôi không nghĩ rằng có bất kỳ điều gì đặc biệt kích hoạt trình phân tích cú pháp bắt đầu hoạt động - mà đúng hơn là mọi thứ bạn nhập vào trình thông dịch sẽ được phân tích cú pháp. Tôi đã làm một chút đọc thêm về trình phân tích cú pháp YACC / Bison tạo vào đêm qua và theo tôi hiểu (hay còn gọi là đừng đặt cược vào trang trại này), Bison sử dụng ngữ pháp mà bạn xác định (trong .y(các) tệp) để tạo trình phân tích cú pháp trong C - tức là một hàm C thực hiện phân tích cú pháp thực tế của đầu vào. Đổi lại, mọi thứ bạn nhập vào trong một phiên R lần đầu tiên được xử lý bởi hàm phân tích cú pháp C này, sau đó sẽ ủy quyền cho hành động thích hợp được thực hiện trong Môi trường R (Nhân tiện, tôi đang sử dụng thuật ngữ này rất lỏng lẻo). Trong giai đoạn này, lhs -> rhssẽ được dịch sang rhs <- lhs, **sang ^, v.v. Ví dụ, đây là đoạn trích từ một trong các bảng của các hàm nguyên thủy trong tên.c :

Bạn sẽ nhận thấy rằng ->, ->>**không được định nghĩa ở đây. Theo như tôi biết, các biểu thức nguyên thủy R như <-[, v.v. ... là tương tác gần nhất mà Môi trường R từng có với bất kỳ mã C cơ bản nào. Điều tôi gợi ý là ở giai đoạn này trong quá trình (từ việc bạn nhập một bộ ký tự vào trình thông dịch và nhấn 'Enter', thông qua đánh giá thực tế của một biểu thức R hợp lệ), trình phân tích cú pháp đã hoạt động kỳ diệu của nó, đó là lý do bạn không thể có được định nghĩa hàm cho ->hoặc **bằng cách bao quanh chúng bằng các dấu gạch ngược, như bạn thường làm.


17
trong khi chờ đợi tôi có dám nói câu trả lời này đáng giá gram.ykhông? ok tôi nên nghiêm túc trở lại làm việc ...
MichaelChirico

2
Chỉ đối với bản ghi (và cũng như một người mới phân tích cú pháp hoàn chỉnh), tôi sẽ lưu ý rằng có vẻ như có sự phân biệt giữa loại mã thông báo (tại đây RIGHT_ASSIGN) và giá trị của nó (tại đây <-, được gán cho yylvalbởi install_and_save2). Đối với tôi, dường như kiểu được sử dụng để định hướng việc phân tích cú pháp của biểu thức (gửi cho chúng ta nhánh đọc { $$ = xxbinary($2,$3,$1); setId( $$, @$); }), trong khi giá trị của nó là thứ được truyền qua xxbinaryđối số đầu tiên của (tức là $2).
Josh O'Brien

@Josh O'Brien Cảm ơn bạn đã đóng góp ý kiến ​​(và cả các chỉnh sửa); bề ngoài nghe có vẻ hợp lý với tôi. Nếu bạn muốn nó vào một lúc nào đó, vui lòng thêm thông tin đó hoặc bất kỳ thông tin liên quan nào khác vào câu trả lời của tôi (Tôi e rằng tôi sẽ bán phần giải thích nếu tôi cố gắng diễn đạt câu đó).
nrussell

3
@nrussell Bạn được chào đón. lang3et al. là các hàm nội tuyến và được tìm thấy ở đây, trong$RHOME/src/include/Rinlinedfuns.h . Đối với tôi, có vẻ như vai trò của họ ở đây là kết hợp các mã thông báo riêng lẻ và các biểu thức được phân tích cú pháp thành các đối tượng ngôn ngữ giống như danh sách, xây dựng hướng tới một phiên bản được phân tích cú pháp hoàn toàn của biểu thức đã nhập.
Josh O'Brien

1
Cảm ơn các cập nhật! như **, tôi làm nhớ ít nhất đọc tại một số điểm ở đâu đó rằng nhà điều hành đó là loại vestigal, vì vậy ít nhất tôi đã nhìn thấy nó công nhận là loại một kẻ bị ruồng bỏ trước đó. Dù sao đi nữa, tiện ích của tôi được xây dựng bây giờ chứa đầy kiến ​​thức hữu ích đáng ngờ ... chỉ là cách tôi thích nó!
MichaelChirico
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.