ประเมินการแสดงออกที่กำหนดให้เป็นสตริง


283

ฉันอยากรู้ว่า R สามารถใช้eval()ฟังก์ชั่นเพื่อทำการคำนวณโดยสตริงหรือไม่

นี่เป็นกรณีทั่วไป:

eval("5+5")

อย่างไรก็ตามแทนที่จะเป็น 10 ฉันจะได้รับ:

[1] "5+5"

ทางออกใด ๆ


6
แม้จะมีคำตอบทั้งหมดที่แสดงวิธีแก้ปัญหาด้วยการแยกวิเคราะห์ ... ทำไมคุณต้องจัดเก็บประเภทภาษาในตัวละครstring? คำตอบของ Martin Mächlerควรได้รับการโหวตมากขึ้น
Petr Matousu

7
ขอบคุณ @PetrMatousu ใช่ฉันตกใจเมื่อเห็นว่ามีการเผยแพร่ข้อมูลผิดพลาดไปทาง SO ตอนนี้ .. โดยผู้ที่พยายาม eval(parse(text = *)) แก้ปัญหาปลอม
Martin Mächler

2
ฉันต้องการเรียกใช้ scrips ของแบบฟอร์ม: QQ = c('11','12','13','21','22','23')เช่น: QQ = c (... , 'ij', .. ) โดยที่ฉัน, j แตกต่างกันไปในช่วงที่อาจแตกต่างจากการวิ่งเพื่อวิ่ง สำหรับตัวอย่างนี้และที่คล้ายกันฉันสามารถเขียนสคริปต์เป็นpaste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep="")และตัวเลือกeval(parse(text=...))สร้างเวกเตอร์ QQ ในสภาพแวดล้อมการทำงานตามสคริปต์ อะไรจะเป็นวิธีที่เหมาะสม coder R ในการทำเช่นนี้ถ้าไม่ได้อยู่กับ "text = ... "?
VictorZurkowski

คำตอบ:


418

eval()ฟังก์ชั่นการประเมินการแสดงออก แต่"5+5"เป็นสตริงไม่แสดงออก ใช้parse()กับtext=<string>เพื่อเปลี่ยนสตริงเป็นนิพจน์:

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

การเรียกeval()ใช้พฤติกรรมหลายอย่างบางอย่างไม่ชัดเจนในทันที

> 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

ดูเพิ่มเติมtryCatch


27
ในฐานะที่เป็นเชนบันทึกด้านล่าง "คุณต้องระบุว่าการป้อนข้อมูลเป็นข้อความเพราะแจงคาดว่าไฟล์โดยค่าเริ่มต้น"
PatrickT

1
ควรระบุผลข้างเคียงของการใช้ eval (การแยกวิเคราะห์) ตัวอย่างเช่นหากคุณมีชื่อตัวแปรที่กำหนดไว้ล่วงหน้าเท่ากับ "David" และคุณกำหนดใหม่โดยใช้ eval (parse (text = "name") == "Alexander") คุณจะได้รับข้อผิดพลาดเนื่องจาก eval & parse ไม่ส่งคืน นิพจน์ R ที่สามารถประเมินได้
Crt

1
@NelsonGon: สำนวน unevaluated สร้างขึ้นโดยใช้quote(), bquote()หรือเครื่องมือที่ซับซ้อนมากขึ้นให้โดยrlangแพคเกจ
Artem Sokolov

@ArtemSokolov ขอบคุณฉันยังคงกลับมาที่คำถามนี้เพื่อหาทางเลือก ฉันได้ดูแล้วrlangแต่สิ่งที่ใกล้เคียงที่สุดที่ฉันพบคือparse_exprสายparse_exprsที่หันไปเหมือนกับการใช้parseและพันสายevalซึ่งดูเหมือนจะเหมือนกับที่ทำกันที่นี่ ฉันไม่แน่ใจว่าจะใช้ประโยชน์จากrlangอะไร
NelsonGon

1
@NelsonGon: ด้วยrlangคุณจะทำงานโดยตรงกับนิพจน์ไม่ใช่สตริง ไม่จำเป็นต้องแยกขั้นตอน มันมีข้อดีสองประการ 1. การปรับเปลี่ยนนิพจน์จะสร้างนิพจน์ที่ถูกต้องเสมอ การจัดการสตริงจะสร้างสตริงที่ถูกต้องเท่านั้น คุณจะไม่รู้ว่ามันเป็นนิพจน์ที่ถูกต้องหรือไม่จนกว่าคุณจะวิเคราะห์คำ 2. ไม่เท่ากับsubstitute()คลาสของฟังก์ชันในโลกสตริงซึ่งจำกัดความสามารถของคุณในการจัดการการเรียกใช้ฟังก์ชันอย่างรุนแรง พิจารณาเสื้อคลุม glmนี้ สตริงที่เทียบเท่าจะมีลักษณะอย่างไร
Artem Sokolov

100

คุณสามารถใช้parse()ฟังก์ชันเพื่อแปลงอักขระเป็นนิพจน์ คุณต้องระบุว่าการป้อนข้อมูลเป็นข้อความเนื่องจากการแยกวิเคราะห์คาดว่าไฟล์ตามค่าเริ่มต้น:

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

7
> fortunes :: fortune ("answer is parse") หากคำตอบคือ parse () คุณควรคิดใหม่อีกครั้ง - Thomas Lumley R-help (กุมภาพันธ์ 2005)>
Martin Mächler

13
@ MartinMächlerนั่นเป็นเรื่องที่น่าขันเพราะแพ็คเกจ R หลักใช้parseตลอดเวลา! github.com/wch/r-source/…
genorama

49

ขออภัยฉันไม่เข้าใจว่าเพราะเหตุใดคนจำนวนมากเกินไปถึงคิดว่าสตริงเป็นสิ่งที่สามารถประเมินได้ คุณต้องเปลี่ยนความคิดของคุณจริงๆ ลืมการเชื่อมต่อทั้งหมดระหว่างสตริงที่ด้านหนึ่งและการแสดงออกการโทรการประเมินผลในอีกด้านหนึ่ง

การเชื่อมต่อ (เป็นไปได้) เท่านั้นคือผ่านparse(text = ....)และโปรแกรมเมอร์ R ที่ดีทุกคนควรรู้ว่านี่ไม่ค่อยเป็นวิธีที่มีประสิทธิภาพหรือปลอดภัยในการสร้างนิพจน์ (หรือการโทร) แต่เรียนรู้เพิ่มเติมเกี่ยวกับsubstitute(), และอาจใช้พลังงานของการใช้quote()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: ตกลงนี่คือตัวอย่าง (ในความคิดเห็นไม่มีการจัดรูปแบบที่ดี):

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

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

และถ้าคุณได้รับประสบการณ์มากขึ้นคุณจะได้เรียนรู้ว่าq5เป็น"call"ในขณะที่e5เป็น"expression"และแม้กระทั่งว่าe5[[1]]เป็นเหมือนq5:

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

4
ยกตัวอย่างได้ไหม บางทีคุณอาจจะแสดงให้เราเห็นวิธีการ "จับ" 5 + 5 ในวัตถุ R แล้วประเมินได้ในภายหลังโดยใช้คำพูดและแทนมากกว่าตัวอักษรและ EVAL (แยก (ข้อความ =)
ริชาร์ด DiSalvo

3
ฉันอาจจะหายไปเล็กน้อย คุณได้ 10 คะแนนเท่าไหร่? หรือนั่นไม่ใช่ประเด็น?
Nick S

@RichardDiSalvo: ใช่q5 <- quote(5+5)ข้างต้นเป็นนิพจน์ (ที่จริงแล้ว "call") 5+5และมันเป็นวัตถุ R แต่ไม่ใช่สตริง คุณสามารถประเมินได้ตลอดเวลา อีกครั้ง: using, quote (), replace (), ... แทนการแยกวิเคราะห์สร้างการโทรหรือการแสดงออกโดยตรงและมีประสิทธิภาพมากกว่าการแยกวิเคราะห์ (text =.) การใช้ eval()ดีใช้งานparse(text=*)ได้ง่ายข้อผิดพลาดและบางครั้งค่อนข้างไม่มีประสิทธิภาพเมื่อเทียบกับสายงานก่อสร้างและจัดการกับพวกเขา .. @Nick S: มัน eval(q5) หรือeval(e5) ในตัวอย่างการทำงานของเรา
Martin Mächler

@NickS: เพื่อให้ได้ 10 คุณประเมินโทร / การแสดงออกเช่นการเรียกร้องeval(.)เกี่ยวกับมัน ประเด็นของฉันคือผู้คนไม่ควรใช้parse(text=.)แต่ควรทำquote(.)เช่นนั้นเพื่อสร้างการโทรซึ่งในภายหลังจะได้รับการeval()แก้ไข
Martin Mächler

2
eval(quote())ทำงานในบางกรณี แต่จะล้มเหลวในบางกรณีที่eval(parse())จะทำงานได้ดี
NelsonGon

18

อีกวิธีหนึ่งคุณสามารถใช้evalsจากpanderแพ็คเกจของฉันเพื่อบันทึกผลลัพธ์และคำเตือนข้อผิดพลาดและข้อความอื่น ๆ ทั้งหมดพร้อมกับผลลัพธ์ดิบ:

> 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
ฟังก์ชั่นที่ดี; เติมหลุมที่เหลือโดยการevaluate::evaluateส่งคืนวัตถุผลจริง; ที่ทำให้ฟังก์ชั่นของคุณเหมาะสำหรับใช้โทรผ่าน mclapply ฉันหวังว่าคุณสมบัตินี้จะยังคงอยู่!
russellpierce

ขอบคุณ @rpierce ฟังก์ชั่นนี้ถูกเขียนขึ้นครั้งแรกในปี 2011 เป็นส่วนหนึ่งของrapportแพ็คเกจของเราและได้รับการบำรุงรักษาอย่างแข็งขันตั้งแต่นั้นมาถูกใช้อย่างหนักในบริการrapporter.netของเรานอกเหนือจากโครงการอื่น ๆ ไม่กี่โครงการเช่นกัน - ดังนั้นฉันแน่ใจว่า ในขณะที่ :) ฉันดีใจที่คุณพบว่ามีประโยชน์ขอบคุณสำหรับคำติชมของคุณ
daroczig


2

ในทำนองเดียวกันโดยใช้rlang:

eval(parse_expr("5+5"))

3
มาที่นี่เพื่อค้นหาrlangคำตอบ แต่ถ้ามีข้อได้เปรียบของทางเลือกพื้นฐานนี้ ที่จริงแล้วการตรวจสอบอย่างใกล้ชิดของรหัสที่ใช้แสดงให้เห็นว่าในความเป็นจริงการใช้eval(parse(....))ที่ฉันต้องการหลีกเลี่ยง
NelsonGon

4
ไม่เพียง แต่ฟิล์มเนกาทีฟเท่านั้น แต่ชื่อของมันก็ทำให้เข้าใจผิด มันไม่ได้ประเมินการแสดงออก ควรเรียกว่า parse_to_expr สิ่งอื่นเพื่อระบุว่าผู้ใช้จะรู้ว่ามันมีไว้สำหรับอาร์กิวเมนต์ตัวละคร
IRTFM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.