R'de golf oynamak için ipuçları


58

R istatistik dilinde golf oynamak için ipuçları arıyorum. R, Golf için belki sıra dışı bir seçimdir. Ancak, bazı şeyleri çok kompakt bir şekilde yapar (diziler, rastgelelik, vektörler ve listeler), yerleşik işlevlerin çoğu çok kısa adlara sahiptir ve isteğe bağlı bir satır sonlandırıcısına (;) sahiptir. R'deki kod golf sorunlarını çözmenize yardımcı olacak hangi ipuçlarını ve püf noktalarını verebilirsiniz?


14
Bu sorunun cevabı R koduna karşı anti-styleguide olarak iki katına çıkabiliyordu, kod golf gerçekten bu işlerin çoğunu yapmanız gereken tek zamandı. :-)
Andrew Brēza

Yanıtlar:


44

Bazı ipuçları:

  1. R'de, <-üzerinden kullanılması önerilir =. Golf oynamak için ters tutar =daha kısadır ...
  2. Bir işlevi birden çok kez çağırırsanız, bunun için kısa bir diğer ad tanımlamak genellikle yararlı olur:

    as.numeric(x)+as.numeric(y)
    
    a=as.numeric;a(x)+a(y)
    
  3. Kısmi eşleme, özellikle işlevler yalnızca bir öğeye ihtiyacınız olan listeleri döndürdüğünde arkadaşınız olabilir. karşılaştırma rle(x)$lengthsiçinrle(x)$l

  4. Pek çok zorluk, girişi okumanızı gerektirir. scangenellikle bunun için iyi bir seçimdir (kullanıcı girişi boş bir satır girerek sonlandırır).

    scan()    # reads numbers into a vector
    scan(,'') # reads strings into a vector
    
  5. Zorlama faydalı olabilir. t=1daha kısa t=TRUE. Alternatif olarak, switchdeğerli karakterleri de kaydedebilirsiniz, ancak 0,1 yerine 1,2 kullanmak isteyeceksiniz.

    if(length(x)) {} # TRUE if length != 0
    sum(x<3)         # Adds all the TRUE:s (count TRUE)
    
  6. Bir işlev karmaşık bir şey hesaplarsa ve aynı temel değere dayalı olarak başka tür hesaplama türlerine ihtiyaç duyuyorsanız, genellikle aşağıdakilerden birinin faydası vardır: a) daha küçük işlevlere ayırmak, b) ihtiyacınız olan tüm sonuçları liste halinde döndürmek veya c) işlevin argümanına bağlı olarak farklı değer türlerini döndürmesini sağlayın.

  7. Herhangi bir dilde olduğu gibi, iyi bildiği gibi - R'nin binlerce işlevi vardır, muhtemelen sorunu birkaç karakterde çözebilecek birileri vardır - hile hangisinin olduğunu bilmek!

Bazı belirsiz fakat kullanışlı işlevler:

sequence
diff
rle
embed
gl # Like rep(seq(),each=...) but returns a factor

Bazı dahili veri setleri ve semboller:

letters     # 'a','b','c'...
LETTERS     # 'A','B','C'...
month.abb   # 'Jan','Feb'...
month.name  # 'January','Feburary'...
T           # TRUE
F           # FALSE
pi          # 3.14...

22
  1. Bir paketi birlikte almak yerine, librarykullanılan paketi kullanarak değişkeni alın ::. Aşağıdakileri karşılaştırın:

    library(splancs);inout(...)
    splancs::inout(...)
    

    Tabii ki, sadece pakette tek bir fonksiyon kullanılıyorsa geçerlidir.

  2. Bu önemsiz ama bir işlev aliasing ait Tommy'nin hilesi @ ne zaman kullanılacağına ilişkin bir pratik kural: En işlev adı uzunluğu varsa mve kullanılırsa no zaman sadece takma, kat m*n > m+n+3harcama takma tanımlarken (çünkü m+3sonra hala harcamak 1 takma ad her kullanıldığında). Bir örnek:

    nrow(a)+nrow(b)     # 4*2 < 4+3+2
    n=nrow;n(a)+n(b)
    length(a)+length(b) # 6*2 > 6+3+2
    l=length;l(a)+l(b)
    
  3. Fonksiyonların yan etkisi olarak zorlama:

    • kullanmak yerine as.integer, karakter dizeleri, tamsayılı kullanarak zorlanabilir ::

      as.integer("19")
      ("19":1)[1] #Shorter version using force coercion.
      
    • tamsayı, sayısal vb., karakterleri kullanmak pasteyerine benzer şekilde zorlanabilir as.character:

      as.character(19)
      paste(19) #Shorter version using force coercion.
      

6
Re: 3. ipucu, el("19":1)bir bayt bile daha kısadır.
JayCe

19

Bazı çok özel golf ipuçları:

  • Bir vektörün uzunluğunu çıkarmanız gerekirse , sayısal, tam sayı, karmaşık veya mantıksal süreden sum(x|1)daha kısadır .length(x)x
  • Bir vektörün son öğe ayıklamak için gerekiyorsa, o (mümkünse) vektörü başlatmak için ucuz olabilir geriye kullanarak rev()ve sonra arayarak x[1]yerine x[length(x)](veya yukarıdaki ucunu kullanarak x[sum(x|1)]) (veya tail(x,1)--- teşekkürler Giuseppe!). (İkinci son öğe arzu edilmiştir), bu ilgili küçük bir farklılık görülebilir burada . Vektörü geriye doğru başlatamasanız bile rev(x)[1], hala daha kısadır x[sum(x|1)](ve karakter vektörleri için de çalışır). Bazen rev, örneğin n:1yerine kullanmak bile gerekmez 1:n.
  • (Görüldüğü gibi burada ). Bir veri çerçevesini bir matrise zorlamak istiyorsanız, kullanmayın as.matrix(x). Devriklerin transpozisyonunu al t(t(x)).

  • ifresmi bir işlevdir. Örneğin, "if"(x<y,2,3)daha kısadır if(x<y)2 else 3(elbette 3-(x<y)her ikisinden de kısadır). Bu, yalnızca sık sık yaptığınız şekilde bu şekilde formüle etmek için fazladan bir parantez gerekmediğinde karakterleri kurtarır.

  • Sayısal nesnelerin eşitsizliğini test etmek için if(x-y), daha kısadır if(x!=y). Sıfır olmayan herhangi bir sayısal olarak kabul edilir TRUE. Eşitliği test ediyorsanız, bunun yerine if(x==y)a else bdeneyin if(x-y)b else a. Ayrıca önceki noktaya bakın.

  • İşlev ellisteden bir öğe çıkarmanız gerektiğinde kullanışlıdır. En yaygın örnek muhtemelen şudur strsplit: el(strsplit(x,""))ondan daha az bayt strsplit(x,"")[[1]].

  • (Kullanıldığı şekliyle burada ) Vektör uzantısı size karakterleri kaydedebilirsiniz: vektör eğer vuzunluğa sahiptir niçine atayabilirsiniz v[n+1]hatasız. Eğer ilk on faktöriyel'dir yazdırmak istiyorsa Örneğin, yapabileceği: v=1;for(i in 2:10)v[i]=v[i-1]*iyerine v=1:10:for(...)(her zaman olduğu gibi başka, daha iyi, yol var gerçi: cumprod(1:10))

  • Bazen, metne dayalı zorluklar (özellikle 2B olanlar için), plotmetinden ziyade metinden daha kolaydır cat. argüman pch=için plotkarakterler çizilir kontroller. Bu, pc=bir bayttan tasarruf etmek için (ayrıca bir uyarı verecektir) kısaltılabilir . Burada örnek .

  • Bir sayının tabanını almak için kullanmayın floor(x). Yerine x%/%1kullanın.

  • Sayısal veya tam sayı vektörünün öğelerinin hepsinin eşit olup olmadığını sınamak için, sdgibi ayrıntılı bir şey yerine sık sık kullanabilirsiniz all.equal. Tüm elemanlar aynı ise, standart sapmaları sıfırdır ( FALSE), aksi takdirde standart sapma pozitifdir ( TRUE). Burada örnek .

  • Tamsayı girişi gerektirmesini beklediğiniz bazı fonksiyonlar aslında değildir. Örneğin, seq(3.5)geri dönecektir 1 2 3( :operatör için aynı şey geçerlidir ). Bu, arama yapmaktan kaçınabilir floorve bazen /yerine kullanabileceğiniz anlamına gelir %/%.


1
tail(v,1)rev(v)[1]golf ipucu "dizinin son elemanı" ile aynı uzunluktadır .
Giuseppe,

read.csv(t="a,b,c",,F)daha kısa el(strsplit("a,b,c",",")).
J.Doe

18
  1. Yerleşik kötüye Tve F. Varsayılan olarak, onlar için değerlendirmek TRUEve FALSEotomatik numerics dönüştürülebilir hangi 1ve 0ve onlar iradesiyle yeniden tanımlanabilir. Bu, bir sayacı sıfırlamanız gerekmediği anlamına gelir (örn. i=0i=i+1), Sadece Tveya istediğiniz zaman kullanabilirsiniz F(ve doğrudan atla F=F+1).
  2. İşlevlerin, çağrılan son nesneyi döndürdüğünü ve açık bir return()aramaya gerek duymadıklarını unutmayın .
  3. Sık kullanılan fonksiyonlar için kısa takma adlar tanımlamak gibi harika p=paste. Eğer bir işlev çok ve birlikte kullanırsanız tam olarak iki argüman, bir olması mümkündür infixing takma bazı bayt kurtaracak. Takma takma adların etrafı sarılmalıdır %. Örneğin:

    `%p%`=paste

    Ve daha sonra x%p%y, 1 bayt daha kısa olan p(x,y). Takma takma ad tanımı, takma işleminden 4 byte daha uzundur p=paste, bu yüzden buna değdiğinden emin olmalısınız.


9
İlkel işlevleri kullanabilir ve birçok bayttan tasarruf edebilirsiniz:`+`=paste; x+y
Masclins

14

Kullanılması if, ifelseve`if`

R. if-ifadeleri yapmanın birkaç yolu vardır. Golf-optimal çözümler çok değişebilir.

Temeller

  1. ifkontrol akışı içindir. Vektörleştirilmez, yalnızca uzunluk 1'in koşullarını değerlendirebilir else. (İsteğe bağlı olarak) başka bir değer vermeyi gerektirir .
  2. ifelsebir fonksiyondur. Vektörleştirilir ve isteğe bağlı uzunluktaki değerleri döndürür. Üçüncü argümanı (başka bir değer) zorunludur. *
  3. `if`, aynı sözdizimine sahip bir işlevdir ifelse. Vektörleştirilmez, ya da zorunlu geri dönüş argümanlarından herhangi biri değildir.

* Teknik olarak zorunlu değildir; ifelse(TRUE,x)iyi çalışıyor, ancak üçüncü argüman boşsa ve koşul değerlendirirse hata verir FALSE. Bu nedenle, yalnızca koşulun her zaman olduğundan eminseniz TRUEve durum buysa, neden if-if ifadesiyle rahatsız ediyorsanız, kullanımı güvenlidir.

Örnekler

Bunların hepsi eşdeğerdir:

if(x)y else z # 13 bytes
ifelse(x,y,z) # 13 bytes
`if`(x,y,z)   # 11 bytes

elseDizeleri doğrudan kodda kullanıyorsanız, etrafındaki boşlukların gerekli olmadığını unutmayın:

if(x)"foo"else"bar"   # 19 bytes
ifelse(x,"foo","bar") # 21 bytes
`if`(x,"foo","bar")   # 19 bytes

Şimdiye kadar, `if`vectorized girdi almadığımız sürece kazanan görünüyor. Peki ya diğer durumu umursamadığımız durumlarda? Diyelim ki, eğer şart ise sadece bir kod çalıştırmak istiyoruz TRUE. Tek bir kod satırı için if, genellikle

if(x)z=f(y)         # 11 bytes
ifelse(x,z<-f(y),0) # 19 bytes
`if`(x,z<-f(y))     # 15 bytes

Birden fazla kod satırı ifiçin hala kazanan:

if(x){z=f(y);a=g(y)}        # 20 bytes
ifelse(x,{z=f(y);a=g(y)},0) # 27 bytes
`if`(x,{z=f(y);a=g(y)})     # 23 bytes

Orada biz olasılığı da var başka durumu hakkında bakım ve her isteğe bağlı kod çalıştırmak yerine bir değer döndürmek istediğiniz yere. Bu gibi durumlarda ifve `if`bayt sayısında eşdeğerdir.

if(x)a=b else z=b   # 17 bytes
ifelse(x,a<-b,z<-b) # 19 bytes
`if`(x,a<-b,z<-b)   # 17 bytes

if(x){z=y;a=b}else z=b   # 22 bytes
ifelse(x,{z=y;a=b},z<-b) # 24 bytes
`if`(x,{z=y;a=b},z<-b)   # 22 bytes

if(x)a=b else{z=b;a=y}   # 22 bytes
ifelse(x,a<-b,{z=b;a=y}) # 24 bytes
`if`(x,a<-b,{z=b;a=y})   # 22 bytes

if(x){z=y;a=b}else{z=b;a=y}   # 27 bytes
ifelse(x,{z=y;a=b},{z=b;a=y}) # 29 bytes
`if`(x,{z=y;a=b},{z=b;a=y})   # 27 bytes

özet

  1. ifelseUzunluk girişi> 1 olduğunda kullanın .

  2. Çok sayıda kod satırı çalıştırmak yerine basit bir değer döndürüyorsanız, `if`işlevi kullanmak muhtemelen bir tam if... elsedeyiminden daha kısadır .

  3. Sadece ne zaman tek bir değer istiyorsanız TRUE, kullanın if.

  4. Keyfi kod yürütmek için `if`ve ifgenellikle bayt sayısı açısından aynıdır; ifGenelde tavsiye ederim çünkü okuması daha kolay.


1
Güzel! Çok iyi karşılaştırmalar, +1!
Billywob 28:16,

13
  1. Aynı anda bir işleve argüman olarak sunarken mevcut ortama bir değişken atayabilirsiniz:

    sum(x <- 4, y <- 5)
    x
    y
  2. A'yı ayarlıyorsanız data.frameve koşulunuz sütunlarının birkaçına bağlıysa, (veya ) data.framekullanarak adı tekrar etmekten kaçınabilirsiniz .withsubset

    d <- data.frame(a=letters[1:3], b=1:3, c=4:6, e=7:9)
    with(d, d[a=='b' & b==2 & c==5 & e==8,])

    onun yerine

    d[d$a=='b' & d$b==2 & d$c==5 & d$e==8,]

    Tabii ki, bu yalnızca referanslarınızın data.frameuzunluğu uzunluğu aşarsa karakterleri kaydeder .with(,)

  3. if...elsebloklar, bloğun bir bölümünün yürüttüğü son ifadenin değerini döndürür. Örneğin, yerine

    a <- 3
    if (a==1) y<-1 else
    if (a==2) y<-2 else y<-3

    Yazabilirsin

    y <- if (a==1) 1 else 
         if (a==2) 2 else 3

4
Sadece (1) 'e dikkat etmek, bunu yaptığınız zaman adlandırılmış argümanlara göre değil sırayla geçirdiğinizdir. Eğer f <- function(a,b) cat(a,b)öyleyse f(a <- 'A', b <- 'B'), aynı değildir f(b <- 'B', a <- 'A').
Ari B. Friedman,

11

Örtük tür dönüşümü

Fonksiyonlar as.character, as.numericve as.logicalçok bayt ağır. Onları kısaltalım.

Sayısaldan mantıksal dönüşüm (4 bayt)

Diyelim ki xsayısal bir vektör. Mantıksal olmayan işleci kullanmak, !sayısal 0olanı FALSE, sıfır olan ve olmayan değerlerin olduğu mantıksal bir vektöre dolaylı olarak gösterir TRUE. !sonra bunu tersine çevirir.

x=!x

x=0:3;x=!xdöner TRUE FALSE FALSE FALSE.

Sayısal veya mantıksal karaktere dönüştürme (7 bayt)

Bu eğlenceli birşey. ( Bu tweetten .)

x[0]=''

R Eğer vektör güncellemekte olduğumuzu görür xile ''sınıfın olduğunu character. Böylece xsınıfa girer, characterböylece yeni veri noktasıyla uyumludur. Daha sonra, o koymak gider ''uygun yere ... ama endeks 0(bu hile de çalışır yok Inf, NaN, NA, NULL, vb). Sonuç olarak, xyalnızca sınıfta değiştirilir.

x=1:3;x[0]=''döner "1" "2" "3"ve x=c(TRUE,FALSE);x[0]=''döner "TRUE" "FALSE".

Çalışma alanınızda önceden tanımlanmış bir karakter nesnesiniz varsa, ''bayt kaydetmek yerine bunu kullanabilirsiniz . Eg x[0]=y!

Belirli koşullar altında sayısal veya mantıksal olarak karaktere dönüştürme (6 bayt)

J.Doe yorumlarda altı baytlık bir çözüme dikkat çekti:

c(x,"")

Bu x, eğer atom ise ve onu bir atom vektörü gerektiren bir işleve aktarmak istiyorsanız çalışır . (İşlev, argüman öğelerini yoksayma konusunda bir uyarı verebilir.)

Mantıksal (4 bayt) sayısal sayıya dönüştürme

Funky endeksleme numarasını yukarıdan kullanabilirsiniz (örneğin x[0]=3), ancak daha hızlı bir yol var:

x=+x

Pozitif operatör dolaylı olarak vektörü sayısal bir vektör olarak gösterir, öyle TRUE FALSEolur 1 0.


Sizin son numara olabilir x=+xtutmaya TRUEolarak 1.
Giuseppe

@Giuseppe Oh, elbette! Teşekkürler, şimdi güncellendi.
rturnbull

Sayısal veya mantıksaldan karaktere dönüştürme. Sen kullanabilirsiniz c(x,"")eğer xatomik olduğunu, daha sonra kullanacağız şartıyla xsadece (şikayet olabilir) ilk elemanın umursayan bir işlevde. Bu 1 byte daha ucuzdur x[0]="";.
J.Doe,

10

R-süre döngüleri

Bazen, kendimi R'nin bir do-whiledöngü kurmasını dileyerek buluyorum , çünkü:

 some_code
while(condition){
 some_code # repeated
}

çok uzun ve çok un-golfy. Bununla birlikte, bu davranışı geri kazanabilir ve {fonksiyonun gücüyle bazı baytları temizleyebiliriz .

{ve R (nin her bir .Primitivefonksiyonu.

Onlar için belgeler okuyor:

Etkili, (anlamsal olarak kimliğe eşdeğerdir function(x) x, oysa {biraz daha ilginçtir, örneklere bakınız.

ve Değer altında

Çünkü (, argümanı değerlendirmenin sonucu. Bu, görünürlük ayarına sahip olduğundan, en üst seviyede kullanılırsa otomatik olarak yazdırılır.

Çünkü {, son ifadenin sonucu değerlendirildi . Bu, son değerlendirmenin görünürlüğüne sahiptir.

(vurgu eklendi)

Peki, bu ne anlama geliyor? Bir do-while döngüsü olduğu kadar basit demektir

while({some_code;condition})0

ifadeleri içeride çünkü {}her biri değerlendirilir ve yalnızca sonuncusu tarafından döndürülür {değerlendirmemizi sağlayan some_codedöngü girmeden önce ve her zaman çalışır conditionolduğunu TRUE(ya truthy). Bu 0, whiledöngünün "gerçek" gövdesini oluşturan birçok 1 baytlık ifadeden biridir .


10
  1. outerİki listenin tüm kombinasyonlarına keyfi bir işlev uygulamak için kötüye . İlk argümanlar tarafından indekslenen i, j ile bir matris düşünün, ardından her bir çift için isteğe bağlı bir işlev (i, j) tanımlayabilirsiniz.

  2. İçin Mapkısayol olarak kullanın mapply. Benim iddiam, mapplydizine erişmeniz gereken durumlarda bir for döngüsünden daha ucuz olmasıdır. R.'deki liste yapısının kötüye unlistkullanılması pahalıdır. methods::elilk elementi ucuza listelemenizi sağlar. Liste destekli işlevleri yerel olarak kullanmaya çalışın.

  3. do.callİşlev çağrılarını isteğe bağlı girişlerle genelleştirmek için kullanın .

  4. Biriktirmek için args Reducekod golf için son derece yararlıdır.

  5. Konsol satırına satır satır yazmak cat(blah, "\n")daha ucuzdur write(blah, 1). Sert kodlu dizeler "\ n" ile bazı durumlarda daha ucuz olabilir.

  6. Bir işlev varsayılan argümanlarla gelirse, doğrudan n. Argümanı belirtmek için işlevi (,, n-arg) kullanabilirsiniz. Örnek: seq(1, 10, , 101)Bazı fonksiyonlarda kısmi değişken eşleştirmesi desteklenir. Örnek: seq(1, 10, l = 101).

  7. Dize manipülasyonuyla ilgili bir zorluk görürseniz, sadece geri düğmesine basın ve bir sonraki soruyu okuyun. strsplitR golfünü mahvetmekten tek başına sorumludur.

Şimdi 2018'den yeni keşfedilen bazı ipuçları için

  1. A[cbind(i,j)] = zmatrisleri işlemek için iyi bir yol olabilir. Bu işlem, i, j, zdoğru uzunluklara sahip vektörler olarak tasarladığınızı varsayarak çok bayt etkindir . Gerçek indeks / assign işlevini çağırarak daha da fazla tasarruf edebilirsiniz "[<-"(cbind(i,j), z). Bu şekilde arama, değiştirilen matrisi döndürür.

  2. \nSatır sonları yerine yeni bir satır kullanın .

  3. Satır sayıları aşağı sıkmak, bayt kazandırabilir. Satır içi atama lapply(A<-1:10,function(y) blah)ve işlev argümanları ataması function(X, U = X^2, V = X^3)bunu yapmanın yoludur.

  4. Öyleyse "[<-"R'de bir işlev var (ve benim SO hakkındaki eski sorumla ilgili )! Bu gibi işlemlerden sorumlu temel işlevdir x[1:5] = rnorm(5). Fonksiyonu isimle çağırmak için düzgün bir özellik, değiştirilmiş vektörü döndürmenizi sağlar. Sırayla kelimeler "[<-"(x, 1:5, normr(5)), yukarıdaki x ile hemen hemen aynı şeyi yapar, ancak değiştirilen x değerini döndürür. İlgili "uzunluk <-", "adlar <-", "her şey <-" hepsi döndürülen çıktıları döndürür


1
Bence "[<-"değiştirilmiş diziyi / matrisi / neyi geri getireceğinden kendi "İpuçları" yanıtına değdiğini düşünüyorum .
Giuseppe

10
  1. Satır içi değerleri kaydetme : Diğerleri, değerleri sıralı olarak iletebileceğinizi ve başka yerlerde kullanım için atayabileceğinizi belirtmişlerdir, örn.

    sum(x<- 1:10, y<- seq(10,1,2))

    Ancak, aynı satırda kullanmak için satır içi değerleri de kaydedebilirsiniz !

    Örneğin

    n=scan();(x=1:n)[abs(x-n/2)<4]

    okur stdin, bir değişken oluşturur x=1:n, sonra xbu değeri kullanarak dizin oluşturur x. Bu bazen baytları kurtarabilir.

  2. Boş vektörün diğer adı Her ikisi de geri döndüğünde {}boş vektör c()olarak kullanabilirsiniz NULL.

  3. Temel Dönüşümn 10 tabanındaki tamsayılar için kullanın n%/%10^(0:nchar(n))%%10. Bu izleyen bir sıfır bırakacaktır, bu nedenle, eğer sizin için önemliyse, n%/%10^(1:nchar(n)-1)%%10dizi indekslemeden daha kısa olduğu için kullanın . Bu floor(log(n,b))+1yerine kullanarak diğer bazlara adapte edilebilirnchar(n)

  4. Kullanılması seqve: : Aksine kullanmaktan daha 1:length(l)(ya da 1:sum(x|1)), kullanabilirsiniz seq(l)sürece lbir olduğu listveya vectorhiç o varsayılan olarak, 1'den büyük uzunlukta seq_along(l). Eğer lpotansiyel olarak uzunluk olabilir 1, seq(a=l)hile olacaktır.

    Ek olarak, :(bir uyarı ile) argümanlarının ilk öğesini kullanır.

  5. Niteliklerin kaldırılmasıc() Bir array(veya matrix) üzerinde kullanmak as.vector; genellikle isim dışı öznitelikleri kaldırır.

  6. Faktoring Kullanımı gamma(n+1), kullanmaktan daha kısadır factorial(n)ve yine de factorialolarak tanımlanır gamma(n+1).

  7. Bozuk Para Çevirme Rastgele bir iş yapması gerektiğinde, zamanın% 50'sini kullanmak üç kez bayttan rt(1,1)<0daha kısadır runif(1)<0.5.

  8. Öğeleri Çıkarma / Hariç Tutma head ve tailbir dizinin ilk / son birkaç elemanını çıkarmak için genellikle yararlıdır; head(x,-1)Son eleman hariç tümünü ayıklar ve uzunluğu zaten bilmiyorsanız, negatif indeksleme kullanmaktan daha kısadır:

    head(x,-1)
    x[-length(x)]
    x[-sum(x|1)]


@ J.Doe kendi görevine layık, sanırım! Belki de "alternatifler" unvanıyla rep. Diğer ipuçları soruları, her bir gönülden de bu soru için onayladığım cevap başına bir ipucu sınırlaması var! Ayrıca, iki bayttan 1:n*0daha kısadır Im(1:n), bu da ikinci numaranızın x+0*-n:n:-) olabileceği anlamına gelir.
Giuseppe

1
J. J. Doe Veya daha da iyisi, kullanım durumuna bağlı olarak !1:nbir dizi nsıfırdır; bunun için MATL / MATLAB ipucuları sorularına (muhtemelen Luis Mendo) dikkat edin.
Giuseppe

Teşekkürler, @Giuseppe! İyi düşüncelerinizden ün almak istemediğim için bu yazıyı oluşturmanızı önerebilir miyim?
J.Doe

@ J.Doe oh, umrumda değil. Diğer R golfçülerin daha fazla görünürlük kazanması her zaman iyidir; Bu noktada oldukça bilinen bir varlık olduğumu söylemenin adil olduğunu düşünüyorum. Etrafınızda oldukça etkileyici gelişmeler olduğunu öne sürüyorsunuz, bu yüzden rep'i alın (amaçlanmayan) ve iyi golf oynamaya devam edin :-)
Giuseppe

1
değil (log(i,b)%/%1):0)yerine floor(log(n,b))+1?
ASCII-sadece

8

Bazı temel kavramlar ancak biraz yararlı olması gerekir:

  1. Kontrol akışı ifadelerinde sıfıra eşit olmayan herhangi bir sayının TRUE, örneğin: if(x)eşdeğer olarak değerlendirileceğini kötüye kullanabilirsiniz if(x!=0). Tersine, if(!x)eşdeğerdir if(x==0).

  2. :(Örn. 1:5) Kullanarak diziler oluştururken ^, :üstelleştirici operatörünün, işlemciden (aksine +-*/) öncelikli olan tek operatör olduğu gerçeğini kötüye kullanabilir .

    1:2^2 => 1 2 3 4 

    bu, normalde kullanmak zorunda olduğunuz parantezlerde iki bayttan tasarruf etmenizi sağlar; örneğin, bir n x nmatrisin ( 1:n^2) veya üstel gösterimde ( 1:10^6) kullanarak daha kısa bir şekilde ifade edilebilecek diğer herhangi bir tamsayı üzerinde döngü yapmak istediğinizde kullanmak zorunda kalırsınız .

  3. Tabii ki +-*/, en yaygın şekilde uygulananlara rağmen , vektörlü işlemlerde de ilgili bir numara kullanılabilir +-:

    for(i in 1:(n+1)) can instead be written as for(i in 0:n+1)

    Bu işe +1yarar çünkü vektörleştirilir ve vektörde sonuçlanan 1her bir öğeye eklenir . Aynı şekilde size bir byte kazandırır.0:n1 2 ... n+10:(n+1) == -1:n+1

  4. Kısa işlevler yazarken (bir satırda ifade edilebilir), biri çevrelenen küme parantezlerine iki bayt kaydetmek için değişken atamasını kötüye kullanabilir {...}:

    f=function(n,l=length(n))for(i in 1:l)cat(i*l,"\n")
    f=function(n){l=length(n);for(i in 1:l)cat(i*l,"\n")}

    Bunun her zaman belirli zorlukların kurallarına uymayabileceğini unutmayın.


Sadece küçük bir düzeltme: ^vektörleştirilir, sadece önceliği vardır :(yani :parantezler tam tersini açıkça belirtmedikçe daha ?Syntaxönce gerçekleştirilir, ikili ve tekli operatörlerin öncelik sırasına bakın ). Aynısı , numaranız n ° 3'ten +-/*daha düşük önceliğe sahip olan ikili için de geçerlidir :.
plannapus

@plannapus Açıkladığınız için teşekkürler. İfadeler güncellendi.
Billywob

7

Operatörlerin anlamını değiştir

R operatörleri sadece ayrıştırıcı tarafından özel muamele gören fonksiyonlardır. Örneğin <, aslında iki değişkenli bir fonksiyondur. Bu iki kod satırı aynı şeyi yapar:

x < 3
`<`(x, 3) 

Bir işleve başka bir işlevi yeniden atayabilirsiniz ve çözümleyici, işleç önceliğine saygı duymak da dahil olmak üzere yine de bir şey yapar, ancak son işlev çağrısı orijinal yerine yenisi olacaktır. Örneğin:

`<`=rep

Şimdi bu iki kod satırı aynı şeyi yapıyor demektir:

rep("a", 3)
"a"<3

ve önceliğe saygı duyulur ve bunun gibi şeyler ortaya çıkar

"a"<3+2
#[1] "a" "a" "a" "a" "a"

Örneğin, bu cevaba ve ayrıca operatör önceliği sayfasına bakın . Yan etki olarak, kodunuz bir golf dilinde yazılan kadar şifreli olacaktır.

Bazı operatörler bir veya iki parametreyi beğenir +ve -kabul eder, böylece şöyle şeyler yapabilirsiniz:

`-`=sample
set.seed(1)
-5  # means sample(5)
#[1] 2 5 4 3 1
5-2 # means sample(5, 2)
#[1] 5 4

Örneğin, bu cevaba bakınız .

İki bayt, üç değişkenli işleç olarak kullanmak için bu cevaba da bakın [.


2
Bu, rturnbull'un ipuçlarına ilişkin bir yorum ancak bence buraya cevap verdiğimde "cevap başına bir ipucu" kuralı uygulamaya başlamamız gerektiğini düşünüyorum.
Giuseppe

1
ayrıca operatörlerin önceliğine bağlı olarak, yardımcı olabilecek bazı korkak şeyler yapabilirsiniz; gibi <daha düşük önceliğe sahip +, ancak potansiyel olarak bir araya zincir olabilir böylece *daha yüksek önceliğe sahiptir +!
Giuseppe

1
@Giuseppe, göndermeden önce ne bulmaya çalıştığımı biliyor ve bulamadınız. Gösterdiğin için teşekkürler. Bu numarayı daha fazla kullanmaya başladığımda, örneklerle operatör önceliği hakkında daha fazla ayrıntı eklemeyi planlıyorum.
JayCe

2
İşte eğlenceli bir tane: Bağlayıcı eğer ?hiç pasteveya iki argüman alabilir diğer bazı fonksiyon, öncelik sırası yine aracılığıyla satır içi atamaları kullanabileceğiniz anlamına gelir a<-b?d<-e.
J.Doe

1
[Üç elemanlı takma ad eklemelisiniz (bu iki bayt); Genellikle outerkullanmanız gerekmediğinden emin olmanız gerekmesine rağmen (ve bunu sürekli olarak unutun!) Gibi şeyler için yararlı buluyorum [. Ayrıca takma ad seçiminde yardımcı olması için operatör öncelik sayfasına bağlantı yapılması da yararlı olacaktır .
Giuseppe

5

Kaçınılabileceğiniz senaryolar paste(...,collapse="")vestrsplit

Bunlar normal ip mücadelelerinde bir acı. Bazı geçici çözümler var.

  • Reduce(paste0,letters) -5 bayt için paste0(letters,collapse="")

  • Bir 2 bayt golf iki vektörleri içeren bir liste var c(1,2,3)ve c(4,5,6)bir dizeye onlara öğeye göre bitiştirmek istiyorum "142536". Operatör kötüye kullanımı p=paste0;"^"=Reduce;p^p^r, normal paste0aramada iki bayttan tasarruf etmenizi sağlar .

  • paste0("(.{",n,"})")20 baytlık bir regex oluşturmak yerine (örneğin) bir regex'te bir regex düşünün: sub(0,"(.{0})",n)17 bayt için.

Bazen (oldukça sık, aslında) bir karakter veya karakter dizisi boyunca yinelemeniz veya bir kelimeyi harflere ayırmanız gerekir. Yaygın olarak kullanılan iki durum vardır: biri işlev veya programa girdi olarak bir karakter vektörü almanız ve biri vektörü önceden bildiğiniz ve kodunuzda bir yerde saklamanız gereken bir yer.

a. Girdi olarak bir dize almanız ve onu kelimelere veya karakterlere ayırmanız gereken yer .

  1. Kelimelere ihtiyacınız varsa (özel durum olarak karakterler dahil):

    • Eğer 0x10kelimeleri ayıran yeni bir satır (ASCII 16) tamam x=scan(,"")ise, kodunuzu girmek için tercih edilir function(s,x=el(strsplit(s," "))).

    • Kelimeler ile ayrılabilir Eğer başka boşluk yeni satırlar vb birden çok boşluk, tab dahil, sen NGM en @ kullanabilirsiniz çift tarama hile : x=scan(,"",t=scan(,"")). Bu, dizgede taranan parametreyi arg scanolarak verir textve onu boşlukla ayırır.

    • İkinci argüman scanherhangi bir dize olabilir, böylece bir tane yarattıysanız, bir bayttan tasarruf etmek için geri dönüştürebilirsiniz .

  2. Bir giriş dizesini bir karakter vektörüne dönüştürmeniz gerekirse :

    • x=el(strsplit(s,""))en kısa genel çözümdür. Bu splitargüman sıfır dahil herhangi bir şey üzerinde çalışır c(), {}vs.

    • Eğer ASCII karakter kodları ile çalışabilir varsa, düşünün utf8ToIntberi, utf8ToInt(x)daha kısadır strsplitçağrı. Onları tekrar yapıştırmak için intToutf8(utf8ToInt(x)), daha kısadır Reduce(paste0,el(strsplit(x,""))).

    • "31415926535"Girdi gibi sayıların rastgele dizelerini ayırmanız gerekirse , karakterler yerine tamsayı sayıları kullanabilmeniz koşuluyla, utf8ToInt(s)-483 bayttan tasarruf etmek için el(strsplit(s,""))kullanabilirsiniz. Bu aynı zamanda sayıları ondalık basamaklara bölmek için kullanılan normal tariflerden de daha kısadır.

b. Önceden kelimeler veya karakterlerden oluşan sabit bir vektöre ihtiyacınız olduğunda .

  • Kullandığınız bakmak, bazı düzenli desen varsa veya alfabetik sırayla olan tek bir karakter vektör gerekiyorsa intToUtf8veya chartrüzeri bir dizi uygulanan a:bveya harfler setleri yerleşik üzerinde lettersveya LETTERS. Yerleşik desen dili chartrolan özellikle güçlü .

  • İçin 1 ila 3 karakter ya da kelime , c("a","b","c")sadece genel kısa bir çözümdür.

  • Eğer sabit bir vektör gerekiyorsa 4 ila 10 sigara boşluk karakterleri veya kelimeleri kullanmak scanile stdinolarak filearg:

f(x=scan(,""))
q
w
e
r
t
y
u
  • Eğer scangelen stdiniçin, mümkün değildir 6 veya daha fazla sigara boşluk karakterleri veya kelimeleri kullanmak scanile textargüman scan(,"",t="a b c d e f").

  • Herhangi bir türden (a) 6 veya daha fazla karakterden oluşan bir vektöre veya (b) 10 veya daha fazla beyaz olmayan karaktere sahip bir vektöre ihtiyacınız varsa , strsplitüzerinden x=el(strsplit("qwertyuiop",""))gitmek muhtemelen yoludur.

  • Sen olabilir paçayı edebilmek aşağıdaki alıntı hile : quote(Q(W,E,R,T,Y)), bu ifadeyi yaratan. Bazı fonksiyonlar böyledir strrepve grepbunu bir dizi vektöre zorlar! Bunu yaparsanız, bu 3 ila 11 arasında herhangi bir kelime veya karakter vektörü uzunluğu için iyidir.

  • strsplitKelimeleri üzerinden kullanmak için iyi bir neden yoktur x=el(strsplit("q w e r t y"," ")). Her zaman scan(,"",t="q w e r t y"))sabit 5 baytlık bir yük ile kaybeder .

İşte her bir yaklaşımın uzunluğundaki tek karakterli bir vektörde okumak için kullanılan bayt sayımlarının bir tablosu n. Her satır içindeki nispi sipariş haricinde, karakterler veya kelimeler için geçerlidir strsplitüzerinde ""sadece karakterler üzerinde çalışır hangi.

| n  | c(...) | scan | scan | strsplit | quote |
|    |        |+stdin|+text | on ""    | hack  |
|    |        |      |      | CHAR ONLY|       |
|----|--------|------|------|----------|-------|
| 1  | 3      | 11   | 15   | 20       | 8     |
| 2  | 10     | 13   | 17   | 21       | 11    |
| 3  | 14     | 15   | 19   | 22       | 13    |
| 4  | 18     | 17   | 21   | 23       | 15    |
| 5  | 22     | 19   | 23   | 24       | 17    |
| 6  | 26     | 21   | 25   | 25       | 19    |
| 7  | 30     | 23   | 27   | 26       | 21    |
| 8  | 34     | 25   | 29   | 27       | 23    |
| 9  | 38     | 27   | 31   | 28       | 25    |
| 10 | 42     | 29   | 33   | 29       | 27    |
| 11 | 46     | 31   | 35   | 30       | 29    |
| 12 | 50     | 33   | 37   | 31       | 31    |

c. Metni karakter matrisi olarak girmeniz gerekiyorsa , kısa görünen birkaç tarif

s="hello\nworld\n foo"

# 43 bytes, returns "" padded data frame
# If lines > 5 are longer than lines <= 5, wraps around and causes error
read.csv(t=gsub("(?<=.)(?=.)",",",s,,T),,F)

# 54 bytes with readLines(), "" padded matrix
sapply(p<-readLines(),substring,p<-1:max(nchar(p)),p))

# plyr not available on TIO
# 58 bytes, returns NA padded matrix, all words split by whitespace
plyr::rbind.fill.matrix(Map(t,strsplit(scan(,"",t=s),"")))
# 61 bytes, returns NA padded matrix
plyr::rbind.fill.matrix(Map(t,(a=strsplit)(el(a(s,"\n")),"")))

1
scanSadece dizgelere ihtiyaç duyduğunuzdan textdaha rekabetçi olan bir argüman var el(strsplit(x," "))! Çevrimiçi deneyin! Son öneriniz aksine read.csv.
Giuseppe

Sadece karakterleri istiyorsanız, aramanız scan5 karaktere kadar el(strsplit(x,""))iyidir scan, 6 veya daha fazla karakterden daha rekabetçidir . Çevrimiçi deneyin! Henüz iyi bir kullanım alanı bulamadım read.csv, ancak bir nedenden dolayı bir veri tablosuna ihtiyaç duyduğunuzda faydalı olabilir mi?
J.Doe

Bir kullanım için hiçbir zaman bulamadım data.frameama belki de yararlı olacağı bir zorluk bulmalı / yaratmalıyız! Belki bir dplyrstil group_by()ve summarize()manipülasyon türü? BİLMİYORUM.
Giuseppe

Ve dizelerde okumak için scan(,"")hala daha iyi görünüyor? Çevrimiçi deneyin!
J.Doe

Evet, elbette, bir giriş biçimini kesinlikle ngm gibi yorumlarsanız, o zaman iki kat scandaha kullanışlıdır.
Giuseppe

4

Bir dizinin ilk sıfır olmayan öğesini bulmanın bazı yolları.

Eğer bir ismi varsa x:

x[!!x][1]

NASıfır olmayan öğeler yoksa (ne zaman xboş, ancak NULLhangi hataları içermezse) döndürür .

anonim:

Find(c, c(0,0,0,1:3))

NULLSıfır olmayan öğeler yoksa veya boşsa döndürür NULL.


NATüm öğeler xsıfır ise bu geri döner , inanıyorum, bu yüzden dikkatli kullanın!
Giuseppe

Find(c,x)aynı bytecount: x'i tekrarlamanız gerekmeyen avantaj (x) ve eşleşme yoksa farklı davranış. TIO
JayCe

Findaynı zamanda üzerinde çalıştığı NULLsürece, sonuç için başka hiçbir şey olması gerekmediği sürece, daha güvenlidir , bu durumda geri dönüp dönmediğinden NAveya NULLdaha güvenli olduğundan emin değilim .
NGM

Evet bu doğru. NULL döndürme ile ilgili sorun hatalar ... sürüm karşılaştırma sorusundaki ilk sign(Find(c,w))hatalara neden olan denedim - Find(c,sign(w))hatayı değil almak için yapmak zorunda kaldı . Her iki yolun da kullanımları olduğunu düşünüyorum.
JayCe,

4

Alternatifleri rep()

Bazen rep()kolon operatörü :ve R'nin vektör geri dönüşümü ile önlenebilir .

  • Tekrar için n, sıfır burada n>0, 0*1:ndaha 3 bayt kısadır rep(0,n)ve !1:nbir dizi FALSEkullanım durumunda izin veriyorsa, 4 bayt daha kısadır.

  • x nZamanları tekrarlamak için x+!1:n, 2 bayttan daha kısa rep(x,n). İçin nolanlar, kullanmak !!1:nsize bir dizi kullanabilirsiniz eğer TRUE.

  • x 2n+1Zamanları tekrarlamak için , nerede n>=0, x+0*-n:n4 bayttan daha kısa rep(x,2*n+1).

  • Açıklamada her iki tarafa !-n:nda TRUEkuşatılmış olarak verilecek n FALSE. Bu kullanılabilir üretmek için bile çağrıları karakter numaralarını intToUtf8()Bir sıfır ihmal olduğunu hatırlarsanız.

Modüler aritmetik yararlı olabilir. argüman repiçeren ifadeler eachbazen tamsayı bölme kullanılarak da önlenebilir.

  • Vektörü oluşturmak için c(-1,-1,-1,0,0,0,1,1,1), -3:5%/%35'ten bayt kısadır rep(-1:1,e=3).

  • Vektörü oluşturmak için c(0,1,2,0,1,2,0,1,2), 0:8%%34 bayttan tasarruf sağlar rep(0:2,3).

  • Bazen doğrusal olmayan dönüşümler dizi aritmetiğini kısaltabilir. Bileşik bir ifadenin içinde eşlemek i in 1:15için c(1,1,3,1,1,3,1,1,3,1,1,3,1,1,3), açık golf sahası cevabı 1+2*(!i%%3)11 bayt. Ancak, 3/(i%%3+1)bir 10 bayt aynı dizisine ve irade zemin, böylece dizi indeksleme için diziyi gerekiyorsa o kullanılabilir.


3

Bir işlev kullanmanız gerektiğinde, pryr::f()yerine kullanın function().

Örnek:

function(x,y){x+y}

eşittir

pryr::f(x,y,x+y)

veya daha da iyisi,

pryr::f(x+y)

Çünkü Eğer sadece bir argüman varsa, formaliteler koddan tahmin edilir .


Eğer (üçüncü örnekte olduğu gibi) bir argüman için aşağı edemeyip, bunun için, bir golf değil function(x,y){x+y}olarak yazılabilir function(x,y)x+yaynı ByteCount için pryr::f(x,y,x+y)ama daha okunabilirlikle.
Khuldraeseth na'Barya

3

Dizeleri içeren zorluklardan kurtulmak

Başka bir cevapta belirtildiği gibi unlist(strsplit(x,split="")ve paste(...,collapse="")iç karartıcı olabilir. Ancak bunlardan uzaklaşmayın, geçici çözümler var!

  • utf8ToIntbir dizgiyi vektöre dönüştürür intToUtf8, ters işlemi yapar. Bir vektörünü alıyorsunuz, bir vektörünü intdeğil, charbazen aradığınız şey budur. Örnek bir listesini oluşturmak için için -, daha iyi şekilde intToUtf8(rep(45,34))dahapaste(rep("-",34),collapse="")
  • gsubgrepTek bir dizide çalışırken ailenin diğer işlevlerinden daha faydalıdır . Yukarıdaki iki yaklaşım , ovs , Giuseppe ve ngm'in tavsiyesinden yararlanan bu cevaptaki gibi birleştirilebilir .
  • Metin satırları (tırnak işaretleri olmadan) giriş alarak bu cevapta olduğu gibi uygun bir I / O formatı seçin ya da bir karakter vektörü alarak bunu seçin . Şüphe duyduğunuzda OP ile kontrol edin.
  • Yorumlarda da belirtildiği <gibi, karakterleri beklendiği gibi sözlüksel olarak karşılaştırır.

intToUtf8ayrıca, eğer ayarlanacaksa tek bir dize yerine s'den bireysel karakterlere (uzunluk-bir tane dizge) multiple = FALSEdönüşecek ikinci bir argümana sahiptir . intTRUE
Giuseppe

Ayrıca, 3.5.0'dan başlayarak üçüncü bir argüman var allow_surrogate_pairs = FALSE, ama ne yaptığını bilmiyorum; Doktorlar iki bayt okumakla ilgili bir şey söylüyorlar, UTF-16ancak ne UTF-8olduğunu çok az biliyorum , bu yüzden başka biri onunla golf oynayacak bir yol bulana kadar görmezden geleceğim.
Giuseppe

2

Kısıtlı kaynak zorlukları için ipuçları:

  1. R değişmezlerinin sabitlerindeki karakterler, altıgen kodlar, sekizli kodlar ve unicodes ile değiştirilebilir.

    örneğin, dize "abcd"yazılabilir:

        # in octal codes
        "\141\142\143\144"
    
        # in hex codes
        "\x61\x62\x63\x64"
    
        # in unicodes
         "\u61\u62\u63\u64"
        # or
        "\U61\U62\U63\U64" 

    Unicode karakterler octal / hex ile karıştırılmadığı sürece, karakterleri octal / hex / unicode ile karıştırıp bazı oct kodlarını ve bazı hex kodlarını birlikte kullanabiliriz.

        # Valid
        "a\142\x63\x64"
    
        # Valid
        "ab\u63\U64"
    
        # Error: mixing Unicode and octal/hex escapes in a string is not allowed
        "\141\142\x63\u64"

    Daha fazla bilgi için bu bölümün sonuna bakın.

  2. İşlevler string değişmezleri kullanılarak yazılabildiğinden, örneğin cat()alternatif olarak yazılabilir:

    'cat'()
    "cat"()
    `cat`()

    fonksiyon isimleri için sekizli kodları, altıgen kodları ve unicode'u kullanabiliriz:

    # all equal to cat()
    "\143\141\164"()
    `\x63\x61\x74`()
    '\u63\u61\u74'()
    "ca\u74"()

    unicode dizilerinin backticks içinde desteklenmemesi istisnasıyla

  3. Operatörlere suistimal eden yuvarlak parantezler önlenebilir, örneğin:

    cat('hello')
    
    # can be written as
    `+`=cat;+'hello'

Bu üç hilenin bir uygulaması bu cevapta bulunabilir .


Ayrıca, sayılar onaltılık olarak yazılabilir: 0xBve 0xbgeri dön 11( geri dönüşlere veya tırnaklara gerek yok).
Robin Ryder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.