Mengevaluasi ekspresi yang diberikan sebagai string


283

Saya ingin tahu apakah R dapat menggunakannya eval() fungsinya untuk melakukan perhitungan yang disediakan oleh misalnya string.

Ini adalah kasus umum:

eval("5+5")

Namun, bukannya 10 saya mendapatkan:

[1] "5+5"

Ada solusi?


6
Terlepas dari semua jawaban yang menunjukkan bagaimana menyelesaikannya dengan parse ... Mengapa Anda perlu menyimpan jenis bahasa dalam karakter string? Jawaban Martin Mächler patut mendapat lebih banyak dukungan.
Petr Matousu

7
@PetrMatousu terima kasih. Ya, saya terkejut melihat bagaimana informasi yang salah tersebar di SO sekarang .. oleh orang-orang yang memilih eval(parse(text = *)) solusi palsu.
Martin Mächler

2
Saya ingin menjalankan skrip dalam bentuk:, QQ = c('11','12','13','21','22','23')yaitu: QQ = c (..., 'ij', ..) dengan i, j yang bervariasi pada rentang yang mungkin berbeda dari yang dijalankan ke yang dijalankan. Untuk contoh ini dan yang serupa, saya dapat menulis skrip sebagai paste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep=""), dan opsi eval(parse(text=...))menciptakan vektor QQ di lingkungan kerja sesuai skrip. Apa yang akan menjadi cara pengkode R yang tepat untuk melakukan ini, jika tidak dengan "text = ..."?
VictorZurkowski

Jawaban:


419

The eval()Fungsi mengevaluasi ekspresi, tetapi "5+5"adalah sebuah string, bukan ekspresi. Gunakan parse()dengan text=<string>untuk mengubah string menjadi ekspresi:

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

Panggilan eval()memanggil banyak perilaku, beberapa tidak segera jelas:

> 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

Lihat juga tryCatch .


27
Seperti yang dicatat Shane di bawah, "Anda harus menentukan bahwa inputnya berupa teks, karena parse mengharapkan file secara default"
PatrickT

1
efek samping dari penggunaan eval (parse) harus ditentukan. Misalnya, jika Anda memiliki nama variabel yang ditentukan sebelumnya sama dengan "David" dan Anda menetapkan kembali menggunakan eval (parse (text = "name") == "Alexander", Anda akan mendapatkan kesalahan karena eval & parse tidak mengembalikan Ekspresi R yang dapat dievaluasi
Crt

1
@NelsonGon: ekspresi unevaluated dibangun menggunakan quote(), bquote()atau alat yang lebih canggih yang disediakan oleh rlangpaket.
Artem Sokolov

@ Artemokolon Terima kasih, saya entah bagaimana terus kembali ke pertanyaan ini mencari alternatif. Saya telah melihat rlangtetapi yang paling dekat saya temukan adalah parse_exprpanggilan parse_exprsyang pada gilirannya sama dengan menggunakan parsedan membungkusnya evalyang tampaknya sama dengan yang dilakukan di sini. Saya tidak yakin apa keuntungan menggunakan rlang.
NelsonGon

1
@NelsonGon: with rlang, Anda akan bekerja secara langsung dengan ekspresi, bukan string. Tidak perlu langkah parse. Ini memiliki dua keunggulan. 1. Manipulasi ekspresi akan selalu menghasilkan ekspresi yang valid. Manipulasi string hanya akan menghasilkan string yang valid. Anda tidak akan tahu apakah itu ekspresi yang valid hingga Anda menguraikannya. 2. Tidak ada yang setara dengan substitute()kelas fungsi di dunia string, yang sangat membatasi kemampuan Anda untuk memanipulasi panggilan fungsi. Pertimbangkan pembungkus glm ini . Seperti apa bentuk string yang setara?
Artem Sokolov

100

Anda dapat menggunakan parse()fungsi ini untuk mengubah karakter menjadi ekspresi. Anda perlu menentukan bahwa input adalah teks, karena parse mengharapkan file secara default:

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

7
> fortunes :: fortune ("answer is parse") Jika jawabannya parse () Anda biasanya harus memikirkan kembali pertanyaannya. - Thomas Lumley R-help (Februari 2005)>
Martin Mächler

13
@ MartinMächler Sungguh ironis, karena paket inti R selalu digunakan parse! github.com/wch/r-source/…
geneorama

49

Maaf tapi saya tidak mengerti mengapa terlalu banyak orang berpikir string adalah sesuatu yang bisa dievaluasi. Anda harus mengubah pola pikir Anda, sungguh. Lupakan semua koneksi antara string di satu sisi dan ekspresi, panggilan, evaluasi di sisi lain.

Koneksi (mungkin) hanya melalui parse(text = ....)dan semua programmer R yang baik harus tahu bahwa ini jarang cara yang efisien atau aman untuk membangun ekspresi (atau panggilan). Sebaliknya mempelajari lebih lanjut tentang substitute(), quote(), dan mungkin kekuatan menggunakan 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, ini sebuah contoh (dalam komentar, tidak ada format yang bagus):

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

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

dan jika Anda mendapatkan lebih berpengalaman Anda akan belajar bahwa q5adalah "call"sedangkan e5adalah "expression", dan bahkan yang e5[[1]]identik dengan q5:

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

4
bisakah kamu memberikan contoh? mungkin Anda bisa menunjukkan kepada kami cara "bertahan" hingga 5 + 5 pada objek r, lalu mengevaluasi nanti, menggunakan kutipan dan pengganti daripada karakter dan eval (parse (text =)?
Richard DiSalvo

3
Saya mungkin sedikit tersesat. Pada poin apa Anda mendapatkan 10? Atau bukan itu intinya?
Nick S

@RichardDiSalvo: ya, di q5 <- quote(5+5)atas adalah ekspresi (sebenarnya "panggilan") 5+5dan itu adalah objek R, tetapi bukan string. Anda dapat mengevaluasinya kapan saja. Lagi: menggunakan, kutipan (), pengganti (), ... alih-alih parse membuat panggilan atau ekspresi secara langsung dan lebih efisien daripada melalui parse (teks =.). Menggunakan eval()baik-baik saja, menggunakan parse(text=*)rentan terhadap kesalahan dan kadang-kadang cukup tidak efisien dibandingkan dengan panggilan konstruksi dan memanipulasi mereka .. @Nick S: Ini eval(q5) atau eval(e5) dalam contoh yang sedang berjalan
Martin Mächler

@NickS: Untuk mendapatkan 10, Anda mengevaluasi panggilan / ekspresi, yaitu, panggil eval(.)saja. Maksud saya adalah bahwa orang tidak boleh menggunakan parse(text=.)melainkan quote(.)dll, untuk membangun panggilan yang nantinya akan eval()diedit.
Martin Mächler

2
eval(quote())memang berfungsi dalam beberapa kasus tetapi akan gagal untuk beberapa kasus di mana eval(parse())akan bekerja dengan baik.
NelsonGon

18

Atau, Anda dapat menggunakan evalsdari panderpaket saya untuk menangkap output dan semua peringatan, kesalahan, dan pesan lainnya bersama dengan hasil mentah:

> 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
Fungsi bagus; mengisi lubang yang ditinggalkan evaluate::evaluatedengan benar-benar mengembalikan objek hasil; yang membuat fungsi Anda cocok untuk digunakan menelepon melalui mclapply. Saya harap fitur itu tetap ada!
russellpierce

Terima kasih, @ rpierce. Fungsi ini awalnya ditulis pada tahun 2011 sebagai bagian dari rapportpaket kami , dan telah secara aktif dipertahankan sejak saat itu karena banyak digunakan dalam layanan rapporter.net kami selain beberapa proyek lain juga - jadi saya yakin itu akan tetap dipertahankan untuk sementara :) Saya senang Anda menemukan itu berguna, terima kasih atas tanggapan Anda.
daroczig


2

Demikian pula dengan menggunakan rlang:

eval(parse_expr("5+5"))

3
Datang ke sini mencari rlangjawaban tetapi bagaimana jika ada keuntungan dari alternatif ini? Sebenarnya, pemeriksaan ketat terhadap kode yang digunakan menunjukkan bahwa sebenarnya menggunakan eval(parse(....))yang ingin saya hindari.
NelsonGon

4
Tidak hanya itu negatif, tetapi namanya juga menyesatkan. Ini TIDAK mengevaluasi ekspresi. Seharusnya disebut parse_to_expr atau sesuatu yang lain untuk menunjukkan bahwa pengguna akan tahu bahwa itu dimaksudkan untuk argumen karakter.
IRTFM
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.