Answers:
这是您需要了解的最重要的Perl高尔夫技巧。每当您要查看一些太长的字符序列以完成任务时,都必须问自己,是否有其他方法可以使用不同的功能来获得相同的效果。通常有。这里只是少数几个:
~~强制执行标量上下文,并且比短4个字符scalar。
y///c比length获得长度短1个字符$_。
是否需要遍历其中的字符$_?替换split//为/./gs。(或者使用/./g,如果你也想跳过换行。)这工作与其他变量:更换split//,$x用$x=~/./gs。
每个内置的Perl都会返回一些信息。print例如,返回1表示成功的I / O。$_例如,如果需要初始化为真实值,则$_=print$foo可以用一块石头杀死两只鸟。
Perl中几乎每个语句都可以写为表达式,从而可以在更广泛的上下文中使用。多个语句可以成为用逗号链接在一起的多个表达式。可以使用短路运算符?: && ||,也可以使用and和进行测试or,它们的功能相同,但优先级低于所有其他运算符(包括赋值)。循环可通过map或进行grep。即使关键字,例如next,last并且return可以在表达式上下文中使用,即使他们不回!牢记这些转换,使您有机会用可以填充到更多上下文中的表达式替换代码块。
$foo。否则,$_=1它要短得多,$_=print""并且具有相同的效果。
$x吗?否则,你可能只是做/./gs和/./g。
滥用Perl的特殊变量!
如上一个答案中所述,$/并且$"默认情况下分别初始化为"\n"和" "。
$,和$\都undef默认设置为,且短3个字符。
设置$\为一个值将导致将其附加到每个print。例如:perl -ple '$\="=".hex.$/'是一个方便的十六进制到十进制转换器。
如果您不是从命令行读取文件,则可以将-i命令行开关用作输入字符串的额外通道。其值将存储在中$^I。
$=强制分配给它的任何内容都是整数。尝试运行perl -ple '$_=$==$_'并给予各种提示。同样,$-将其值强制为非负整数(即,前导破折号被视为非数字字符)。
您可以将其$.用作布尔值标志,该标志会在每次循环迭代时自动重置为true(非零)值while(<>)。
-n 和无与伦比的花括号众所周知,命令行开关-n可用于每行执行一次脚本。
perl --help 说:
-n assume "while (<>) { ... }" loop around program
它没有明确说的是Perl不只是假设程序周围存在循环。它从字面上包裹while (<>) { ... }着它。
这样,以下命令彼此等效:
perl -e 'while(<>){code}morecode'
perl -ne 'code;END{morecode}'
perl -ne 'code}{morecode'
-p 和无与伦比的花括号与上述类似,该-p开关环绕while (<>) { ... ; print }程序。
通过使用不匹配的花括号,perl -p 'code}{morecode'将在code对所有输入行执行后仅打印一次,然后是morecode。
由于$_在morecode;print执行时是未定义的,$\因此可以滥用输出记录分隔符来打印实际输出。
例如
perl -pe '$\+=$_}{'
从STDIN每行读取一个数字并打印其总和。
#!perl -n在第一行完成此操作,对吗?
}for(...){在花括号之间插入一个通常也很方便,例如codegolf.stackexchange.com/a/25632
使用$_消除标引用。这是大多数函数默认使用的特殊变量,仅保留参数是引用此变量的捷径。
通过更改$n为$_,您可以更改$n=<>;chop$n;print$n为$_=<>;chop;print
在此,该print功能$_默认情况下会打印的内容,并且chop也可在上使用$_。
$_=<>;必需的,不自动<>;将行读入$_吗?
$_=<>;print和<>;print。第一个重复我输入的内容,而另一个不重复。
print while(<>)。不知道这是一个特殊的情况下,或有一些连贯的逻辑背后,无论是<>在的一部分perlop,也不while部分perlsyn似乎提这种行为。
while(<>)是一种特殊情况,在perlsyn,I / O运算符中有记录:'当且仅当输入符号是“ while”语句条件内的唯一内容时(即使伪装成“ for(;;)”,)循环),则该值会自动分配给全局变量$ _,破坏之前的值。”
尽可能使用Perl的特殊变量,例如:
$"代替" "$/代替"\n"在词法分析器的帮助下,它们的另一个好处是可以保证一个字符的长标识符。这样就可以将其粘贴到其后的关键字,如下所示:print$.for@_
所有特殊变量的列表在此处提供:特殊变量
不要使用qw。这浪费了可以更好地使用的两个字符。例如,不要写以下内容。
@i=qw(unique value);
而是使用裸词。
@i=(unique,value);
或者,如果您不能使用裸字,请使用glob语法。
@i=<unique value>;
glob 语法也可以用于产生有趣的效果。
@i=<item{1,2,3}>;
不要use strict。(不要在此引用我,PCG.SE上下文还很重要),更重要的是,不要像在严格的条件下那样进行编码。普通嫌疑犯:
my如果可以避免的话,请不要声明变量。真正需要的唯一变量my是您希望按词法作用域限定的变量。打高尔夫球时几乎没有这些东西,您不需要示波器保护,而可以完全控制递归。print hello将无法正常工作。它实际上意味着print hello $_(打印$_到filehandle hello)。
print现在我找不到一个简短的例子)
我确定其中一些具有正式名称,但我只是不知道它们。
print $n++ while ($n < 10) $var = join('',<>) print ('X'*10) . "\n";比print ('X'*10) . $/;say功能比短print,但您必须使用-E代替-ea..z,甚至aa..zz。如果需要作为字符串,请使用join。$z = 'z'; print ++$z;将显示aa我现在能想到的就是这些。我可能以后再添加一些。
print ('X'*10) . $/;应该做的?对我来说,它打印0,没有换行符。一方面,括号成为的函数样式调用参数print,其绑定比紧密.。您是说x而不是*什么意思吗?
while,join'',<>;没有它们也可以使用。
使用$%代替代替$a可以使您将变量名直接放在或旁边if,如下所示:forwhile
@r=(1,2,3,4,5);$%=4;
print$_*$%for@r
可以使用许多方法,但是请检查docs和@BreadBox的答案中哪些具有魔术效果!
如果您不能按照@JB 的答案使用语句修饰符,则map可能会保存一个字节:
for(@c){} 与 map{}@c;
如果要进行嵌套迭代,则很有用,因为您可以将后缀for循环放在内map。
Perl具有用于“匹配前文本”和“匹配后文本”的魔术变量,因此可以拆分成两个一组,字符数可能更少:
($x,$y)=split/,/,$_;
($x,$y)=/(.+),(.+)/;
/,/; # $x=$`, $y=$'
# Note: you will need to save the variables if you'll be using more regex matches!
这也可以很好地替代substr:
$s=substr$_,1;
/./;# $s=$'
$s=substr$_,4;
/.{4}/;# $s=$'
如果需要匹配的内容,$&可以使用,例如:
# assume input like '10 PRINT "Hello, World!"'
($n,$c,$x)=split/ /,$_;
/ .+ /; # $n=$`, $c=$&, $x=$'
如果您print在代码中多次说了四遍(显然,这取决于您要调用的例程的长度),请用较短的子名称替换它:
sub p{print@_}p;p;p;p
与
print;print;print;print
如果您有如下代码:
$i--if$i>0
您可以使用:
$i-=$i>0
而是节省一些字节。
如果您没有分配变量,因此不能使用面包箱的提示,则可以使用以下表达式0|:
rand 25 # random float eg. 19.3560355885212
int rand 25 # random int
0|rand 25 # random int
rand 25|0 # random int
~~rand 25 # random int
值得注意的是,您不需要使用整数来访问数组索引:
@letters = A..Z;
$letters[rand 26]; # random letter
您可以在嵌套三元逻辑中运行多个不同的语句。
假设你有一个大的if- elsif声明。这可以是任何逻辑和任意数量的语句。
if( $_ < 1 ) {
$a=1;
$b=2;
$c=3;
say $a.$b.$c;
} elsif($_ < 2 ) {
$a=3;
$b=2;
$c=1;
say $a.$b.$c;
} elsif( $_ < 3) {
$a=2;
$b=2;
$c=2;
say $a.$b.$c;
}
您可以(cmd1, cmd2, cmd3)在内部使用三元运算符来运行所有命令。
$_ < 1 ?
($a=1,$b=2,$c=3,say$a.$b.$c):
$_ < 2 ?
($a=3,$b=2,$c=1,say$a.$b.$c):
$_ < 3 ?
($a=2,$b=2,$c=2,say$a.$b.$c):
0; #put the else here if we have one
这是一个虚拟的例子:
perl -nE'$_<1?($a=1,$b=2,$c=3,say$a.$b.$c):$_<2?($a=3,$b=2,$c=1,say$a.$b.$c):$_<3?($a=2,$b=2,$c=2,say$a.$b.$c):0;' <(echo 2)
select(undef,undef,undef,$timeout)代替Time::HiRes(摘自https://stackoverflow.com/a/896928/4739548)
许多挑战要求您比整数更精确地入睡。select()的timeout参数可以做到这一点。
select($u,$u,$u,0.1)
效率比:
import Time::HiRes qw(sleep);sleep(0.1)
前者只有20个字节,而后者则占用39个字节。但是,前者要求您不使用$u并且从未定义过它。
如果要使用它,很多导入都会带来Time::HiRes收益,但是如果只需要一次,使用select($u,$u,$u,0.1)节省19个字节,这在大多数情况下无疑是一个改进。
除非挑战另有说明,否则您不需要尾随换行符。
我们的“挑战”说“将0到9的随机数输出到STDOUT”。我们可以使用以下代码(28个字节):
$s=int(rand(10));print"$s\n"
并将其缩短为此(25个字节):
$s=int(rand(10));print $s
通过简单地打印变量。最后一个仅专门适用于此挑战(19个字节):
print int(rand(10))
但这仅在您不必在赋值和打印之间对变量执行任何操作时才有效。
以下是将输入整形为正弦波的代码:
s/./print" "x(sin($i+=.5)*5+5).$&/eg;
如您所见,这是在标准输入中遍历字符的非常紧凑的方式。您可以使用其他正则表达式来更改事物的匹配方式。
$_=print""比短$_=print$foo。