R에서의 골프 팁


58

R 통계 언어로 골프 팁을 찾고 있습니다. R은 아마도 골프를위한 비 전통적인 선택 일 것입니다. 그러나 특정 작업 (시퀀스, 임의성, 벡터 및 목록)을 매우 콤팩트하게 만들고 많은 내장 함수는 매우 짧은 이름 을 가지며 선택적 줄 종결 자 (;)가 있습니다. R의 코드 골프 문제를 해결하는 데 도움이되는 팁과 요령은 무엇입니까?


14
이 질문에 대한 답변은 코드 골프가 실제로 이러한 일을 많이 해야하는 유일한 시간이라는 점을 감안할 때 R의 스타일 가이드로 두 배가 될 수 있습니다 :-)
Andrew Brēza

답변:


44

몇 가지 팁 :

  1. R에서는 <-over 를 사용 하는 것이 좋습니다 =. 골프의 경우 반대 입장 =이 짧아 지기 때문에 ...
  2. 함수를 두 번 이상 호출하면 짧은 별칭을 정의하는 것이 유리합니다.

    as.numeric(x)+as.numeric(y)
    
    a=as.numeric;a(x)+a(y)
    
  3. 부분 매칭은 친구가 될 수 있습니다. 특히 함수가 하나의 항목 만 필요한 목록을 반환 할 때 더욱 그렇습니다. 비교 rle(x)$lengthsrle(x)$l

  4. 많은 과제는 입력을 읽어야합니다. scan사용자는 종종 빈 줄을 입력하여 입력을 종료합니다.

    scan()    # reads numbers into a vector
    scan(,'') # reads strings into a vector
    
  5. 강제가 유용 할 수 있습니다. t=1보다 훨씬 짧습니다 t=TRUE. 또는 switch귀중한 문자를 저장할 수도 있지만 0,1 대신 1,2를 사용하는 것이 좋습니다.

    if(length(x)) {} # TRUE if length != 0
    sum(x<3)         # Adds all the TRUE:s (count TRUE)
    
  6. 함수가 복잡한 것을 계산하고 동일한 핵심 값을 기반으로 다양한 다른 유형의 계산이 필요한 경우 a) 작은 함수로 나누고 b) 필요한 모든 결과를 목록으로 반환하거나 c) 함수에 대한 인수에 따라 다른 유형의 값을 반환합니다.

  7. 다른 언어와 마찬가지로 잘 알고 있습니다. R에는 수천 개의 기능이 있으며, 문자가 거의없는 경우 문제를 해결할 수있는 기능이있을 수 있습니다.

애매하지만 유용한 기능들 :

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

일부 내장 데이터 세트 및 기호 :

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. 로 패키지를 가져 오는 대신을 library사용하여 패키지에서 변수를 가져옵니다 ::. 다음을 비교하십시오.

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

    물론 패키지에서 하나의 단일 기능을 사용하는 경우에만 유효합니다.

  2. 이것은 사소한이지만 기능을 앨리어싱의 토미의 트릭 @ 사용하는 경우에 대한 엄지 손가락의 규칙 : 함수 이름의 길이가있는 경우 m와 사용 n후 경우에만 별칭 시간을 m*n > m+n+3지출 별칭을 정의 할 때 (때문에 m+3다음 당신은 여전히 지출 별명이 사용될 때마다 1). 예를 들면 :

    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. 기능의 부작용으로 강요 :

    • 를 사용하는 대신 다음을 사용하여 as.integer문자열을 정수로 강제 변환 할 수 있습니다 :.

      as.integer("19")
      ("19":1)[1] #Shorter version using force coercion.
      
    • 정수, 숫자 등은 다음 paste대신에 문자를 사용하여 비슷하게 강제 할 수 있습니다 as.character.

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

6
다시 : 세 번째 팁 el("19":1)은 1 바이트만큼 짧습니다.
JayCe

19

매우 구체적인 골프 팁 :

  • 당신이 벡터의 길이를 추출해야하는 경우, sum(x|1)보다 짧은 length(x)만큼 x, 숫자의 정수, 복잡하거나 논리적이다.
  • 당신이 벡터의 마지막 요소를 추출해야하는 경우, 그것은 (가능하다면) 벡터 초기화하는 저렴 수 있습니다 뒤쪽으로 사용 rev()하고 호출 x[1]보다는 x[length(x)](또는 위의 팁을 사용 x[sum(x|1)]) (또는 tail(x,1)--- 감사 주세페을!). 이것에 대한 약간의 변형 (두 번째 마지막 요소가 필요한 곳)이 여기에서 볼 수 있습니다 . 벡터를 뒤로 초기화 할 수 없더라도 rev(x)[1]여전히 짧습니다 x[sum(x|1)](및 문자 벡터에서도 작동합니다). 때로는 대신을 rev사용 n:1하는 등의 필요조차 없습니다 1:n.
  • ( 여기에서 볼 수 있듯이 ). 데이터 프레임을 행렬로 강제 변환하려면을 사용하지 마십시오 as.matrix(x). 조옮김의 조옮김 t(t(x)).

  • if공식적인 기능입니다. 예를 들어, "if"(x<y,2,3)보다 짧습니다 if(x<y)2 else 3(물론 3-(x<y)둘 중 하나보다 짧습니다). 이런 식으로 공식화하기 위해 추가 괄호 쌍이 필요하지 않은 경우에만 문자를 저장합니다.

  • 동일하지 않은 숫자 객체를 테스트하는 if(x-y)경우보다 짧습니다 if(x!=y). 0이 아닌 숫자는로 간주됩니다 TRUE. 평등을 테스트하는 경우 대신 if(x==y)a else b시도하십시오 if(x-y)b else a. 또한 이전 요점을 참조하십시오.

  • 이 기능 el은 목록에서 항목을 추출해야 할 때 유용합니다. 가장 일반적인 예는 아마도 strsplit: el(strsplit(x,""))이상 적은 바이트입니다 strsplit(x,"")[[1]].

  • ( 여기에 사용됨 ) 벡터 확장은 문자를 절약 할 수 있습니다. 벡터 v길이가 길면 오류없이 n할당 할 수 있습니다 v[n+1]. 첫 번째 열 계승을 인쇄하고 싶었 예를 들어, 당신은 할 수 : v=1;for(i in 2:10)v[i]=v[i-1]*i보다는 v=1:10:for(...)(언제나처럼, 또 다른, 더 나은 방법이 있지만 : cumprod(1:10))

  • 때로는 텍스트 기반 문제 (특히 2D 문제)의 plot경우 텍스트보다 텍스트가 더 쉽습니다 cat. 어떤 문자가 그려 지는지 pch=plot제어하는 인수 . 이것은 pc=바이트를 절약하기 위해 단축 될 수 있습니다 (경고를 줄 것입니다). 여기에 예가 있습니다 .

  • 숫자를 바닥에 쓰려면을 사용하지 마십시오 floor(x). x%/%1대신 사용하십시오 .

  • 숫자 형 벡터 나 정수형 벡터의 요소가 모두 같은지 테스트하려면 sd과 같은 장황한 것이 아니라 종종 사용할 수 있습니다 all.equal. 모든 요소가 동일하면 표준 편차가 0 ( FALSE) 이고 표준 편차가 양수 ( TRUE)입니다. 여기에 예가 있습니다 .

  • 정수 입력이 필요한 일부 함수는 실제로 필요하지 않습니다. 예를 들어, seq(3.5)리턴합니다 1 2 3( :연산자도 마찬가지입니다 ). 이것은 호출을 피할 수 floor있으며 때로는 /대신 대신 사용할 수 있음을 의미합니다 %/%.


1
tail(v,1)rev(v)[1]"어레이의 마지막 요소"골프 팁과 동일한 길이 입니다.
주세페

read.csv(t="a,b,c",,F)보다 짧습니다 el(strsplit("a,b,c",",")).
J.Doe

18
  1. 내장 매크로를 남용 T하고 F. 기본적으로 TRUEand로 평가되며 FALSE숫자 1및 로 자동 변환 0될 수 있으며 마음대로 재정의 할 수 있습니다. 즉, 카운터를 초기화 할 필요가 없습니다 (예 : i=0... i=i+1) T또는 F필요에 따라 사용 하거나 F=F+1나중에 바로 이동할 수 있습니다 .
  2. 함수는 마지막으로 return()호출 된 객체를 반환하므로 명시적인 호출이 필요하지 않습니다 .
  3. 일반적으로 사용되는 함수에 대한 짧은 별칭을 정의하는 것이 좋습니다 p=paste. 함수를 많이 사용하고 정확히 두 개의 인수 를 사용하면 별칭이 붙으면 일부 바이트가 절약 될 수 있습니다. 삽입 별명은로 묶어야합니다 %. 예를 들면 다음과 같습니다.

    `%p%`=paste

    그리고 이후 x%p%y1 바이트보다 짧은 것 p(x,y)입니다. 그래도 별명 정의는 비고 정보 다 4 바이트 더 길 p=paste므로 그 가치가 있는지 확인해야합니다.


9
원시 함수를 사용할 수 있고 많은 바이트를 절약 할 수 있습니다.`+`=paste; x+y
Masclins

14

사용 if, ifelse`if`

R.에서 if-statement를 수행하는 방법은 여러 가지가 있습니다. 골프 최적화 솔루션은 크게 다를 수 있습니다.

기본

  1. if제어 흐름입니다. 벡터화되지 않습니다. 즉, 길이 1의 조건 만 평가할 수 있습니다. else선택적으로 else 값을 반환해야합니다.
  2. ifelse함수입니다. 벡터화되어 임의 길이의 값을 반환 할 수 있습니다. 세 번째 주장 (다른 가치)은 의무적입니다. *
  3. `if`와 구문이 동일한 함수 ifelse입니다. 벡터화되지 않았으며 반환 인수가 의무화되지 않았습니다.

* 기술적으로 의무적 인 것은 아닙니다. ifelse(TRUE,x)잘 작동하지만 세 번째 인수가 비어 있고 조건이로 평가되면 오류가 발생합니다 FALSE. 따라서 조건이 항상 확실하다고 확신하는 경우에만 사용하는 것이 안전하며 TRUE, 그러한 경우 if 문으로 귀찮게하는 이유는 무엇입니까?

이들은 모두 동등합니다 :

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

else코드에서 직접 문자열을 사용하는 경우 공백 이 필요하지 않습니다.

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

지금까지 `if`입력을 벡터화하지 않은 한 승자가 될 것으로 보입니다. 그러나 else 조건에 관심이없는 경우는 어떻습니까? 조건이 인 경우 일부 코드 만 실행하려고한다고 가정 해보십시오 TRUE. 한 줄의 코드만으로도 if가장 좋습니다.

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

여러 줄의 코드에서 if여전히 승자가됩니다.

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

이 우리가 가능성이기도 않는 다른 조건에 대한 관심은, 우리는 임의의 코드를 실행하는 대신 값을 반환 할 위치. 이러한 경우, if`if`바이트 수에 동일합니다.

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

요약

  1. ifelse길이가 1보다 큰 입력을 사용하는 경우 사용하십시오 .

  2. 많은 코드 줄을 실행하지 않고 간단한 값을 반환하는 경우 `if`함수를 사용하는 것이 full if... else문 보다 짧을 수 있습니다.

  3. 언제 단일 값을 원하면을 TRUE사용하십시오 if.

  4. 임의의 코드를 실행 `if`하고 if일반적 바이트 개수의 관점에서 동일하다; 나는 if주로 읽기 쉽기 때문에 추천 합니다.


1
좋은! 아주 좋은 비교, +1!
Billywob

13
  1. 변수를 함수에 대한 인수로 제공하면서 동시에 현재 환경에 변수를 지정할 수 있습니다.

    sum(x <- 4, y <- 5)
    x
    y
  2. a를 부분 집합하고 data.frame조건이 여러 열에 의존 data.frame하는 경우 with(또는 subset) 를 사용하여 이름을 반복하지 않아도 됩니다.

    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,])

    대신에

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

    물론 이것은 참조 data.frame길이가 길이를 초과하는 경우에만 문자를 저장합니다.with(,)

  3. if...else블록은 블록의 일부가 실행되는 최종 명령문의 값을 리턴 할 수 있습니다. 예를 들어

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

    당신은 쓸 수 있습니다

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

4
(1)에 대해서만주의 할 때 명명 된 인수가 아닌 순서대로 전달한다는 것입니다. 경우 f <- function(a,b) cat(a,b), 다음 f(a <- 'A', b <- 'B')과 동일하지 않습니다 f(b <- 'B', a <- 'A').
Ari B. Friedman

11

암시 적 유형 변환

기능은 as.character, as.numeric그리고 as.logical너무 바이트 무겁습니다. 그것들을 다듬어 봅시다.

숫자 (4 바이트)에서 논리로 변환

x숫자 형 벡터 라고 가정하십시오 . 논리 not 연산자를 사용하면 !숫자가 암시 적으로 논리 벡터로 다시 캐스팅 됩니다 . 여기서 0is FALSE및 0이 아닌 값은 TRUE입니다. !그런 다음 반전시킵니다.

x=!x

x=0:3;x=!x을 반환합니다 TRUE FALSE FALSE FALSE.

숫자 또는 논리 (7 바이트)에서 문자로 변환

이것은 재미있는 것입니다. ( 이 트윗 에서)

x[0]=''

R 은 클래스 의 인 벡터 x를 업데이트하고 있음을 알았습니다 . 따라서 새로운 데이터 포인트와 호환되도록 클래스에 캐스트 됩니다 . 다음으로, 넣어 간다 적절한 장소에 ...하지만 인덱스 (이 트릭도 함께 작동 존재하지 않는 , , , , 등). 결과적으로 클래스에서만 수정됩니다.''characterxcharacter''0InfNaNNANULLx

x=1:3;x[0]=''반환 "1" "2" "3"x=c(TRUE,FALSE);x[0]=''반환 "TRUE" "FALSE".

작업 공간에 이미 문자 오브젝트가 정의되어 있으면 ''바이트를 저장하는 대신이 를 사용할 수 있습니다 . 예 x[0]=y!

특정 조건 (6 바이트)에서 숫자 또는 논리 문자로 변환

J.Doe 는 주석에서 6 바이트 솔루션을 지적했습니다.

c(x,"")

x원자 인 경우 원자 벡터가 필요한 함수에 전달하려는 경우 작동합니다 . 이 함수는 인수의 요소를 무시하는 것에 대한 경고를 표시 할 수 있습니다.

논리 (4 바이트)에서 숫자로 변환

위에서 펑키 인덱싱 트릭을 사용할 수 x[0]=3있지만 실제로 더 빠른 방법이 있습니다.

x=+x

양의 연산자는 암시 적으로 벡터를 숫자 벡터로 다시 캐스팅하므로이 TRUE FALSE됩니다 1 0.


마지막 트릭은 x=+x로 유지 TRUE하는 것 1입니다.
Giuseppe

물론 @ 주세페! 감사합니다. 지금 업데이트하십시오.
rturnbull

숫자 또는 논리에서 문자로 변환 당신이 사용할 수있는 c(x,"")경우 x원자, 당신은 다음 사용하는 거라고 제공하는 x전용 (이 불평 수) 첫 번째 요소에 대한 관심 함수에서. 보다 1 바이트 저렴합니다 x[0]="";.
J.Doe

10

R의 do-while 루프

때때로, 나는 R이 do-while루프를 갖기를 바랐다.

 some_code
while(condition){
 some_code # repeated
}

너무 길고 골치 거리가 아닙니다. 그러나이 동작을 복구하고 {함수 의 힘으로 일부 바이트를 줄일 수 있습니다 .

{그리고 (각각 .PrimitiveR. 함수

그들에 대한 문서는 다음과 같습니다.

사실상 (신원과 의미 적으로 동일 function(x) x하지만 {약간 더 흥미 롭습니다 (예 참조).

그리고 가치 아래

의 경우 (인수를 평가 한 결과입니다. 가시성이 설정되어 있으므로 최상위 수준에서 사용하면 자동 인쇄됩니다.

에 대해 {, 마지막 표현식의 결과가 평가되었습니다 . 이것은 마지막 평가의 가시성을 가지고 있습니다.

(강조 추가)

이것이 무엇을 의미합니까? 그것은 do-while 루프가 간단하다는 것을 의미합니다.

while({some_code;condition})0

표현식 안에 있기 때문에 {}각각 평가, 만 마지막은 에 의해 반환 {우리가 평가할 수 있도록 some_code루프에 들어가기 전에, 그것은 때마다 실행 condition이다 TRUE(또는 truthy을). 이것은 루프 0의 "실제"본문을 형성하는 많은 1 바이트 표현식 중 하나입니다 while.


10
  1. outer두 목록의 모든 조합에 임의의 기능을 적용하기위한 남용 . 첫 번째 인수로 인덱스 된 i, j를 갖는 행렬을 상상 한 다음 각 쌍에 대해 임의의 함수 (i, j)를 정의 할 수 있습니다.

  2. Map대한 바로 가기로 사용하십시오 mapply. 내 주장은 mapply인덱스에 액세스 해야하는 상황에서 for 루프보다 저렴하다는 것입니다. R의 목록 구조를 남용하는 unlist것은 비싸다. methods::el첫 번째 요소를 저렴하게 나열 해제 할 수 있습니다. 기본적으로 목록 지원 기능을 사용하십시오.

  3. do.call임의의 입력으로 함수 호출을 일반화하는 데 사용 합니다.

  4. 에 대한 누적 인수 Reduce는 코드 골프에 매우 도움이됩니다.

  5. 로 줄 단위로 콘솔에 쓰는 cat(blah, "\n")것이보다 저렴합니다 write(blah, 1). 경우에 따라 "\ n"으로 하드 코딩 된 문자열이 더 저렴할 수 있습니다.

  6. 함수에 기본 인수가있는 경우 function (,, n-arg)을 사용하여 n 번째 인수를 직접 지정할 수 있습니다. 예 : seq(1, 10, , 101)일부 함수에서는 부분 인수 일치가 지원됩니다. 예 : seq(1, 10, l = 101).

  7. 문자열 조작과 관련된 문제가있는 경우 뒤로 버튼을 누르고 다음 질문을 읽으십시오. strsplitR 골프를 망치는 것은 하나의 책임입니다.

이제 2018에서 새로 발견 된 팁

  1. A[cbind(i,j)] = z행렬을 조작하는 좋은 방법이 될 수 있습니다. 이 연산은 i, j, z올바른 길이의 벡터로 디자인한다고 가정하면 매우 바이트 효율적 입니다. 실제 인덱스 / 할당 기능을 호출하여 더 많은 비용을 절약 할 수 있습니다 "[<-"(cbind(i,j), z). 이 호출 방식은 수정 된 행렬을 반환합니다.

  2. \n줄 바꿈 대신 새 줄을 사용하십시오 .

  3. 줄 카운트를 줄이면 바이트를 절약 할 수 있습니다. 인라인 할당 lapply(A<-1:10,function(y) blah)및 함수 인수 할당 function(X, U = X^2, V = X^3)이이를 수행하는 방법입니다.

  4. 그래서 "[<-"R의 함수이다 (그리고 관련이 SO에 내 고대 질문 )! 이는 다음과 같은 작업을 담당하는 기본 기능 x[1:5] = rnorm(5)입니다. 이름으로 함수를 호출하는 깔끔한 속성을 사용하면 수정 된 벡터를 반환 할 수 있습니다. 순서대로 단어 "[<-"(x, 1:5, normr(5))는 수정 된 x를 반환한다는 점을 제외하고 위 코드와 거의 동일합니다. 관련된 "length <-", "names <-", "anything <-"은 모두 수정 된 출력을 리턴합니다.


1
나는 "[<-"수정 된 배열 / 행렬 / 무엇이든 반환 할 것이기 때문에 사용하는 것이 "팁"응답에 합당 하다고 생각 합니다.
Giuseppe

10
  1. 인라인으로 값 저장 : 다른 사람들은 순서대로 값을 전달하고 다른 곳에서 사용하기 위해 값을 할당 할 수 있다고 언급했습니다.

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

    그러나 같은 줄에서 사용하기 위해 값을 인라인으로 저장할 수도 있습니다 !

    예를 들어

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

    에서 읽고 stdin변수 x=1:n를 만든 다음 x해당 값 을 사용하여 색인을 생성합니다 x. 때때로 바이트를 절약 할 수 있습니다.

  2. 빈 벡터의 별칭 모두 반환 {}되는 빈 벡터 c()로 사용할 수 있습니다 NULL.

  3. 기본 변환n 밑이 10 인 정수는 을 사용 n%/%10^(0:nchar(n))%%10합니다. 이것은 후행 0을 남기므로 중요한 n%/%10^(1:nchar(n)-1)%%10경우 배열 인덱싱보다 짧으므로 사용 하십시오. floor(log(n,b))+1대신에 다른베이스에 적용 할 수 있습니다.nchar(n)

  4. 사용 seq: : 대신 사용하는 것보다 1:length(l)(또는 1:sum(x|1)), 당신이 사용할 수있는 seq(l)만큼 lA는 listvector에 대한 기본값으로, 1보다 큰 길이 seq_along(l). l잠재적으로 length 1일 수 있으면 seq(a=l)트릭을 수행합니다.

    또한 :will은 (경고와 함께) 인수의 첫 번째 요소를 사용합니다.

  5. 속성 제거 하여 c()array(또는 matrix과 동일 할 것) as.vector; 일반적으로 이름이 아닌 속성을 제거합니다.

  6. 계승 사용은 gamma(n+1)사용하는 것보다 짧은 factorial(n)factorial같이 정의된다 gamma(n+1)어쨌든.

  7. 동전 뒤집기 50 %의 임의 작업을 수행해야 할 경우 사용 시간 이 3 바이트 rt(1,1)<0보다 짧습니다 runif(1)<0.5.

  8. 요소를 제외 / 추출 headtail종종 어레이의 제 / 최후의 요소를 추출하는 것이 유용하다; head(x,-1)길이를 모르는 경우 마지막 요소를 제외한 모든 요소를 ​​추출하고 음수 색인을 사용하는 것보다 짧습니다.

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


@ J.Doe 자체 게시물 가치가 있다고 생각합니다! 아마도 "대체"라는 제목이있을 것입니다 rep. 다른 팁 질문에는 답변 당 하나의 팁 제한이 있으며,이 질문에 대해서도 진심으로 보증합니다! 또한 2 바이트 1:n*0보다 짧으므로 Im(1:n)두 번째 트릭도 가능합니다 x+0*-n:n:-)
Giuseppe

1
@ J.Doe 또는 더 나은 !1:n것은 n사용 사례에 따라 0 의 배열입니다 . 그래도 MATL / MATLAB 팁 질문 (아마도 Luis Mendo)에게 감사드립니다.
주세페

감사합니다, @Giuseppe! 좋은 아이디어로 명성을 얻고 싶지 않기 때문에이 게시물을 만들 것을 제안 할 수 있습니까?
J.Doe

@ J.Doe 오, 상관 없어요. 항상 다른 R 골퍼들에게 더 많은 가시성을 확보하는 것이 좋습니다. 나는이 시점에서 내가 꽤 알려진 존재라고 말하는 것이 공평하다고 생각합니다! 당신은 상당히 인상적인 개선을 제안하는 주위에 있었으므로, 담당자를 가져 와서 좋은 골프를 계속하십시오 :-)
Giuseppe

1
하지 (log(i,b)%/%1):0)않고 floor(log(n,b))+1?
ASCII 전용

8

몇 가지 기본 개념이지만 다소 유용해야합니다.

  1. 제어 흐름 설명에서 0이 아닌 숫자는 다음과 같이 평가됨을 남용 할 수 있습니다 . TRUE예를 들면 다음 if(x)과 같습니다 if(x!=0). 반대로, if(!x)와 같습니다 if(x==0).

  2. :(예를 들어 1:5)를 사용하여 시퀀스를 생성 할 때 지수 연산자 ^가-연산자보다 우선하는 유일한 연산자라는 사실을 남용 할 수 있습니다 :(와 반대로 +-*/).

    1:2^2 => 1 2 3 4 

    예를 들어 n x n행렬 ( 1:n^2) 또는 지수 표기법 ( 1:10^6)을 사용하여 더 짧은 방식으로 표현할 수있는 다른 정수 의 요소를 반복하려는 경우 일반적으로 사용해야하는 괄호에 2 바이트가 절약됩니다 .

  3. 물론 +-*/가장 일반적으로 적용되지만 벡터화 된 연산에도 관련 트릭을 사용할 수 있습니다 +-.

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

    이것은 +1벡터화 되어 벡터 10:n생성 하는 각 요소에 추가 되기 때문에 작동 합니다 1 2 ... n+1. 마찬가지로 0:(n+1) == -1:n+11 바이트도 절약합니다.

  4. 짧은 함수 (한 줄로 표현할 수 있음)를 작성할 때 변수 할당을 남용하여 묶는 중괄호에 2 바이트를 절약 할 수 있습니다 {...}.

    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")}

    이것이 항상 특정 문제의 규칙을 준수하지는 않을 수 있습니다.


약간의 수정 : ^벡터화되어 우선 순위가 높습니다 :(즉, :대괄호가 명시 적으로 반대를 나타내지 않는 한 이전에 실행 됩니다 ( ?Syntax이진 및 단항 연산자의 정확한 우선 순위 참조 ). 따라서 트릭 n ° 3 +-/*보다 우선 순위가 낮은 이진에 대해서도 마찬가지입니다 :.
plannapus

@plannapus 설명해 주셔서 감사합니다. 문구를 업데이트했습니다.
Billywob

7

연산자의 의미 변경

R 연산자는 파서에 의해 특별한 처리를받는 함수일뿐입니다. 예를 들어 <실제로는 두 변수의 함수입니다. 이 두 줄의 코드는 동일한 작업을 수행합니다.

x < 3
`<`(x, 3) 

연산자에 다른 함수를 다시 할당 할 수 있으며 파서는 연산자 우선 순위를 포함하여 여전히 수행하지만 최종 함수 호출은 원래 함수가 아닌 새로운 함수 호출입니다. 예를 들면 다음과 같습니다.

`<`=rep

이제이 두 줄의 코드가 같은 일을합니다.

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

우선 순위가 존중되어 다음과 같은 결과가 나타납니다.

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

예를 들어이 답변연산자 우선 순위 페이지를 참조하십시오 . 부작용으로 귀하의 코드는 골프 언어로 작성된 코드처럼 암호가됩니다.

일부 연산자 는 하나 또는 두 개의 매개 변수를 좋아 +하고 -받아 들일 수 있으므로 다음과 같은 작업을 수행 할 수도 있습니다.

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

예를 들어이 답변을 참조하십시오 .

2 바이트, 3 인수 연산자로 사용하려면 이 답변 을 참조하십시오 [.


2
이것은 rturnbull의 팁 에 대한 의견 이지만 여기에 올 때 필요한 것을 찾기가 너무 어려워서 "응답 당 하나의 팁"규칙을 시행해야한다고 생각합니다.
주세페

1
또한 연산자의 우선 순위에 따라 도움이 될 수있는 펑키 한 일을 할 수 있습니다. 등 <보다 낮은 우선 순위를 가지고 +있지만, *보다 높은 우선 순위를 가지고 +그래서 당신은 잠재적으로 그들을 함께 체인 수 있습니다!
주세페

1
@Giuseppe 당신은 내가 게시하기 전에 무엇을 찾으려고했지만 그것을 찾을 수 없다는 것을 알고 있습니다. 지적 해 주셔서 감사합니다. 이 트릭을 점점 더 많이 사용하면서 예제와 함께 연산자 우선 순위에 대한 자세한 내용을 추가 할 계획입니다.
JayCe

2
여기에 재미있는 일이다 : 당신이 결합하는 경우 ?paste또는 두 개의 인수를 취할 수있는 몇 가지 다른 기능은, 우선 순위 순서가 여전히 통해 인라인 할당을 사용할 수 있다는 것을 의미합니다 a<-b?d<-e.
J.Doe

1
[3 요소 별명 (2 바이트)으로 추가해야합니다 . 나는 종종 같은 것들에 대한 것이 도움이 outer물론 당신이 확인해야하지만 (그리고 지속적으로 잊어!), 당신이 실제로 사용할 필요가 없습니다 [. 별명 선택을 돕기 위해 운영자 우선 순위 페이지 에 링크하는 것도 도움이 될 것 입니다.
주세페

5

당신이 피할 수 시나리오 paste(...,collapse="")strsplit

이것은 일반적인 문자열 도전에서 고통입니다. 몇 가지 해결 방법이 있습니다.

  • Reduce(paste0,letters) -5 바이트 paste0(letters,collapse="")

  • 2 바이트 골프 두 개의 벡터를 포함하는 목록을 c(1,2,3)c(4,5,6)하고 문자열로 그들에게 요소 현명한 연결할 싶어 "142536". 운영자 악용으로 p=paste0;"^"=Reduce;p^p^r일반적인 paste0호출 에서 2 바이트를 절약 할 수 있습니다 .

  • paste0("(.{",n,"})")정규식을 20 바이트로 구성 하는 대신 정규식에서 sub(0,"(.{0})",n)17 바이트를 고려하십시오.

때로는 (실제로는 매우 자주) 문자 또는 문자열로 구성된 벡터를 반복하거나 단어를 문자로 분할해야 할 때가 있습니다. 두 가지 일반적인 사용 사례가 있습니다. 하나는 함수 또는 프로그램의 입력으로 문자 벡터를 가져와야하고 다른 하나는 사전에 벡터를 알고 어딘가에 코드에 저장해야하는 것입니다.

에이. 문자열을 입력 으로 받아 단어문자 로 분할 해야하는 경우 .

  1. 단어 가 필요한 경우 (특수 문자로 문자 포함) :

    • 0x10단어를 구분 하는 줄 바꿈 (ASCII 16)이 정상이면 x=scan(,"")코드를 줄 바꿈하는 것이 좋습니다 function(s,x=el(strsplit(s," "))).

    • 단어에 의해 분리 될 수있는 경우 다른 공백 줄 바꿈 등 여러 공백, 탭, 포함, 당신은 NGM의 @ 사용할 수있는 이중 스캔 트릭 : x=scan(,"",t=scan(,"")). 이에 문자열에서 스캔을 제공 scan는 AS text인수와 공백하여 분리한다.

    • 두 번째 인수는 scan모든 문자열이 될 수 있으므로 문자열을 만든 경우 바이트를 저장하기 위해 재활용 할 수 있습니다 .

  2. 입력 문자열을 문자 로 구성된 벡터 로 변환해야하는 경우 :

    • x=el(strsplit(s,""))가장 짧은 일반적인 솔루션입니다. split인수를 포함한 길이가 0의 아무것도 작동 c(), {}당신이 길이가 0 인 변수를 만든 일이 있다면, 당신은 바이트를 저장하는 데 사용할 수 있도록 등.

    • 당신이 ASCII 문자 코드로 작업 할 수있는 경우, 고려 utf8ToInt하기 때문에, utf8ToInt(x)댄 짧은 strsplit전화. 다시 붙여 넣으려면 intToutf8(utf8ToInt(x))보다 짧습니다 Reduce(paste0,el(strsplit(x,""))).

    • "31415926535"입력 과 같이 임의의 숫자 문자열을 분리 해야하는 경우 종종 문자처럼 정수 자리를 사용할 수있는 경우 utf8ToInt(s)-483 바이트를 절약 el(strsplit(s,""))할 수 있습니다. 이것은 숫자를 10 진수로 나누기위한 일반적인 레시피보다 짧습니다.

비. 사전에 단어 나 문자로 고정 된 벡터 가 필요한 경우 .

  • 당신은 사용하여보고, 몇 가지 규칙적인 패턴이 있거나 알파벳 순서에있는 단일 문자의 벡터가 필요한 경우 intToUtf8또는 chartr를 통해 시퀀스에 적용 a:b하거나 문자 세트에 내장에 letters이상 LETTERS. 내장 된 패턴 언어 chartr특히 강력 합니다.

  • 를 들어 1 ~ 3 문자 나 단어 , c("a","b","c")유일한 일반 짧은 솔루션입니다.

  • 당신의 고정 된 벡터가 필요한 경우 4과 10 사이 공백 이외의 문자 나 단어를 사용 scan하여 stdin는 AS fileARG :

f(x=scan(,""))
q
w
e
r
t
y
u
  • 경우 scan에서은 stdin을 위해, 할 수 없습니다 6 개 이상의 비 공백 문자 나 단어 사용 scantext인수 scan(,"",t="a b c d e f").

  • 당신의 벡터를해야하는 경우 (가) 모든 유형의 6 개 이상의 문자 또는 (b) 10 이상의 공백이 아닌 문자 , strsplit를 통해 x=el(strsplit("qwertyuiop",""))아마 갈 수있는 방법입니다.

  • 당신은 할 수있다 멀리 얻을 수있을 다음 인용 트릭 : quote(Q(W,E,R,T,Y))표현한다는 생성한다. 일부 기능은 좋아 strrep하고, grep문자열의 벡터이 강요합니다! 그렇게하면 3에서 11 사이의 단어 나 문자형 벡터에 적합합니다.

  • strsplitvia를 통해 단어를 사용해야 할 이유가 없습니다 x=el(strsplit("q w e r t y"," ")). 항상 scan(,"",t="q w e r t y"))5 바이트의 고정 오버 헤드로 손실됩니다 .

다음은 길이가 단일 문자 인 벡터를 읽는 데 각 방법에서 사용하는 바이트 수의 표입니다 n. 각 행 내에서 상대적인 순서를 제외하고, 문자 나 단어에 대해 유효 strsplit""문자 만 작동한다.

| 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    |

씨. 텍스트를 문자 행렬로 입력해야하는 경우 짧게 보이는 몇 가지 레시피는 다음과 같습니다.

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
scan문자열 만 필요한 경우 text보다 경쟁력 있는 인수 el(strsplit(x," "))가 있습니다! 온라인으로 사용해보십시오! 에 대한 마지막 제안과는 반대로 read.csv.
Giuseppe

당신이 문자를 원한다면, 당신의 전화 scan는 최대 5 자 el(strsplit(x,""))이상 scan이며, 6 명 이상 보다 경쟁력이 있습니다. 온라인으로 사용해보십시오! 아직에 대한 좋은 사용법을 찾지 못했지만 read.csv어떤 이유로 데이터 테이블이 필요한 경우 유용 할 수 있습니까?
J.Doe

나는 용도를 찾지 data.frame못했지만 도움이 될만한 도전을 찾아야 할 수도 있습니다! 아마 dplyr스타일 group_by()summarize()조작의 유형? IDK.
Giuseppe

그리고 문자열을 읽는 scan(,"")것이 여전히 더 나은 것 같습니까? 온라인으로 사용해보십시오!
J.Doe

ngm이 입력 형식을 엄격하게 해석하면 double scan이 편리합니다.
Giuseppe

4

배열의 첫 번째 0이 아닌 요소를 찾는 몇 가지 방법.

이름이 있다면 x:

x[!!x][1]

NA0이 아닌 요소가없는 경우를 반환 합니다 ( x비어 있지만 NULL오류가 없는 경우 포함 ).

익명으로 :

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

NULL0이 아닌 요소가 없거나 비어 있거나없는 경우를 반환 합니다 NULL.


나는 NA모든 요소 x가 0 이면 돌아올 것이라고 생각합니다.주의해서 사용하십시오!
주세페

Find(c,x)x와 동일한 바이트 수 : x를 반복 (정의) 할 필요가 없다는 장점과 일치하지 않는 경우 다른 동작. TIO
JayCe

Find또한 NULL결과에 다른 일이 발생하지 않는 한 작동하는 동안 조금 더 안전합니다.이 경우 반환 NA또는 NULL더 안전 한지 확실하지 않습니다 .
ngm

아 맞다. NULL을 반환하는 문제는 오류입니다 ... 버전 비교 질문에서 내가 처음 시도한 sign(Find(c,w))오류는 오류가 발생하지 Find(c,sign(w))않도록해야했습니다. 나는 두 가지 방법이 모두 사용한다고 생각합니다.
JayCe

4

대안 rep()

rep()콜론 연산자 :와 R의 벡터 재활용으로 때로는 피할 수 있습니다 .

  • 반복을 위해 n, 제로를 여기서 n>0, 0*1:n보다 3 바이트 짧다 rep(0,n)!1:n배열이 FALSE사용 사례가 허용하는 경우, 4 바이트 짧다.

  • 반복 x n시간 x+!1:n은보다 2 바이트 짧습니다 rep(x,n). 대한 n사람, 사용 !!1:n하면의 배열을 사용할 수 있는지 TRUE.

  • 반복합니다 x 2n+1n>=0, x+0*-n:n4 바이트보다 짧은입니다 rep(x,2*n+1).

  • 성명서 !-n:n에는 TRUE양쪽에 측면이 n FALSE있습니다. 이것은 사용할 수 있습니다 생성 호출 문자의 수를 intToUtf8()당신이 제로가 무시됩니다 것을 기억합니다.

모듈 식 산술이 유용 할 수 있습니다. 정수 나누기를 사용하여 인수가있는 rep명령문을 each피할 수 있습니다.

  • 벡터를 생성하려면 c(-1,-1,-1,0,0,0,1,1,1), -3:5%/%35 바이트 짧다 rep(-1:1,e=3).

  • 벡터를 생성하려면 c(0,1,2,0,1,2,0,1,2), 0:8%%34 바이트를 저장합니다 rep(0:2,3).

  • 때때로 비선형 변환은 시퀀스 산술을 단축시킬 수 있습니다. 복합 명령문 내부에 맵핑 i in 1:15하기 c(1,1,3,1,1,3,1,1,3,1,1,3,1,1,3)위해 명백한 골치 아픈 대답은 1+2*(!i%%3)11 바이트입니다. 그러나 3/(i%%3+1)10 바이트 같은 순서로, 의지의 바닥을, 그래서 당신은 배열의 인덱스 순서를해야 할 경우 사용할 수 있습니다.


3

함수를 사용해야 할 때는 pryr::f()대신을 사용하십시오 function().

예:

function(x,y){x+y}

에 해당

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

또는 더 나은

pryr::f(x+y)

인수가 하나만 있으면 코드에서 형식을 추측합니다 .


당신이 (세 번째 예에서와 같은) 하나 개의 인수로 내려받을 수 있습니다하지 않는 한,이 들어, 골프 아닙니다 function(x,y){x+y}같이 쓸 수있다 function(x,y)x+y와 같은 bytecount가에 대한 pryr::f(x,y,x+y)하지만 더 가독성.
Khuldraeseth na'Barya

3

현과 관련된 생존 과제

바와 같이 다른 답변에 언급, unlist(strsplit(x,split="")paste(...,collapse="")우울이 될 수 있습니다. 그러나 이것들로부터 멀리 떨어지지 마십시오. 해결 방법이 있습니다!

  • utf8ToInt문자열을 벡터로 변환 intToUtf8하고 역 연산을 수행합니다. 당신의 벡터지고있어 int의 아닌 벡터를 char하지만 때로는 이것이 당신이 찾고있는 것입니다. 인스턴스의 목록을 생성하기 위해 -,보다 효율적으로 사용하는 intToUtf8(rep(45,34))것보다를paste(rep("-",34),collapse="")
  • gsubgrep단일 문자열에서 작동 할 때 제품군 의 다른 기능보다 더 유용 합니다. 위의 두 가지 접근 방식은 이 답변 에서와 같이 결합되어 ovs , Giuseppengm 의 조언을 활용할 수 있습니다.
  • 같이 편리한 I / O 형식을 선택 이 답변 (따옴표없이) 텍스트의 선으로 입력을 복용 또는 이 하나가 문자의 벡터를 복용. 확실하지 않은 경우 OP를 확인하십시오.
  • 주석에서 지적했듯이 <문자열을 사전 식으로 예상대로 비교합니다.

intToUtf8또한 multiple = FALSEint설정된 경우 단일 문자열이 아닌 s에서 개별 문자 (길이 1 문자열) 로 변환되는 두 번째 인수 가 TRUE있습니다.
Giuseppe

또한 3.5.0부터 세 번째 논쟁 allow_surrogate_pairs = FALSE이 있지만 그것이 무엇인지 모르겠습니다. 문서는 2 바이트를 읽는 것에 대해 무언가를 UTF-16말하지만 나는 그것이 무엇인지 거의 알지 못 UTF-8하기 때문에 누군가가 그것을 가지고 골프를 할 수있는 방법을 찾을 때까지 나는 그것을 무시할 것입니다.
Giuseppe

2

제한된 소스 문제에 대한 팁 :

  1. R 리터럴 상수의 문자는 16 진 코드, 8 진 코드 및 유니 코드로 대체 될 수 있습니다.

    예를 들어 문자열을 "abcd"작성할 수 있습니다.

        # 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" 

    유니 코드 문자가 8 진수 / 16 진수와 혼합되지 않는 한 문자를 8 진수 / 16 진수 / 유니 코드와 혼합하고 일부 8 진수 코드와 16 진수 코드를 함께 사용할 수도 있습니다.

        # 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"

    자세한 내용 은이 섹션의 끝 부분 을 참조하십시오.

  2. 문자열 리터럴을 사용하여 함수를 작성할 수 있으므로 cat()대체적으로 작성할 수 있습니다.

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

    함수 이름에 8 진 코드, 16 진 코드 및 유니 코드를 사용할 수 있습니다.

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

    유니 코드 시퀀스가 ​​백틱 내에서 지원되지 않는 유일한 예외는``

  3. 작업자를 학대하는 둥근 괄호는 다음과 같이 피할 수 있습니다.

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

이 답변에서 세 가지 트릭을 모두 적용 할 수 있습니다.


: 또한, 숫자 16 진수로 작성 할 수 있습니다 0xB0xb반환 11(역 따옴표 또는 따옴표 필요 없음).
로빈 라이더
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.