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
代替-e
a..z
,甚至aa..zz
。如果需要作为字符串,请使用join
。$z = 'z'; print ++$z;
将显示aa
我现在能想到的就是这些。我可能以后再添加一些。
print ('X'*10) . $/;
应该做的?对我来说,它打印0
,没有换行符。一方面,括号成为的函数样式调用参数print
,其绑定比紧密.
。您是说x
而不是*
什么意思吗?
while
,join'',<>;
没有它们也可以使用。
使用$%
代替代替$a
可以使您将变量名直接放在或旁边if
,如下所示:for
while
@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
。