R打高尔夫球的秘诀


58

我正在寻找使用R统计语言打高尔夫球的技巧。R可能是高尔夫的非常规选择。但是,它非常紧凑地执行某些操作(序列,随机性,向量和列表),许多内置函数的名称都很短,并且具有可选的行终止符(;)。您可以提供哪些提示和技巧来帮助解决R中的代码高尔夫问题?


14
鉴于代码高尔夫确实是您唯一应该做很多事情的唯一时间,因此该问题的答案可以用作R的反样式指南:-)
AndrewBrēza17年

Answers:


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也可以为您节省一些宝贵的字符,但是您将要使用1,2而不是0,1。

    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. 这是微不足道的,但是何时使用@Tommy别名函数的秘诀:如果函数名的长度为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)甚至更短了一个字节。
JayCe

19

一些非常具体的高尔夫技巧:

  • 如果需要提取向量的长度,sum(x|1)短于length(x)只要x是数值,整数,复杂的或逻辑。
  • 如果您需要提取向量的最后一个元素,则使用,然后调用而不是(或使用上述技巧)来向后初始化向量(如果可能,则更便宜)(或---感谢Giuseppe!)。关于这个(其中倒数第二个元件需要的话)的轻微变化可以看出这里。即使您不能向后初始化向量,它也要短于(并且也适用于字符向量)。有时甚至不需要,例如使用代替。rev()x[1]x[length(x)]x[sum(x|1)]tail(x,1)rev(x)[1]x[sum(x|1)]revn:11: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)。任何非零数字均视为TRUE。如果您正在测试相等性,请说,if(x==y)a else b然后尝试if(x-y)b else a。另请参阅上一点。

  • el当您需要从列表中提取项目时,此功能很有用。最常见的例子可能是strsplitel(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文本比文本挑战更容易catpch=用于plot控制绘制哪些字符的参数。可以将其缩短为pc=(也会发出警告)以节省一个字节。这里的例子。

  • 要讲数字,请不要使用floor(x)。使用x%/%1代替。

  • 要测试数字或整数向量的元素是否全部相等,通常可以使用sd而不是诸如的冗长的词all.equal。如果所有元素都相同,则其标准偏差为零(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. 滥用buildins TF。默认情况下,它们的取值为TRUEFALSE,可以将其自动转换为数字10,并且可以随意重新定义它们。这意味着您不需要初始化计数器(例如i=0... i=i+1),您可以根据需要使用TF(然后直接跳至F=F+1稍后)。
  2. 请记住,函数返回最后一个被调用的对象,不需要显式return()调用。
  3. 为常用功能定义短别名非常好,例如p=paste。如果您经常使用一个函数且仅带有两个参数,则插入别名可能会为您节省一些字节。插入别名必须用包围%。例如:

    `%p%`=paste

    随后的x%p%y字节比p(x,y)。短1个字节。插入别名定义比非插入别名长4个字节p=paste,因此您必须确保它是值得的。


9
您可以使用原始函数并节省许多字节:`+`=paste; x+y
Masclins

14

使用ififelse`if`

在R中有多种方法来执行if语句。高尔夫最佳解决方案可能相差很大。

基础

  1. if用于控制流。它不是向量化的,即只能评估长度为1的条件。它要求else(可选)返回else值。
  2. ifelse是一个功能。它是向量化的,并且可以返回任意长度的值。它的第三个参数(else值)是必须的。*
  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

在某些情况下,我们确实关心else条件,并且希望执行任意代码而不是返回值。在这些情况下,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块可以返回其中一部分执行的final语句的值。例如,代替

    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')
阿里·弗里德曼

11

隐式类型转换

功能as.characteras.numeric以及as.logical太字节重。让我们修剪一下。

从数字转换为逻辑(4个字节)

假设x是一个数值向量。使用逻辑非运算符!隐式地将数字重铸为逻辑向量,其中0is FALSE和非零值是TRUE!然后将其反转。

x=!x

x=0:3;x=!x返回TRUE FALSE FALSE FALSE

从数字或逻辑转换为字符(7个字节)

这很有趣。(通过此推文。)

x[0]=''

[R看到你正在更新向量x'',这是阶级的character。因此它强制转换x为类,character以便与新数据点兼容。其次,它关系到把''在适当的地方......但指数0不存在(这一招也可与InfNaNNANULL,等)。结果,x仅在类中被修改。

x=1:3;x[0]=''返回"1" "2" "3",然后x=c(TRUE,FALSE);x[0]=''返回"TRUE" "FALSE"

如果您的工作空间中已经定义了一个字符对象,则可以使用它代替''保存字节。例如,x[0]=y

在特定条件下从数字或逻辑转换为字符(6个字节)

J.Doe在评论中指出了一个六字节的解决方案:

c(x,"")

如果x是原子的,并且打算将其传递给需要原子向量的函数,则此方法有效。(该函数可能会发出有关忽略参数元素的警告。)

从逻辑转换为数字(4个字节)

您可以使用上方的时髦索引技巧(例如x[0]=3),但实际上有一种更快的方法:

x=+x

正运算符隐式地将向量重铸为数字向量,因此TRUE FALSE变为1 0


你的最后绝招可以x=+xTRUE作为1
朱塞佩

@Giuseppe哦,当然了!谢谢,现在更新。
rturnbull

从数字或逻辑到字符的转换。c(x,"")如果x您要x在仅关心第一个元素的函数中使用(可能会抱怨),则可以使用if 原子的。这比便宜1字节x[0]="";
J.Doe '18

10

R中的Do-while循环

有时,我发现自己希望R有一个do-while循环,因为:

 some_code
while(condition){
 some_code # repeated
}

太长了,而且太笨拙了。但是,我们可以恢复此行为,并使用该{功能删除一些字节。

{(.PrimitiveR 中的每个函数。

他们的文档内容为:

实际上,(在语义上等同于identity function(x) x,而{在某种程度上更有趣,请参见示例。

在价值之下

对于(,对参数求值的结果。设置了可见性,因此如果在顶层使用,将自动打印。

对于{最后一个表达式的结果求值。这具有最后评估的可见性。

(添加了重点)

那么这是什么意思?这意味着do-while循环非常简单

while({some_code;condition})0

因为里面{}的每个表达式都被求值,并且只有最后一个表达式由返回{,因此我们可以some_code在进入循环之前进行求值,并且每次运行condition都是TRUE(或正确)。的0是形成的“真实”的躯体上的1个字节表达的一个while循环。


10
  1. 滥用outer将任意函数应用于两个列表的所有组合。想象一个矩阵,其中i,j由第一个args索引,然后您可以为每对定义一个任意函数(i,j)。

  2. 使用Map作为快捷方式mapply。我的主张是,mapply在需要访问索引的情况下,它比for循环便宜。滥用R.中的列表结构unlist非常昂贵。methods::el让您便宜地取消列出第一个元素。尝试在本地使用具有列表支持的功能。

  3. 使用do.call一概而论任意输入函数调用。

  4. 的args累积Reduce对于代码高尔夫非常有用。

  5. 用逐行写入控制台cat(blah, "\n")更便宜write(blah, 1)。在某些情况下,带有“ \ n”的硬编码字符串可能会更便宜。

  6. 如果函数带有默认参数,则可以使用function(,, n-arg)直接指定第n个参数。示例:seq(1, 10, , 101)在某些函数中,支持部分参数匹配。范例:seq(1, 10, l = 101)

  7. 如果您遇到涉及字符串操作的挑战,只需按返回按钮,然后阅读下一个问题。strsplit单手负责破坏R高尔夫球。

现在获取一些2018年新发现的技巧

  1. A[cbind(i,j)] = z可能是操纵矩阵的好方法。假设您设计i, j, z为长度正确的向量,则此操作非常有效。您可以通过调用实际的index / assign函数节省更多"[<-"(cbind(i,j), z)。这种调用方式返回修改后的矩阵。

  2. 使用新行而不是\n换行符。

  3. 压缩行数可以节省您的字节数。内联分配lapply(A<-1:10,function(y) blah)和函数args分配function(X, U = X^2, V = X^3)是实现此目的的方法。

  4. "[<-"R中的一个函数也是如此(并且与我关于SO的古老问题有关)!那是负责诸如之类的操作的基础功能x[1:5] = rnorm(5)。通过名称调用函数的整洁属性允许您返回修改后的向量。按照顺序,word "[<-"(x, 1:5, normr(5))会执行与上面的代码几乎相同的操作,除了它返回修改后的x。相关的“ length <-”,“ names <-”,“ anything <-”都返回修改后的输出


1
我认为使用"[<-"值得它自己的“技巧”答案,因为它将返回修改后的数组/矩阵/任何内容。
朱塞佩

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。这将留下一个尾随的零,因此,如果这对您很重要,请使用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),只要l是一个listvector长度大于1的,因为它默认为seq_along(l)。如果l可能是长度1,那就可以seq(a=l)了。

    此外,:将(带有警告)使用其参数的第一个元素。

  5. 删除属性使用c()上一个array(或matrix)会做的一样as.vector; 通常会删除非名称属性。

  6. 阶乘使用gamma(n+1)比使用短,factorial(n)并且factorial始终定义为gamma(n+1)

  7. 硬币翻转需要50%的时间执行随机任务时,使用时间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” 的标题。其他提示问题每个答案只能有一个提示,我也对此表示衷心的支持!此外,1:n*0短于Im(1:n)2个字节,这意味着您的第二个技巧也可以x+0*-n:n:-)
朱塞佩

1
@ J.Doe甚至更好,!1:n它也是一个n零数组,具体取决于用例;不过,可以归功于MATL / MATLAB提示问题(可能是Luis Mendo)。
朱塞佩

谢谢@Giuseppe!我是否可以建议您创建此帖子,因为我不想从您的好主意中获得声誉。
J.Doe '18年

@ J.Doe哦,我不在乎。让其他R高尔夫球手获得更大的知名度总是很高兴;我认为现在可以说我是一个知名的实体!您一直在提出建议,希望取得令人印象深刻的改进,因此请选择代表(双关语是无用的),并保持打高尔夫的出色表现:-)
朱塞佩

1
不是(log(i,b)%/%1):0)代替floor(log(n,b))+1
仅限ASCII

8

一些基本概念,但应有些用处:

  1. 在控制流语句中,您可能会滥用将不等于零的任何数字都评估为TRUE,例如:if(x)等于if(x!=0)。相反,if(!x)等效于if(x==0)

  2. 当使用:(例如1:5)生成序列时,可能会滥用幂运算符^是唯一优先于:-operator的操作符(与相对+-*/)的事实。

    1:2^2 => 1 2 3 4 

    这样可以在圆括号上为您节省两个字节,以防您想要例如对n x n矩阵(1:n^2)的元素或任何其他可以用指数表示法(1:10^6)较短表示的整数进行循环。

  3. 当然,也可以在矢量化操作上使用相关的技巧+-*/,尽管最常用的技巧是+-

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

    之所以+1可行,是因为将其矢量化,并将其添加1到矢量的0:n结果的每个元素中1 2 ... n+1。同样,0:(n+1) == -1:n+1您也可以节省一个字节。

  4. 编写短函数(可以在一行上表示)时,可以滥用变量分配以将两个字节保存在封闭的花括号中{...}

    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二进制和一元运算符的确切优先顺序)。+-/*优先级低于:技巧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
这是对rturnbull技巧的评论,但我认为我们需要开始执行“每个答案一个技巧”的规则,因为来这里时很难找到我需要的技巧
朱塞佩

1
另外,根据操作员的优先级,您可以做一些时髦的事情,可能会有所帮助;like的<优先级低于+,但*优先级高于,+因此您可以将它们链接在一起!
朱塞佩

1
@Giuseppe,您知道我在发布前试图找到的内容,但找不到。感谢您指出。随着越来越多地使用此技巧,我计划在示例中添加有关运算符优先级的更多详细信息。
JayCe

2
这是一个有趣的例子:如果您绑定?paste或可以接受两个参数的某个其他函数,则优先顺序意味着您仍然可以通过使用内联分配a<-b?d<-e
J.Doe '18 -10-2

1
您应该添加[一个三元素别名(即两个字节)。我经常发现它对于诸如此类的事情outer(并始终将其遗忘!)很有用,尽管您当然需要确保实际上不需要使用[。链接到运算符优先级页面以帮助选择别名也可能会有所帮助。
朱塞佩

5

您可以避免paste(...,collapse="")strsplit

这些是通常的弦乐挑战中的痛苦。有一些解决方法。

  • Reduce(paste0,letters) 对于-5个字节 paste0(letters,collapse="")

  • 一个2字节的高尔夫,其中有一个包含两个向量的列表c(1,2,3)c(4,5,6)并且想要将它们逐个元素地连接到string "142536"。操作员滥用为您p=paste0;"^"=Reduce;p^p^r节省了平时paste0通话的两个字节。

  • 与其paste0("(.{",n,"})")构造(例如)20个字节的正则表达式,不如考虑一个正则表达式中的正则表达式:sub(0,"(.{0})",n)17个字节。

有时(实际上,通常很多),您需要遍历字符或字符串的向量,或将一个单词拆分为字母。有两种常见的用例:一种是需要将字符向量作为函数或程序的输入,另一种是需要事先知道向量并将其存储在代码中的某个地方。

一种。在需要将字符串作为输入并将其拆分为单词字符的地方

  1. 如果需要单词(特殊情况下包括字符):

    • 如果用换行符0x10(ASCII 16)分隔单词是可以的,x=scan(,"")则最好将代码包装在中function(s,x=el(strsplit(s," ")))

    • 如果单词可以用任何其他空格分隔,包括多个空格,制表符,换行符等,则可以使用@ngm的double scan技巧x=scan(,"",t=scan(,""))。这将扫描的字符串scan作为textarg并用空格分隔。

    • 中的第二个参数scan可以是任何字符串,因此,如果创建了一个字符串,则可以对其进行回收以保存一个字节。

  2. 如果需要将输入字符串转换为字符向量

    • x=el(strsplit(s,""))是最短的通用解决方案。该split参数适用于长度为零的任何内容,包括c(){}等等,因此,如果您碰巧创建了一个零长度的变量,则可以使用它来保存一个字节。

    • 如果可以使用ASCII字符代码,请考虑utf8ToInt,因为utf8ToInt(x)它比strsplit调用短。将它们粘贴回去,intToutf8(utf8ToInt(x))比短Reduce(paste0,el(strsplit(x,"")))

    • 如果您需要分割任意数字串(例如"31415926535"输入),则可以utf8ToInt(s)-48在上保存3个字节el(strsplit(s,"")),前提是您可以使用整数代替字符(通常是这样)。这也比将数字拆分为十进制数字的常用方法要短。

b。您需要事先固定的单词或字符向量

  • 如果您需要一个具有一定规则模式或字母顺序的单个字符向量,请通过或在内置字母集或上使用intToUtf8chartr应用于序列。内置的模式语言是特别强大a:blettersLETTERSchartr

  • 对于1到3个字符或单词c("a","b","c")是唯一通用的最短解决方案。

  • 如果您需要4到10个非空格字符或单词的固定向量,请使用scanwith stdin作为filearg:

f(x=scan(,""))
q
w
e
r
t
y
u
  • 如果无法使用scanfrom stdin,则对于6个或更多的非空白字符或单词,请scantext参数一起使用scan(,"",t="a b c d e f")

  • 如果您需要一个(a)6个或更多任何类型的字符或(b)10个或更多非空白字符的向量,则可能要使用strsplitvia x=el(strsplit("qwertyuiop",""))

  • 可能可以摆脱下面的引用技巧quote(Q(W,E,R,T,Y)),创建该表达式。某些功能,例如strrep和,grep会将其强制为字符串向量!如果这样做,这对于3到11之间的任意长度的单词或字符向量都非常有用。

  • 没有充分的理由在上使用strsplit单词x=el(strsplit("q w e r t y"," "))。它总是丢失scan(,"",t="q w e r t y"))5个字节的固定开销。

这是每种方法用来读取length个字符的向量所使用的字节数的表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    |

C。如果您需要将文本作为字符矩阵输入,那么一些看起来很短的食谱是

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
朱塞佩

如果您只想要字符,则呼叫scan最好最多5个字符,el(strsplit(x,""))scan6个或更多字符更具竞争力。在线尝试!我尚未找到的好用处read.csv,但如果出于某种原因需要数据表,也许会有用吗?
J.Doe,

我从来没有发现a的用处,data.frame但也许我们需要在有帮助的地方找到/创造一个挑战!也许是dplyr风格group_by()summarize()操纵类型?IDK。
朱塞佩

而且对于字符串阅读来说scan(,"")似乎还更好?在线尝试!
J.Doe,

是的,可以肯定的,尽管如果您严格按照ngm的方式解释输入格式那么double scan会很方便。
朱塞佩

4

查找数组的第一个非零元素的一些方法。

如果有名称x

x[!!x][1]

返回NA如果没有非零元素(包括时x是空的,但不是NULL该错误)。

匿名地:

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

NULL如果没有非零元素,或者为空或,则返回NULL


我相信,NA如果所有元素x都为零,这将返回,因此请谨慎使用!
朱塞佩

Find(c,x)具有相同的字节数:不需要重复(定义)x的优点,如果不匹配,则具有不同的行为。TIO
JayCe '18

Find只要它对NULL结果没有其他影响,它在工作时也会更安全一些,在这种情况下,我不确定返回NA还是NULL更安全。
ngm

哦,是的。返回NULL的问题是错误...在我第一次尝试的版本比较问题中sign(Find(c,w)),这导致了错误-必须这样做Find(c,sign(w))才能使其不出错。我认为两种方式都有其用途。
JayCe

4

替代品 rep()

有时rep()可以避免使用冒号运算符:和R的向量循环。

  • 对于重复的n零,其中n>0,如果比使用情况短,其中,0*1:nrep(0,n)和短3个字节!1:n,则的数组FALSE短4个字节。

  • 要重复x n一次,请x+!1:n比缩短2个字节rep(x,n)。对于n一个,!!1:n如果可以使用的数组,则使用TRUE

  • 要重复x 2n+1一次,其中n>=0x+0*-n:n比短4个字节rep(x,2*n+1)

  • 该声明!-n:nTRUE在双方的两侧n FALSE。这可以用来产生甚至在调用字符的数字intToUtf8(),如果你还记得,一个零被忽略。

模块化算法可能会很有用。使用整数除法有时可以避免使用rep带有each参数的语句。

  • 要生成矢量c(-1,-1,-1,0,0,0,1,1,1)-3:5%/%3它比短5个字节rep(-1:1,e=3)

  • 要生成矢量c(0,1,2,0,1,2,0,1,2),请0:8%%3在上保存4个字节rep(0:2,3)

  • 有时非线性变换会缩短序列算法。要映射i in 1:15c(1,1,3,1,1,3,1,1,3,1,1,3,1,1,3)复合语句内部,明显的golfy答案是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与相同的字节数,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朱塞佩NGM
  • 选择一个方便的I / O格式,在这个答案正在输入的文本行(不带引号),或这一个以字符的载体。如有疑问,请与OP确认。
  • 正如评论中指出的那样,按<字典顺序对字符串进行比较,就像人们期望的那样。

intToUtf8还具有第二个参数multiple = FALSE,该参数将从ints 转换为单个字符(长度为一的字符串),而不是设置为的单个字符串TRUE
朱塞佩

另外,从3.5.0开始,还有第三个参数allow_surrogate_pairs = FALSE,但我不知道它的作用。该文档说了一些有关读取两个字节的内容,UTF-16但是我几乎不知道这UTF-8是什么,所以我将忽略它,直到其他人找到打高尔夫球的方法为止。
朱塞佩

2

限制来源挑战的提示:

  1. R文字常量中的字符可以用十六进制代码,八进制代码和unicode代替。

    例如,字符串"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" 

    我们还可以将字符与八进制/十六进制/ unicode混合,并同时使用某些八进制代码和一些十六进制代码,只要unicode字符不与八进制/十六进制混合即可,例如:

        # 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`()

    我们也可以使用八进制代码,十六进制代码和unicode作为函数名称:

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

    唯一的例外是反引号内不支持unicode序列''

  3. 圆括号可以避免虐待操作员,例如:

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

这三个技巧的应用都可以在此答案中找到


同样,数字可以用十六进制表示:0xB0xb返回11(不需要反引号或引号)。
罗宾·赖德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.