2012-10-24 22 views
6

This question numaralı özel kümeli özel işlevleri gösterme çağrısı, şu soruyu sordu: Çağrı yığınında bulunan special primitive işlevlerini görmenin bir yolu var mı?Çağrı yığını

Örneğin, çıkışta çağrı yığını döndüren bir işlev oluşturmak:

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

Bu işlevi çağırmak ve assign özel ilkel fonksiyonları kullanarak önler kullanarak bir nesnenin ile sonuç atama:

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

[[2]] 
myFun(4) 

Ancak, atama işlecini kullanarak bu,

> `<-`(myObj, myFun(6)) 
[[1]] 
myFun(6) 
yığınının dışına çıkarılır.Verilen

, tüm bu ortak çağrı yığını içinde atama operatörü görmek istemek olmayabilir, ama bu tür rep ve log gibi diğer fonksiyonları da olsun gizli ben çağrıları erişmek için herhangi bir yolu var sanmıyorum

+1

1. R Içselleri kılavuzunda O bağlantılı bölümü de ilginçtir: Daha önce İlkel fonksiyonlarını kullanmak için motivasyon çeşitliliği kabul etmemişti. '<-',' log' ve 'UseMethod' tüm temel öğelerdir, ancak oldukça farklı nedenlerle. hepsi 'ile yapılabilir - –

+0

JoshO'Brien @ O motivasyonları mutlaka doğru olduğunu sanmıyorum.Internal' işlevleri de – hadley

cevap

6

çağrı yığını yoluyla ilkel işlevler. İşte bu yüzden.

"tipik" R fonksiyonu değerlendirildiğinde:

  1. parametreler, resmi argüman eşleştirilir.
  2. Yeni bir ortam (çevreleyen ortamına bir işaretçi ile) oluşturulur ve buna resmi argümanlar atanır.
  3. İşlev gövdesi yeni oluşturulan ortamda değerlendirilir.

işlev çağrısı birbirine sys.calls(), sys.frames() ve benzerleri gibi bir erişim sağlamak için "çağrı yığını" ya da "çerçeve yığını" olduğu içinde iç içe geçmiş zaman inşa edilir kapatma ortamların zinciri.

Güçlü şüphem, çağrı yığını üzerinde ilkel işlev çağrılarının görünmemesidir çünkü hiçbir R tarafı ortamı değerlendirme sırasında oluşturulur. Hiçbir ortam oluşturulmaz, dolayısıyla çağrı yığında hiçbir ortam görünmez. Bir aramanın

Değerlendirilmesi bu işlevlerden birini normal şekilde başlar, ama ne zaman: Biraz daha iyi kavramak için

, burada John Chambers 464 Software for Data Analysis sayfasındaki ilkel fonksiyonların değerlendirilmesini açıklar nasıl değerlendirici, işlev nesnesinin, R'de tanımlanan bir işlev yerine bir ilkel olduğunu keşfeder ve tamamen farklı bir hesaplamaya yönlendirir. Nesne, yalnızca resmi bağımsız değişkenleri olan bir işlev nesnesi ve bir dize argümanı ile .Primitive() işlevine bir çağrı gibi görünüyor. Gerçekte, esas olarak, bölümünün bir çekirdeği olan bir tabloya R dizisini uygulayan C kodu içerir. Tablonun girişi, bu belirli numaralı çağrıları değerlendirmekten sorumlu olan çekirdekte bir C rutinini tanımlar. ilkel. Değerlendirici kontrolü bu rutine aktarır ve rutinini, bir C-dil işaretçisini aramanın değerini temsil eden R nesnesine döndürmesini bekler.

+2

Ayrıca 'src/main/eval.c' – hadley

+0

@hadley'nin en üstündeki yorumlara bakın. Bu ilginç ama yine de biraz aklımda. BUILTIN'in görüşmelerinin "yerleşik" ve "özel" ilkel işlevlerle nasıl ilişkili olduğunu bilmeme yardımcı olurdu. Luka Tierney, bazen bağlamın **, şimdi <-' ve 'log' (örneğin) için yaratıldığını söylüyor mu? Ve Brian Ripley'in parantezsel yorumunda "yabancı" kodu ne oluşturur? Hmm. Hala öğrenmek için çok şey var. –

+0

@ JoshO'Brien, Harika bir cevap ve SoDA'ya gösterdiğiniz için teşekkür ederiz. İşyerinde rafta, ama henüz bu son bölüme geçmemiştim. Okuduktan ve eval.c' dosyasına baktıktan sonra, c düzeyinde, bir nesnenin bir isme ("NAMED()") atanıp atanmadığını belirleyen bir mekanizma var gibi görünüyor. İşaretçiyi Tyler'ın işlevi tarafından oluşturulan nesneye elde etmek mümkün olsaydı, c kodunda bu nesnenin atanıp atanmadığını sorgulayabilirdi. Benim için de öğrenmek için çok şey var! – BenBarnes

1

Josh'un cevabın doğru olduğunu düşünmüyorum.

Örneğinizdeki <- çağrı yığınında olsaydı olur. Ama değildir.

Küçük özet: normal R işlevi değerlendirmesi, erişildiğinde tembel olarak değerlendirilen sözünü olarak ele alır.

foo(bar(baz)) 

bar(baz)

(hiç değilse) foodeğerlendirilir: Bu şu çağrıda anlamına gelir. böylece gibi bar içindeki çağrı yığını, incelemek Sonuç olarak, eğer:

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

[[2]] 
bar(baz) 

Alas belirttiğin gibi, <- (ve =) bir değil,:

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

aşağıdaki gibi ... o zaman görünüyor Normal işlev, ilkel (BUILTINSXP). aşağıdaki gibi Aslında, defined in the R source var:

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

dördüncü argümanı bir göz atın: 100. Bu koddan önceki yorum, hanelerin ne anlama geldiğini açıklar. İşte ilgili bölümü en soldaki rakamı açıklayan var:

1 Bu aşağıdaki kodu olduğunu bar(baz) çağrısı atama öncesinde değerlendirilir anlamına gelir (BUILTINSXP)

çağırmadan önce argümanları değerlendirmek diyor = Z :

<-sys.calls() listesinde görünmüyor yüzden
`<-`(x, bar(baz)) 

: o güncel bir çağrı değildir. Değerlendirmeyi bar bitirdikten sonra çağırır.


bu sınırlamasını aşmak için bir yolu var: Eğer R kodunda <-/= yeniden tanımlayabilirsiniz. Bunu yaparsanız, bu normal bir Ar fonksiyonu gibi davranı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)))) 
} 

Ancak bu <- yeniden tanımlanır kapsamında için her sonraki atama ihmal edilemez bir performans isabet tabi olduğunu dikkat: aslında, atama kabaca 1000 (!!!) daha yavaş bir faktör yapar. Bu genellikle kabul edilemez. çok ilginç ve iyi ifade edilmiş soru için