Sự khác biệt giữa các nhà khai thác chuyển nhượng của R = = và và <- <trong R là gì?


712

Sự khác biệt giữa các toán tử gán =<-trong R là gì?

Tôi biết rằng các toán tử hơi khác nhau, như ví dụ này cho thấy

x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"

Nhưng đây có phải là sự khác biệt duy nhất?


45
Như đã lưu ý ở đây , nguồn gốc của <-biểu tượng đến từ bàn phím APL cũ thực sự có một <-phím duy nhất trên chúng.
2014 lúc 17:30

Câu trả lời:


97

Sự khác biệt giữa các toán tử gán =<-trong R là gì?

Như ví dụ của bạn cho thấy, =<-có quyền ưu tiên toán tử hơi khác nhau (xác định thứ tự đánh giá khi chúng được trộn trong cùng một biểu thức). Trong thực tế, ?Syntaxtrong R đưa ra bảng ưu tiên toán tử sau, từ cao nhất đến thấp nhất:

…
‘-> ->>’           rightwards assignment
‘<- <<-’           assignment (right to left)=’                assignment (right to left)

Nhưng đây có phải là sự khác biệt duy nhất?

Vì bạn đã hỏi về các toán tử gán : có, đó là sự khác biệt duy nhất. Tuy nhiên, bạn sẽ được tha thứ vì tin vào điều khác. Ngay cả tài liệu R về ?assignOpstuyên bố rằng có nhiều khác biệt:

Toán tử <-có thể được sử dụng ở bất cứ đâu, trong khi toán tử =chỉ được phép ở cấp cao nhất (ví dụ: trong biểu thức hoàn chỉnh được gõ tại dấu nhắc lệnh) hoặc là một trong các biểu thức con trong danh sách các biểu thức được giằng.

Chúng ta đừng đặt quá nhiều điểm vào đó: tài liệu R là (tinh tế) sai [ 1 ] . Điều này rất dễ để hiển thị: chúng ta chỉ cần tìm một ví dụ ngược lại của =toán tử không (a) ở cấp cao nhất, cũng không (b) một biểu thức con trong danh sách các biểu thức (ví dụ {…; …}). - Không cần quảng cáo thêm:

x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1

Rõ ràng chúng tôi đã thực hiện một nhiệm vụ, sử dụng =, bên ngoài bối cảnh (a) và (b). Vậy, tại sao tài liệu về tính năng ngôn ngữ R cốt lõi bị sai trong nhiều thập kỷ?

Đó là bởi vì trong cú pháp của R, biểu tượng =có hai ý nghĩa riêng biệt thường xuyên bị xáo trộn:

  1. Ý nghĩa đầu tiên là như một toán tử gán . Đây là tất cả những gì chúng ta đã nói về cho đến nay.
  2. Ý nghĩa thứ hai không phải là một toán tử mà là một mã thông báo cú pháp báo hiệu các đối số được đặt tên trong một lệnh gọi hàm. Không giống như =toán tử, nó thực hiện không có hành động nào trong thời gian chạy, nó chỉ thay đổi cách biểu thức được phân tích cú pháp.

Hãy xem nào.

Trong bất kỳ đoạn mã nào có dạng chung

‹function_name›(‹argname› = ‹value›,)
‹function_name›(‹args›, ‹argname› = ‹value›,)

... những =là dấu hiệu rằng định nghĩa tên tham số đi qua: đó là không toán tử gán. Hơn nữa, =hoàn toàn bị cấm trong một số bối cảnh cú pháp:

if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …

Bất kỳ điều nào trong số này cũng sẽ gây ra lỗi không mong đợi '=' trong trò chơi blas Đá.

Trong bất kỳ bối cảnh nào khác, =đề cập đến cuộc gọi toán tử gán. Cụ thể, chỉ cần đặt dấu ngoặc đơn xung quanh biểu thức con sẽ làm cho bất kỳ điều nào ở trên (a) hợp lệ và (b) một bài tập . Ví dụ, các nhiệm vụ sau đây thực hiện nhiệm vụ:

median((x = 1 : 10))

Nhưng cũng:

if (! (nf = length(from))) return()

Bây giờ bạn có thể phản đối rằng mã đó là tàn bạo (và bạn có thể đúng). Nhưng tôi đã lấy mã này từ base::file.copyhàm (thay thế <-bằng =) - đó là một mẫu phổ biến trong phần lớn cơ sở mã R lõi.

Giải thích ban đầu của John Chambers , mà tài liệu R có thể dựa trên, thực sự giải thích điều này một cách chính xác:

[ =gán được] chỉ được phép ở hai vị trí trong ngữ pháp: ở cấp cao nhất (dưới dạng một chương trình hoàn chỉnh hoặc biểu thức do người dùng nhập); và khi bị cô lập khỏi cấu trúc logic xung quanh, bằng dấu ngoặc hoặc thêm một cặp dấu ngoặc đơn.


Một lời thú nhận: Tôi đã nói dối sớm hơn. Có một sự khác biệt thêm giữa =<-các nhà khai thác: họ gọi là chức năng riêng biệt. Theo mặc định, các hàm này thực hiện tương tự nhưng bạn có thể ghi đè một trong hai hàm riêng biệt để thay đổi hành vi. Ngược lại, <-->(gán từ trái sang phải), mặc dù khác biệt về mặt cú pháp, luôn gọi cùng một hàm. Ghi đè cái này cũng ghi đè cái kia. Biết điều này hiếm khi thực tế nhưng nó có thể được sử dụng cho một số shenanigans vui vẻ .


1
Về quyền ưu tiên và lỗi trong tài liệu của R, quyền ưu tiên ?thực sự đúng ở giữa =<-, điều này có hậu quả quan trọng khi ghi đè ? và hầu như không có gì khác.
Moody_Mudskipper

@Moody_Mudskipper thật kỳ quái! Bạn có vẻ đúng, nhưng theo mã nguồn ( main/gram.y), quyền ưu tiên ?được ghi lại chính xác và thấp hơn cả hai =<-.
Konrad Rudolph

Tôi không nói tiếng C nhưng tôi cho rằng =sẽ được đối xử đặc biệt trước khi cây phân tích được xây dựng. Có thể liên quan đến các đối số hàm, điều đó có nghĩa là trong foo(x = a ? b)chúng ta sẽ tìm kiếm =trước khi phân tích phần còn lại của biểu thức.
Moody_Mudskipper


2
@Moody_Mudskipper FWIW điều này cuối cùng đã được sửa trong 4.0.0.
Konrad Rudolph

661

Sự khác biệt trong toán tử gán sẽ rõ ràng hơn khi bạn sử dụng chúng để đặt giá trị đối số trong lệnh gọi hàm. Ví dụ:

median(x = 1:10)
x   
## Error: object 'x' not found

Trong trường hợp này, xđược khai báo trong phạm vi của hàm, vì vậy nó không tồn tại trong không gian làm việc của người dùng.

median(x <- 1:10)
x    
## [1]  1  2  3  4  5  6  7  8  9 10

Trong trường hợp này, xđược khai báo trong không gian làm việc của người dùng, vì vậy bạn có thể sử dụng nó sau khi hoàn thành cuộc gọi chức năng.


Có một ưu tiên chung trong cộng đồng R khi sử dụng <-để gán (trừ chữ ký hàm) để tương thích với (rất) các phiên bản cũ của S-Plus. Lưu ý rằng các không gian giúp làm rõ các tình huống như

x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3

Hầu hết các IDE R đều có phím tắt để <-dễ gõ hơn. Ctrl+ =trong Architect, Alt+ -trong RStudio ( Option+ -dưới macOS), Shift+ -(gạch dưới) trong emacs + ESS.


Nếu bạn thích viết =để <-nhưng muốn sử dụng các biểu tượng phân phổ biến hơn đối với mã công khai phát hành (ở Cran, ví dụ), sau đó bạn có thể sử dụng một trong những tidy_*chức năng trong formatRgói để tự động thay thế =với <-.

library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5

Câu trả lời cho câu hỏi "Tại sao lại x <- y = 5ném lỗi mà không phải x <- y <- 5?" là "Điều đó thuộc về phép thuật có trong trình phân tích cú pháp". Cú pháp của R chứa nhiều trường hợp mơ hồ phải được giải quyết bằng cách này hay cách khác. Các chọn nhóm phân tích cú pháp để giải quyết các bit của biểu thức thứ tự khác nhau tuỳ thuộc vào việc =hay <-được sử dụng.

Để hiểu những gì đang xảy ra, bạn cần biết rằng chuyển nhượng âm thầm trả về giá trị được gán. Bạn có thể thấy rõ hơn bằng cách in rõ ràng, ví dụ print(x <- 2 + 3).

Thứ hai, sẽ rõ ràng hơn nếu chúng ta sử dụng ký hiệu tiền tố để gán. Vì thế

x <- 5
`<-`(x, 5)  #same thing

y = 5
`=`(y, 5)   #also the same thing

Trình phân tích cú pháp diễn giải x <- y <- 5như

`<-`(x, `<-`(y, 5))

Chúng tôi có thể mong đợi rằng x <- y = 5sau đó sẽ là

`<-`(x, `=`(y, 5))

nhưng thực ra nó được hiểu là

`=`(`<-`(x, y), 5)

Điều này là do =mức độ ưu tiên thấp hơn <-, như được hiển thị trên ?Syntaxtrang trợ giúp.


4
Điều này cũng được đề cập trong chương 8.2.26 của The R Inferno của Patrick Burns (Không phải tôi nhưng dù sao cũng là một đề xuất)
Uwe

3
Tuy nhiên, median((x = 1:10))có tác dụng tương tự như median(x <- 1:10).
Francesco Napolitano

2
Tôi không thực sự coi chúng là các phím tắt, trong mọi trường hợp bạn nhấn cùng một số phím
yosemite_k

5
Tôi mới nhận ra rằng lời giải thích của bạn về cách x <- x = 5hiểu được hơi sai: Trong thực tế, R diễn giải nó là ​`<-<-`(x, y = 5, value = 5)(mà bản thân nó ít nhiều tương đương với tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)). Rất tiếc!
Konrad Rudolph

4
Tôi và tôi chỉ nhận ra rằng phần đầu tiên của câu trả lời này là không chính xác và thật không may, khá sai lệch vì nó tồn tại một quan niệm sai lầm phổ biến: Cách bạn sử dụng =trong một cuộc gọi chức năng không thực hiện chuyển nhượng và không phải là toán tử gán. Đó là một biểu thức R được phân tích cú pháp hoàn toàn riêng biệt, tình cờ sử dụng cùng một ký tự. Hơn nữa, mã bạn hiển thị không khai báo xra phạm vi của phạm vi chức năng. Các khai báo hàm Thực hiện nói lời tuyên bố. Hàm gọi không (nó phức tạp hơn một chút với các ...đối số được đặt tên ).
Konrad Rudolph

103

Hướng dẫn kiểu R của Google đơn giản hóa vấn đề bằng cách cấm "=" cho phép gán. Một lựa chọn không tồi.

https://google.github.io/styleguide/Rguide.xml

Hướng dẫn R đi vào chi tiết đẹp trên tất cả 5 toán tử gán.

http://stat.ethz.ch/R-manual/R-patched/l Library / base / html / assOps.html


133
Mặt trái của sự phân công tình cờ x<-ykhi x < -ycó ý nghĩa, làm tôi bực mình rất nhiều mà cá nhân tôi thích =. Có mã của bạn phụ thuộc vào khoảng trắng hiện diện có vẻ không tốt cho tôi. Bạn có thể đề xuất khoảng cách là lời khuyên về phong cách nhưng để mã của bạn chạy khác đi liệu có khoảng trống ở đó hay không? Điều gì sẽ xảy ra nếu bạn định dạng lại mã của mình hoặc sử dụng tìm kiếm và thay thế, khoảng trắng đôi khi có thể biến mất và mã bị sai lệch. Đó không phải là một vấn đề với =. IIUC, cấm =tương đương với yêu cầu " <- "; tức là 3 ký tự bao gồm một khoảng trắng, không chỉ " <-".
Matt Dowle

12
Lưu ý rằng bất kỳ giá trị 0 nào đều được TRUER. xem xét . Vì vậy, nếu bạn có ý định kiểm tra nếu xnhỏ hơn -y, bạn có thể viết if (x<-y)sẽ không cảnh báo hoặc lỗi và có vẻ hoạt động tốt. Nó sẽ chỉ là FALSEkhi y=0, mặc dù.
Matt Dowle

4
Nếu bạn cấm =và sử dụng <- thì thật khó để tranh luận rằng grep "[^<]<-[^ ]" *.Rkhông cần thêm một bước nữa . =không cần một ví dụ grep.
Matt Dowle

34
Tại sao làm tổn thương mắt và ngón tay của <-bạn nếu bạn có thể sử dụng =? Trong 99,99% số lần =là tốt. Đôi khi bạn cần <<-mặc dù, đó là một lịch sử khác nhau.
Fernando

10
Việc tập trung vào <- có lẽ là một trong những lý do khập khiễng vì thiếu + = và - =.
Chris

37

x = y = 5tương đương với x = (y = 5), bởi vì các toán tử gán "nhóm" từ phải sang trái, hoạt động. Ý nghĩa: gán 5 cho y, để lại số 5; và sau đó gán 5 cho x.

Điều này không giống như (x = y) = 5, không hoạt động! Ý nghĩa: gán giá trị của yđể x, để lại giá trị của y; và sau đó gán 5 cho, umm ..., chính xác là gì?

Khi bạn trộn các loại toán tử gán khác nhau, <-liên kết chặt chẽ hơn =. Vì vậy, x = y <- 5được giải thích là x = (y <- 5), đó là trường hợp có ý nghĩa.

Thật không may, x <- y = 5được hiểu là (x <- y) = 5, đó là trường hợp không hoạt động!

Xem ?Syntax?assignOpscho các quy tắc ưu tiên (ràng buộc) và nhóm.


Vâng, như câu trả lời của Konrad Rudolph đã nói <- <<-ở trên = trong bảng ưu tiên, có nghĩa là <-sẽ được đưa ra trước. Vì vậy, x <- y = 5nên được thực hiện như (x <- y) = 5.
Nick Dong

1
@Nick Đồng Có thật. Thật hữu ích, bảng ưu tiên toán tử được ghi lại rõ ràng trong ? Cú pháp {cơ sở} .
Steve Pitchers

33

Theo John Chambers, toán tử =chỉ được phép ở "cấp cao nhất", có nghĩa là nó không được phép trong các cấu trúc điều khiển như if, làm cho lỗi lập trình sau trở thành bất hợp pháp.

> if(x = 0) 1 else x
Error: syntax error

Như ông viết, "Không cho phép biểu mẫu gán mới [=] trong các biểu thức điều khiển sẽ tránh các lỗi lập trình (như ví dụ ở trên) có nhiều khả năng với toán tử bằng hơn so với các phép gán S khác."

Bạn có thể quản lý để làm điều này nếu nó "tách biệt với cấu trúc logic xung quanh, bằng dấu ngoặc hoặc thêm một cặp dấu ngoặc đơn", như vậy if ((x = 0)) 1 else xsẽ hoạt động.

Xem http://developer.r-project.org/equalAssign.html


11
Đó là một lỗi phổ biến, x==0hầu như luôn luôn có nghĩa là thay thế.
Aaron rời Stack Overflow

14
À, vâng, tôi đã bỏ qua rằng bạn nói "lỗi lập trình". Đó thực sự là tin tốt rằng điều này gây ra lỗi. Và một lý do tốt để thích x=0làm bài tập hơn x<-0!
Steve Pitchers

7
Vâng, thật tuyệt khi điều này gây ra lỗi, mặc dù tôi rút ra một bài học khác về những gì thích hơn; Tôi chọn sử dụng =ít nhất có thể bởi vì ===trông rất giống nhau.
Aaron rời Stack Overflow

2
Cách trình bày ví dụ này rất lạ đối với tôi. if(x = 0) 1 else xném một lỗi, giúp tôi tìm và sửa lỗi. if(x <- 1) 1 else xkhông ném lỗi và rất khó hiểu.
Gregor Thomas

3
Ý tôi là, một trình kiểm tra lỗi thực sự hữu ích sẽ đưa ra một lỗi ở đó và nói rằng "bạn có mã vô dụng sẽ luôn trả về elsegiá trị, ý bạn là viết nó theo cách đó?", Nhưng, đó có thể là một giấc mơ xa vời ...
TylerH

26

Các toán tử <-=gán vào môi trường mà chúng được đánh giá. Toán tử <-có thể được sử dụng ở bất cứ đâu, trong khi toán tử =chỉ được phép ở cấp cao nhất (ví dụ: trong biểu thức hoàn chỉnh được gõ tại dấu nhắc lệnh) hoặc là một trong các biểu thức con trong danh sách các biểu thức được giằng.


8
Tôi nghĩ "cấp cao nhất" có nghĩa là ở cấp độ câu lệnh, thay vì mức biểu thức. Vì vậy, x <- 42trên chính nó là một tuyên bố; trong if (x <- 42) {}đó sẽ là một biểu thức và không hợp lệ. Để rõ ràng, điều này không liên quan gì đến việc bạn có ở trong môi trường toàn cầu hay không.
Steve Pitchers

1
Điều này: Các nhà điều hành = chỉ được phép ở cấp cao nhất là một sự hiểu lầm được tổ chức rộng rãi và hoàn toàn sai.
Konrad Rudolph

Điều này không đúng - ví dụ: điều này hoạt động, mặc dù sự phân công không phải là một biểu thức hoàn chỉnh:1 + (x = 2)
Pavel Minaev

1
Để làm rõ ý kiến ​​của KonradRudolph và PavelMinaev, tôi nghĩ rằng quá mạnh để nói rằng nó hoàn toàn sai, nhưng có một ngoại lệ, đó là khi nó "bị cô lập khỏi cấu trúc logic xung quanh, bởi niềng răng hoặc một cặp dấu ngoặc đơn."
Aaron rời Stack Overflow

Hoặc trong function() x = 1, repeat x = 1, if (TRUE) x = 1....
Moody_Mudskipper

6

Điều này cũng có thể thêm vào sự hiểu biết về sự khác biệt giữa hai toán tử đó:

df <- data.frame(
      a = rnorm(10),
      b <- rnorm(10)
)

Đối với phần tử đầu tiên R đã gán các giá trị và tên thích hợp, trong khi tên của phần tử thứ hai trông hơi lạ.

str(df)
# 'data.frame': 10 obs. of  2 variables:
#  $ a             : num  0.6393 1.125 -1.2514 0.0729 -1.3292 ...
#  $ b....rnorm.10.: num  0.2485 0.0391 -1.6532 -0.3366 1.1951 ...

Phiên bản R 3.3.2 (2016-10-31); macOS Sierra 10.12.1


6
bạn có thể giải thích chi tiết hơn về lý do tại sao điều này xảy ra / chuyện gì đang xảy ra ở đây không? (gợi ý: data.framecố gắng sử dụng tên của biến được cung cấp làm tên của phần tử trong khung dữ liệu)
Ben Bolker

Chỉ cần nghĩ, điều này có thể là một lỗi? Và nếu vậy, làm thế nào và ở đâu để tôi báo cáo nó?
Denis Rasulev

7
nó không phải là một lỗi Tôi đã cố gắng gợi ý câu trả lời trong bình luận của tôi ở trên. Khi đặt tên của phần tử, R sẽ sử dụng tương đương make.names("b <- rnorm(10)").
Ben Bolker
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.