Đánh giá biểu thức được đưa ra dưới dạng một chuỗi


283

Tôi tò mò muốn biết liệu R có thể sử dụng eval()chức năng của nó để thực hiện các phép tính được cung cấp bởi ví dụ như một chuỗi không.

Đây là một trường hợp phổ biến:

eval("5+5")

Tuy nhiên, thay vì 10 tôi nhận được:

[1] "5+5"

Bất kì giải pháp nào?


6
Bất chấp tất cả các câu trả lời cho thấy cách giải quyết điều đó bằng phân tích cú pháp ... Tại sao bạn cần lưu trữ các loại ngôn ngữ trong một ký tự string? Câu trả lời của Martin Mächler xứng đáng nhận được nhiều sự ủng hộ hơn.
Petr Matousu

7
Cảm ơn bạn @PetrMatousu. Có, tôi bị sốc khi thấy thông tin sai lệch được lan truyền trên SO bây giờ .. bởi những người ủng hộ eval(parse(text = *)) các giải pháp giả mạo.
Martin Mächler

2
Tôi muốn chạy các đoạn mã có dạng : QQ = c('11','12','13','21','22','23'), ví dụ: QQ = c (..., 'ij', ..) với i, j khác nhau trên một phạm vi có thể thay đổi từ chạy sang chạy. Đối với ví dụ này và các ví dụ tương tự, tôi có thể viết tập lệnh dưới dạng paste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep="")và tùy chọn eval(parse(text=...))tạo vectơ QQ trong môi trường làm việc theo kịch bản. Điều gì sẽ là cách mã hóa R thích hợp để làm điều này, nếu không phải với "text = ..."?
VictorZurkowski

Câu trả lời:


417

Các eval()chức năng đánh giá một biểu thức, nhưng "5+5"là một chuỗi, không phải là một biểu hiện. Sử dụng parse()với text=<string>để thay đổi chuỗi thành một biểu thức:

> eval(parse(text="5+5"))
[1] 10
> class("5+5")
[1] "character"
> class(parse(text="5+5"))
[1] "expression"

Gọi eval()gọi nhiều hành vi, một số không rõ ràng ngay lập tức:

> class(eval(parse(text="5+5")))
[1] "numeric"
> class(eval(parse(text="gray")))
[1] "function"
> class(eval(parse(text="blue")))
Error in eval(expr, envir, enclos) : object 'blue' not found

Xem thêm thử .


27
Như Shane lưu ý bên dưới, "Bạn cần xác định rằng đầu vào là văn bản, vì phân tích cú pháp mong đợi một tệp theo mặc định"
PatrickT

1
tác dụng phụ của việc sử dụng eval (phân tích cú pháp) nên được chỉ định. Ví dụ: nếu bạn có tên biến được xác định trước bằng "David" và bạn gán lại bằng eval (parse (text = "name") == "Alexander", bạn sẽ gặp lỗi vì eval & parse không trả về Biểu thức R có thể được đánh giá.
Crt

1
@NelsonGon: biểu Unevaluated xây dựng sử dụng quote(), bquote()hoặc các công cụ phức tạp hơn được cung cấp bởi các rlanggói.
Artem Sokolov

@ArtemSokolov Cảm ơn, bằng cách nào đó tôi tiếp tục quay lại câu hỏi này để tìm kiếm một giải pháp thay thế. Tôi đã xem rlangnhưng gần nhất tôi tìm thấy là parse_exprcác cuộc gọi parse_exprslần lượt giống như sử dụng parsevà gói nó trong evalđó dường như là điều tương tự như được thực hiện ở đây. Tôi không chắc chắn những lợi thế sẽ được sử dụng rlang.
NelsonGon

1
@NelsonGon: với rlang, bạn sẽ làm việc trực tiếp với các biểu thức, không phải chuỗi. Không có bước phân tích cần thiết. Nó có hai lợi thế. 1. Thao tác biểu thức sẽ luôn tạo ra các biểu thức hợp lệ. Thao tác chuỗi sẽ chỉ tạo ra chuỗi hợp lệ. Bạn sẽ không biết nếu chúng là biểu thức hợp lệ cho đến khi bạn phân tích chúng. 2. Không có tương đương với substitute()lớp chức năng trong thế giới chuỗi, điều này hạn chế nghiêm trọng khả năng của bạn để thao tác các lệnh gọi hàm. Hãy xem xét gói bọc glm này . Một chuỗi tương đương sẽ trông như thế nào?
Artem Sokolov

99

Bạn có thể sử dụng parse()hàm để chuyển đổi các ký tự thành một biểu thức. Bạn cần xác định rằng đầu vào là văn bản, vì phân tích cú pháp mong đợi một tệp theo mặc định:

eval(parse(text="5+5"))

7
> fortunes :: fortune ("answer is parse") Nếu câu trả lời là parse () bạn thường nên suy nghĩ lại về câu hỏi. - Thomas Lumley R-help (Tháng 2 năm 2005)>
Martin Mächler

13
@ MartinMächler Thật là mỉa mai, bởi vì các gói R lõi sử dụng parsemọi lúc! github.com/wch/r-source/...
geneorama

49

Xin lỗi nhưng tôi không hiểu tại sao quá nhiều người thậm chí nghĩ rằng một chuỗi là thứ có thể được đánh giá. Bạn phải thay đổi suy nghĩ của bạn, thực sự. Quên tất cả các kết nối giữa các chuỗi ở một bên và các biểu thức, cuộc gọi, đánh giá ở phía bên kia.

Kết nối (có thể) chỉ thông qua parse(text = ....)và tất cả các lập trình viên R giỏi nên biết rằng đây hiếm khi là một phương tiện hiệu quả hoặc an toàn để xây dựng các biểu thức (hoặc các cuộc gọi). Thay vì tìm hiểu thêm về substitute(), quote()và có thể sức mạnh của việc sử dụng do.call(substitute, ......).

fortunes::fortune("answer is parse")
# If the answer is parse() you should usually rethink the question.
#    -- Thomas Lumley
#       R-help (February 2005)

Dec.2017: Ok, đây là một ví dụ (trong các bình luận, không có định dạng đẹp):

q5 <- quote(5+5)
str(q5)
# language 5 + 5

e5 <- expression(5+5)
str(e5)
# expression(5 + 5)

và nếu bạn nhận được nhiều kinh nghiệm bạn sẽ biết rằng q5là một "call"trong khi e5là một "expression", và thậm chí là e5[[1]]giống hệt q5:

identical(q5, e5[[1]])
# [1] TRUE

4
bạn có thể cho một ví dụ? có lẽ bạn có thể chỉ cho chúng tôi cách "giữ" đến 5 + 5 trong một đối tượng r, sau đó đánh giá nó sau, sử dụng trích dẫn và thay thế thay vì một ký tự và eval (parse (text =)?
Richard DiSalvo

3
Tôi có thể bị mất một chút. Tại thời điểm nào bạn nhận được 10? Hay đó không phải là vấn đề?
Nick S

@RichardDiSalvo: có, q5 <- quote(5+5)ở trên biểu thức (thực ra là "cuộc gọi") 5+5và nó là một đối tượng R, nhưng không phải là một chuỗi. Bạn có thể đánh giá nó bất cứ lúc nào. Một lần nữa: bằng cách sử dụng, quote (), thay thế (), ... thay vào đó, parse tạo ra các cuộc gọi hoặc biểu thức trực tiếp và hiệu quả hơn là thông qua parse (text =.). Sử dụng eval()là tốt, sử dụng parse(text=*)là dễ bị lỗi và đôi khi khá kém hiệu quả so với các cuộc gọi xây dựng và thao túng chúng .. @Nick S: Đó là eval(q5) hoặc eval(e5) trong ví dụ chạy của chúng tôi
Martin Mächler

@NickS: Để có được 10, bạn đánh giá cuộc gọi / biểu thức, tức là gọi eval(.)vào nó. Quan điểm của tôi là mọi người không nên sử dụng parse(text=.)mà là quote(.)vân vân, để xây dựng cuộc gọi mà sau này sẽ được chỉnh sửa eval().
Martin Mächler

2
eval(quote())không hoạt động trong một vài trường hợp nhưng sẽ thất bại trong một số trường hợp eval(parse())sẽ hoạt động tốt.
NelsonGon

18

Ngoài ra, bạn có thể sử dụng evalstừ pandergói của tôi để nắm bắt đầu ra và tất cả các cảnh báo, lỗi và thông báo khác cùng với kết quả thô:

> pander::evals("5+5")
[[1]]
$src
[1] "5 + 5"

$result
[1] 10

$output
[1] "[1] 10"

$type
[1] "numeric"

$msg
$msg$messages
NULL

$msg$warnings
NULL

$msg$errors
NULL


$stdout
NULL

attr(,"class")
[1] "evals"

2
Chức năng đẹp; lấp đầy một lỗ còn lại bằng evaluate::evaluatecách thực sự trả về đối tượng kết quả; mà chức năng của bạn phù hợp để sử dụng để gọi qua mclapply. Tôi hy vọng tính năng đó vẫn còn!
russellpierce

Cảm ơn bạn, @rpierce. Chức năng này ban đầu được viết vào năm 2011 như là một phần của rapportgói của chúng tôi và đã được duy trì tích cực kể từ đó vì được sử dụng rất nhiều trong dịch vụ rapporter.net của chúng tôi bên cạnh một số dự án khác - vì vậy tôi chắc chắn rằng nó sẽ được duy trì cho trong khi :) tôi rất vui vì bạn thấy nó hữu ích, cảm ơn phản hồi của bạn
daroczig


2

Tương tự sử dụng rlang:

eval(parse_expr("5+5"))

3
Đến đây để tìm kiếm một rlangcâu trả lời, nhưng nếu có lợi thế của điều này so với các lựa chọn thay thế cơ sở thì sao? Trên thực tế, kiểm tra chặt chẽ mã được sử dụng cho thấy rằng thực tế nó đang sử dụng eval(parse(....))mà tôi muốn tránh.
NelsonGon

4
Không chỉ những tiêu cực, mà tên của nó cũng gây hiểu nhầm. Nó KHÔNG đánh giá một biểu thức. Nên được gọi là parse_to_expr nếu một cái gì đó khác để chỉ ra rằng người dùng sẽ biết rằng nó dành cho các đối số ký tự.
IRTFM
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.