सभी खट्टे कार्य करें


11

आर में, मैं source()कुछ कार्यों को लोड करने के लिए उपयोग कर रहा हूं :

source("functions.R")

क्या इस फ़ाइल में परिभाषित सभी कार्यों की सूची प्राप्त करना संभव है? फ़ंक्शन नामों के रूप में। (शायद source()खुद किसी तरह इसे वापस कर सकते हैं?)।

पुनश्च: अंतिम उपाय source()दूसरी बार कॉल करना होगा local({ source(); })और फिर ls()अंदर और फ़ंक्शंस को फ़िल्टर करना होगा, लेकिन यह बहुत जटिल है - क्या आसान और कम अनाड़ी समाधान है?


1
यह उपयोग नहीं करता है source(), लेकिन यह पुराना धागा आपके लिए रुचि का हो सकता है।
एंड्रयू

1
@ धन्यवाद, मैंने प्रस्तावित समाधानों की जांच की है, लेकिन मुझे लगता है कि आखिरी उपाय की तुलना में यह अजीब लगता है, जो मैंने प्रश्न में प्रस्तुत किया :)
TMS

2
मैं यह नहीं जानता कि यह समाधान पागलपन है:envir <- new.env() source("functions.R", local=envir) lsf.str(envir)
LocoGris

2
अपनी स्रोत फ़ाइलों से एक पैकेज बनाएं। फिर आपको पैकेज नेमस्पेस सहित सभी फायदे मिलते हैं।
रोलैंड

@ टीएमएस ने आपके प्रश्न को गलत समझा / पढ़ा नहीं कि आप परिभाषित कार्य चाहते थे । क्षमा याचना!
एंड्रयू

जवाबों:


7

मुझे लगता है कि एक अस्थायी वातावरण में फ़ाइल को स्रोत करने का सबसे अच्छा तरीका होगा। उस वातावरण को सभी कार्यों के लिए क्वेरी करें, फिर उन मानों को मूल वातावरण में कॉपी करें।

my_source <- function(..., local=NULL) {
  tmp <- new.env(parent=parent.frame())
  source(..., local = tmp)
  funs <- names(tmp)[unlist(eapply(tmp, is.function))]
  for(x in names(tmp)) {
    assign(x, tmp[[x]], envir = parent.frame())
  }
  list(functions=funs)
}

my_source("script.R")

धन्यवाद, यह समाधान आशाजनक लग रहा है, अभी केवल एक के रूप में! आश्चर्य की बात है, एक कम से कम upvotes के साथ। यह एक है जिसे मैंने अंतिम उपाय के रूप में उल्लेख किया है, लेकिन new.env()सुरुचिपूर्ण के बजाय इसका उपयोग करना local({ })जो मुझे यकीन नहीं है कि अगर यह assignमूल फ्रेम के साथ काम करेगा ।
TMS

1) क्या आपको लगता है कि यह साथ काम करेगा local()? और बीटीडब्ल्यू, 2) आप लूप के लिए क्या करते हैं: पर्यावरण को मर्ज करने के लिए कुछ कार्य नहीं है?
टीएमएस

1
@ टीएमएस यह स्थानीय के साथ काम कर सकता है, हालांकि मैंने इसकी कोशिश नहीं की है। मैं दूसरे चर को एक वातावरण से दूसरे वातावरण में कॉपी करने के दूसरे तरीके से अनजान हूँ। यह आम ऑपरेशन नहीं है।
MrFlick

मुझे लगता है कि attachइसका इस्तेमाल किया जा सकता है, ठीक है, एक वातावरण को दूसरे में संलग्न करें। हालांकि आपको posतर्क निर्दिष्ट करने के बजाय तर्क का उपयोग करना होगा parent.frame। और यह केवल पूरे वातावरण की प्रतिलिपि बनाने के लिए अच्छी तरह से काम करेगा, MrFlick का forलूप आपको केवल फ़ंक्शन को कॉपी करने देता है।
ग्रिगोर थॉमस

5

यह थोड़ा क्लिंक है लेकिन आप sourceइस तरह से कॉल करने से पहले और बाद में ऑब्जेक्ट्स में बदलाव देख सकते हैं ।

    # optionally delete all variables
    #rm(list=ls())

    before <- ls()
    cat("f1 <- function(){}\nf2 <- function(){}\n", file = 'define_function.R')
    # defines these
    #f1 <- function() {}
    #f2 <- function() {}
    source('define_function.R')
    after <- ls()

    changed <- setdiff(after, before)
    changed_objects <- mget(changed, inherits = T)
    changed_function <- do.call(rbind, lapply(changed_objects, is.function))
    new_functions <- changed[changed_function]

    new_functions
    # [1] "f1" "f2"

धन्यवाद! मेरे पास यह विचार भी था, लेकिन यह बहुत ही सरल कारण से काम नहीं करता है - अगर पैकेज पहले से ही लोड था (जो कि हर समय होता है जब मैं कोड को डीबग करता हूं, मैं सिर्फ स्रोतों को पुन: स्रोत करता हूं), तो यह कुछ भी नहीं देता है।
टीएमएस

3

मुझे लगता है कि यह रेगेक्स लगभग हर प्रकार के फ़ंक्शन (बाइनरी ऑपरेटर, असाइनमेंट फ़ंक्शंस) और प्रत्येक वैध चरित्र को फ़ंक्शन नाम में पकड़ता है, लेकिन मैं एक किनारे का मामला याद कर सकता हूं।

# lines <- readLines("functions.R")

lines <- c(
  "`%in%` <- function",
  "foo <- function",
  "foo2bar <- function",
  "`%in%`<-function",
  "foo<-function",
  ".foo <-function",
  "foo2bar<-function",
  "`foo2bar<-`<-function",
  "`foo3bar<-`=function",
  "`foo4bar<-` = function",
  "` d d` <- function", 
  "lapply(x, function)"
)
grep("^`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?\\s*(<-|=)\\s*function", lines)
#>  [1]  1  2  3  4  5  6  7  8  9 10
funs <- grep("^`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?\\s*(<-|=)\\s*function", lines, value = TRUE)
gsub("^(`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?).*", "\\1", funs)
#>  [1] "`%in%`"      "foo "        "foo2bar "    "`%in%`"      "foo"        
#>  [6] ".foo "       "foo2bar"     "`foo2bar<-`" "`foo3bar<-`" "`foo4bar<-`"

1
मुझे लगता है कि यह वास्तव में एक अच्छा समाधान नहीं है, लेकिन यह निश्चित रूप से एक मजेदार समाधान है। अगर मुझे वास्तव में इस जानकारी की आवश्यकता है, तो मैं फ़ाइल को पैकेज में बदल दूंगा।
एलन ओसलाघन

मुझे दो किनारे याद आ गए हैं! कार्य कार्य .और कार्य के साथ शुरू हो सकते हैं ( `foo<-`<- function(x, value)मौजूद।
एलन ओलाघन

मैं =असाइनमेंट के लिए उपयोग करता हूं , यह मेरे किसी भी कार्य को पकड़ नहीं पाएगा ...
ग्रेगोर थॉमस

अच्छी पकड़ - संपादित। मैं ध्यान दूंगा कि आर आपको मूर्खतापूर्ण चीजें करने देता है जैसे ` d d` <- function(x)कि वर्तमान में पकड़ा नहीं गया है। मैं नहीं चाहता कि रेगेक्स बहुत मूर्खतापूर्ण हो, हालांकि मैं फिर से सोच सकता हूं।
एलन ओलाघन

इसके अलावा, आप के साथ काम करता है आवंटित कर सकते हैं assign, <<-और ->। और इस दृष्टिकोण को उन कार्यों के लिए बनाना बहुत कठिन होगा जो कार्यों के भीतर परिभाषित हैं , लेकिन वास्तव में खट्टे वातावरण में नहीं हैं। आपका जवाब मानक मामलों के लिए बहुत अच्छी तरह से काम करना चाहिए, लेकिन आप वास्तव में regex से बाहर एक आर पार्सर लिखना नहीं चाहते हैं।
ग्रेगर थॉमस

1

यदि यह आपकी स्वयं की स्क्रिप्ट है, ताकि आप इस पर नियंत्रण रखें कि यह कैसे स्वरूपित होता है तो एक साधारण सम्मेलन पर्याप्त होगा। बस यह सुनिश्चित करें कि प्रत्येक फ़ंक्शन नाम अपनी लाइन पर पहले वर्ण पर शुरू होता है और यह शब्द functionउस लाइन पर भी दिखाई देता है। शब्द का कोई अन्य उपयोग functionएक पंक्ति पर दिखाई देना चाहिए जो एक स्थान या टैब से शुरू होता है। फिर एक-पंक्ति समाधान है:

sub(" .*", "", grep("^\\S.*function", readLines("myscript.R"), value = TRUE))

इस दृष्टिकोण के फायदे हैं

  • यह बहुत सरल है । नियम केवल बताए गए हैं और फ़ंक्शन नामों को निकालने के लिए आवश्यक आर कोड की केवल एक सरल रेखा है। रेगेक्स भी सरल है और एक मौजूदा फ़ाइल के लिए यह जांचना बहुत आसान है - बस शब्द को पकड़ें functionऔर जांचें कि क्या प्रत्येक घटना नियम का पालन करती है।

  • स्रोत को चलाने की आवश्यकता नहीं है। यह पूरी तरह से स्थिर है

  • कई मामलों में आपको स्रोत फ़ाइल को बिल्कुल भी बदलने की आवश्यकता नहीं होगी और अन्य में भी न्यूनतम परिवर्तन होंगे। यदि आप इसे ध्यान में रखते हुए स्क्रैच से स्क्रिप्ट लिख रहे हैं तो इसे व्यवस्थित करना और भी आसान है।

सम्मेलनों के विचार के साथ कई अन्य विकल्प हैं। आपके पास एक अधिक परिष्कृत रेगेक्स हो सकता है या आप # FUNCTIONकिसी भी फ़ंक्शन परिभाषा की पहली पंक्ति के अंत में जोड़ सकते हैं यदि आप स्क्रैच से स्क्रिप्ट लिख रहे हैं और फिर उस वाक्यांश को संक्षिप्त कर दें और लाइन पर पहला शब्द निकालें लेकिन यहाँ मुख्य सुझाव लगता है इसकी सादगी और सूचीबद्ध अन्य लाभों के कारण विशेष रूप से आकर्षक।

परीक्षा

# generate test file
cat("f <- function(x) x\nf(23)\n", file = "myscript.R") 

sub(" .*", "", grep("^\\S.*function", readLines("myscript.R"), value = TRUE))
## [1] "f"

lapply(x, function(y) dostuff(y))इसे तोड़ देंगे
एलन ओलाघन

@alan ocallaghan, आपका उदाहरण उल्लिखित नियमों का उल्लंघन करता है इसलिए यह वैध रूप से नहीं हो सकता है। यह लिखने के लिए और अभी भी नियमों के भीतर रहने के लिए किसी को एक नई लाइन पर कार्य शुरू करना होगा जो इंडेंट है या किसी को लेपली इंडेंट करना पड़ सकता है।
जी। ग्रोथेंडिक

मुझे लगता है कि यदि आप विशिष्ट स्वरूपण की आवश्यकता करते हैं, तो उपयोगिता बड़े पैमाने पर कम हो जाती है, क्योंकि इसके लिए फ़ाइल को बदलने की आवश्यकता हो सकती है - जिस स्थिति में, आप उपयोगकर्ता को फ़ंक्शन नामों को मैन्युअल रूप से पढ़ने का सुझाव दे सकते हैं
एलन ओसलाघन

1
यह केवल एक विचार है यदि आप फ़ाइल को नियंत्रित नहीं करते हैं लेकिन हमने उस संभावना को बाहर रखा है। प्रोग्रामिंग में सम्मेलनों का उपयोग करना बहुत आम है। मैं अक्सर अपने # TODOपूरे कोड डाल देता हूं, इसलिए मैं उदाहरण के लिए, मुझे करने के लिए तैयार कर सकता हूं। # FUNCTIONकिसी भी फ़ंक्शन परिभाषा की पहली पंक्ति के अंत में समान पंक्तियों के साथ एक और संभावना लिखी जाएगी ।
जी। ग्रोथेंडिक

1
रेक्स के साथ पार्सिंग करने की कोशिश कर रहा है नरक के लिए सड़क ....
टीएमएस

0

यह टोकन के एक अनुक्रम (प्रतीक, असाइनमेंट ऑपरेटर, फिर फ़ंक्शन) की खोज के लिए मेरी टिप्पणी से पोस्ट में उपयोग किए गए कोड को एडाप्ट करता है, और इसे किसी भी परिभाषित कार्यों को हथियाना चाहिए। मुझे यकीन नहीं है कि अगर यह MrFlick के उत्तर के रूप में मजबूत है, लेकिन यह एक और विकल्प है:

source2 <- function(file, ...) {
  source(file, ...)
  t_t <- subset(getParseData(parse(file)), terminal == TRUE)
  subset(t_t, token == "SYMBOL" & 
           grepl("ASSIGN", c(tail(token, -1), NA), fixed = TRUE) & 
           c(tail(token, -2), NA, NA) == "FUNCTION")[["text"]]
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.