ทางเลือกที่เร็วกว่าเพื่อความไม่พอใจ ()


9

deparse(control = c("keepNA", "keepInteger"))ฉันจะรักษาแพคเกจที่อาศัยซ้ำโทรออกไปยัง controlเหมือนกันเสมอและการแสดงออกที่แตกต่างกันไป deparse()ดูเหมือนว่าจะใช้เวลามากในการตีความชุดตัวเลือกเดียวกันซ้ำ ๆ.deparseOpts()ดูเหมือนว่าจะใช้เวลามากในการตีความซ้ำชุดเดียวกันกับตัวเลือก

microbenchmark::microbenchmark(
    a = deparse(identity, control = c("keepNA", "keepInteger")),
    b = .deparseOpts(c("keepNA", "keepInteger"))
)
# Unit: microseconds
# expr min  lq  mean median  uq  max neval
#    a 7.2 7.4 8.020    7.5 7.6 55.1   100
#    b 3.0 3.2 3.387    3.4 3.5  6.0   100

ในบางระบบการ.deparseOpts()โทรซ้ำซ้อนจะใช้เวลาส่วนใหญ่ของรันไทม์ของdeparse()( กราฟไฟที่นี่ )

ฉันอยากจะโทรเพียง.deparseOpts()ครั้งเดียวแล้วส่งรหัสตัวเลขไปให้deparse()แต่ดูเหมือนจะเป็นไปไม่ได้โดยไม่ต้องโทร.Internal()หรือเรียกใช้รหัส C โดยตรงไม่ว่าจะเป็นแบบไหนดีที่สุดจากมุมมองการพัฒนาบรรจุภัณฑ์

deparse
# function (expr, width.cutoff = 60L, backtick = mode(expr) %in% 
#     c("call", "expression", "(", "function"), 
#     control = c("keepNA", "keepInteger", "niceNames", 
#         "showAttributes"), nlines = -1L) 
# .Internal(deparse(expr, width.cutoff, backtick, .deparseOpts(control), 
#     nlines))
# <bytecode: 0x0000000006ac27b8>
# <environment: namespace:base>

มีวิธีแก้ปัญหาที่สะดวกหรือไม่

คำตอบ:


4

1)กำหนดฟังก์ชั่นที่สร้างสำเนาของผู้มีความอับอายซึ่งสภาพแวดล้อมได้รับการตั้งค่าใหม่เพื่อค้นหา. deparseOpts รุ่นที่เปลี่ยนแปลงซึ่งถูกตั้งค่าให้เท่ากับฟังก์ชันเอกลักษณ์ ในRunนั้นเราเรียกใช้ฟังก์ชันนั้นเพื่อสร้างdeparse2และดำเนินการนั้น หลีกเลี่ยงการทำงาน.Internalโดยตรง

make_deparse <- function() {
  .deparseOpts <- identity
  environment(deparse) <- environment()
  deparse
}

Run <- function() {
  deparse2 <- make_deparse()
  deparse2(identity, control = 65)
}

# test
Run()

2)อีกวิธีในการทำเช่นนี้คือการกำหนดฟังก์ชั่นคอนสตรัคเตอร์ที่สร้างสภาพแวดล้อมที่จะนำสำเนาที่แก้ไขแล้วdeparseและเพิ่มการสืบค้นกลับไปยังสำเนานั้นที่นิยามใหม่.deparseOptsเป็นฟังก์ชันเอกลักษณ์ จากนั้นคืนสภาพแวดล้อมนั้น จากนั้นเราจะมีฟังก์ชั่นบางอย่างที่ใช้มันและสำหรับตัวอย่างนี้เราจะสร้างฟังก์ชั่นแสดงให้เห็นถึงมันแล้วก็ดำเนินการRun Runหลีกเลี่ยงการใช้.Internal

make_deparse_env <- function() {
  e <- environment()
  deparse <- deparse
  suppressMessages(
    trace("deparse", quote(.deparseOpts <- identity), print = FALSE, where = e)
  )
  e
}

Run <- function() {
  e <- make_deparse_env()
  e$deparse(identity, control = 65)
}

# test
Run()

3)วิธีที่สามคือการกำหนดใหม่deparseโดยการเพิ่มอาร์กิวเมนต์ใหม่ซึ่งกำหนด.deparseOptsให้มีค่าเริ่มต้นidentityและตั้งค่าcontrolให้มีค่าเริ่มต้นที่ 65

make_deparse65 <- function() {
  deparse2 <- function (expr, width.cutoff = 60L, backtick = mode(expr) %in% 
    c("call", "expression", "(", "function"), 
    control = 65, nlines = -1L, .deparseOpts = identity) {}
  body(deparse2) <- body(deparse)
  deparse2
}

Run <- function() {
  deparse65 <- make_deparse65()
  deparse65(identity)
}

# test
Run()

ว้าวช่างฉลาดเหลือเกิน !!! ฉันจะไม่คิดอย่างนั้น! รอคอยที่จะทำการเปรียบเทียบในทุกระบบของฉัน!
รถม้า

เมื่อฉันใช้ (1) และ precompute backtickอาร์กิวเมนต์การแยกเป็นเร็วขึ้น 6x! ฉันจะไปกับที่ ขอบคุณมากสำหรับวิธีแก้ปัญหา!
รถม้า

อืม ... เห็นได้ชัดว่าR CMD checkตรวจจับการ.Internal()โทรในฟังก์ชั่นที่ผลิตโดย (1) make_deparse()(expr, control = 64, backtick = TRUE)ค่อนข้างง่ายต่อการทำงานฉันแค่ต้องการ มันโง่ที่จะสร้าง deparser ใหม่ทุกครั้งที่ฉันใช้ แต่มันก็ยังเร็วกว่าความไร้เดียงสาที่deparse()ฉันเคยใช้มาก่อน
รถม้า

ไม่ได้สำหรับฉัน. ฉันพยายามสร้างแพคเกจที่มีเพียงmake_deparseและRunฟังก์ชั่นใน (1) และวิ่งR CMD buildและR CMD check --as-cranภายใต้"R version 3.6.1 Patched (2019-11-18 r77437)"และมันก็ไม่บ่นและฉันไม่ต้องการแก้ไขปัญหาใด ๆ คุณแน่ใจหรือว่าคุณไม่ได้ทำอะไรที่แตกต่างออกไป
G. Grothendieck

1
มันเกิดจากรหัสของคุณกำหนดไว้ที่ระดับบนสุด: direct_deparse <- make_direct_deparse(). Runรหัสที่แสดงในคำตอบก็คือระวังไม่ให้ทำอย่างนั้นและเฉพาะที่กำหนดไว้ในฟังก์ชั่นคือภายใน
G. Grothendieck
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.