2012-10-24 38 views
6

This question提示以下問題:有沒有辦法查看調用堆棧中的special primitive函數?在調用堆棧中顯示特殊的原始函數

例如,創建一個返回退出調用堆棧的函數:

myFun <- function(obj){ 
    on.exit(print(sys.calls())) 
    return(obj) 
} 

調用此函數,並使用assign避免了使用特殊原語功能分配其結果的一個對象:

> assign("myObj",myFun(4)) 
[[1]] 
assign("myObj", myFun(4)) 

[[2]] 
myFun(4) 

但是使用賦值運算符,這會被排除在堆棧之外

> `<-`(myObj, myFun(6)) 
[[1]] 
myFun(6) 

當然,它可能不是所有的共同希望看到在調用棧中的賦值運算符,而其他功能,如replog也得到隱蔽

+1

+1表示一個非常有趣和明確的問題。 R Internals指南中的鏈接部分也很有趣:我以前沒有考慮過使用Primitive函數的各種動機。 '<-','log'和'UseMethod'都是原語,但原因很不一樣。 –

+0

@ JoshO'Brien我不認爲這些動機必然是正確的 - 他們都可以用'完成。內部函數 – hadley

回答

6

我不認爲有任何的方式來訪問調用通過調用堆棧的原始函數。這是爲什麼。

當一個「典型的」 R函數評估:

  1. 提供的參數相匹配的形式參數。
  2. 創建一個新環境(帶有指向其環境的指針),並將正式參數分配給它。
  3. 函數的主體在新創建的環境中進行評估。

被建立起來時的函數調用被嵌套彼此是「調用棧」或「幀棧」,其sys.calls()sys.frames()等提供一些訪問內包圍的環境中的鏈。

我的強烈懷疑是調用原始函數不會出現在調用堆棧上,因爲在評估過程中沒有創建R端環境。沒有環境創建,所以沒有環境出現在調用堆棧上。

對於一些更深入的瞭解,這裏的約翰·錢伯斯這樣描述頁面上的基本功能評價464 Software for Data Analysis的:

評價打電話來的這些功能一個通常的方式開始了, 但是當評估器發現該函數對象是一個原始文件 而不是R中定義的函數,它將分支到一個完全不同的計算。該對象似乎只是一個函數對象,其形式爲 參數,並使用字符串參數調用函數.Primitive()。 實際上,它實質上只包含一個表中的索引,它是實現R的核心的C代碼的一部分。該表的條目標識負責評估對該特定的 原始。評估器將控制轉移到該例程,並且期望例程 將C語言指針返回到代表該調用的 值的R對象。

+2

另請參閱'src/main/eval.c'頂部的註釋 – hadley

+0

@hadley - 這很有趣,但仍然有點凌駕於我的頭上。如果我知道那裏討論的「BUILTIN」調用與「內建」和「特殊」原語功能有關,這將有所幫助。 Luke Tierney說有時**現在是爲'<-'和'log'創建的(例如)?在Brian Ripley的括號評論中,「外國」代碼是什麼構成的?嗯。很多仍然學習。 –

+0

@ JoshO'Brien,謝謝你的偉大的答案和SoDA的指針。這是在工作的架子上,但我還沒有得到最後一章。看完它後,看看'eval.c'文件,它看起來就像在c級一樣,有一種機制來確定一個對象是否被分配了一個名字('NAMED()')。如果可以獲取由泰勒函數創建的對象的指針,可以在c代碼中查詢該對象是否被分配。我也很想學習! – BenBarnes

1

我不認爲喬希的回答是正確的。

那麼,它是正確的,如果<-是在你的例子中的調用堆棧。 但它不是

小回顧:正常的R功能評估將參數作爲承諾,在訪問時會延遲評估。這意味着,在下面的調用:

foo(bar(baz)) 

bar(baz)評估foo(如果有的話)。因此,如果我們檢查裏面bar調用堆棧,就像這樣:

bar = function (x) { 
    sys.calls() 
} 

...那麼,它看起來如下:

[[1]] 
foo(bar(baz)) 

[[2]] 
bar(baz) 

可惜的是,正如你指出,<-(和=)不是正常功能,這是一個原始(BUILTINSXP)。事實上,它是defined in the R source如下:

{"<-",  do_set,  1, 100, -1, {PP_ASSIGN, PREC_LEFT, 1}}, 

看看第四個參數:100。此代碼之前的註釋解釋了數字的含義。下面是相關的部分,解釋了最左邊的數字:

Z = 1表示調用(BUILTINSXP

這意味着下面的代碼調用bar(baz)是轉讓前評估之前評估參數:

`<-`(x, bar(baz)) 

這就是爲什麼<-不會在sys.calls()列表中出現:它不是當前呼叫。它在bar完成評估後被調用。


有解決此限制的方法:你可以在R代碼裏面重新定義<-/=。如果你這樣做,它的行爲像一個正常的一個R函數:

`<-` = function (lhs, rhs) { 
    name = as.name(deparse(substitute(lhs), backtick = true)) 
    rhs # evaluate expression before passing it to `bquote`, for a cleaner call stack 
    eval.parent(bquote(base::`<-`(.(name), .(rhs)))) 
} 

但是,要小心,這將在那裏<-被重新定義範圍內承擔不可忽略的性能損失爲隨後指派:事實上,它使得分配大致成爲因子1000(!!!)較慢。這通常不被接受。