在Perl打高尔夫球的秘诀?


47

您在Perl打高尔夫球有哪些一般秘诀?我正在寻找可以普遍用于解决高尔夫问题的想法,这些想法至少在某些方面是Perl特有的(例如,“删除评论”不是答案)。请为每个答案发布一个提示。

Answers:


26

TMTOWTDI

这是您需要了解的最重要的Perl高尔夫技巧。每当您要查看一些太长的字符序列以完成任务时,都必须问自己,是否有其他方法可以使用不同的功能来获得相同的效果。通常有。这里只是少数几个:

  • ~~强制执行标量上下文,并且比短4个字符scalar

  • y///clength获得长度短1个字符$_

  • 是否需要遍历其中的字符$_?替换split///./gs。(或者使用/./g,如果你也想跳过换行。)这工作与其他变量:更换split//,$x$x=~/./gs

  • 每个内置的Perl都会返回一些信息。print例如,返回1表示成功的I / O。$_例如,如果需要初始化为真实值,则$_=print$foo可以用一块石头杀死两只鸟。

  • Perl中几乎每个语句都可以写为表达式,从而可以在更广泛的上下文中使用。多个语句可以成为用逗号链接在一起的多个表达式。可以使用短路运算符?: && ||,也可以使用and和进行测试or,它们的功能相同,但优先级低于所有其他运算符(包括赋值)。循环可通过map或进行grep。即使关键字,例如nextlast并且return可以在表达式上下文中使用,即使他们不回!牢记这些转换,使您有机会用可以填充到更多上下文中的表达式替换代码块。


$_=print""比短$_=print$foo
ASCIIThenANSI

5
想法是您已经需要打印$foo。否则,$_=1它要短得多,$_=print""并且具有相同的效果。
面包盒'16

3
对于第三个,您的意思是遍历其中的字符$x吗?否则,你可能只是做/./gs/./g
redbmk

25

滥用Perl的特殊变量!

  • 如上一个答案中所述,$/并且$"默认情况下分别初始化为"\n"" "

  • $,$\undef默认设置为,且短3个字符。

  • 设置$\为一个值将导致将其附加到每个print。例如:perl -ple '$\="=".hex.$/'是一个方便的十六进制到十进制转换器。

  • 如果您不是从命令行读取文件,则可以将-i命令行开关用作输入字符串的额外通道。其值将存储在中$^I

  • $=强制分配给它的任何内容都是整数。尝试运行perl -ple '$_=$==$_'并给予各种提示。同样,$-将其值强制为非负整数(即,前导破折号被视为非数字字符)。

  • 您可以将其$.用作布尔值标志,该标志会在每次循环迭代时自动重置为true(非零)值while(<>)


20

-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在第一行完成此操作,对吗?
ASCIIThenANSI

@ASCIIThenANSI:是的,这是正确的。(对于最新答案,我们深表歉意。)
丹尼斯

1
在应该归功的地方提供功劳,我想我在@primo的Perl答案之一中第一次看到了这三个技巧(无括号,-p$\​)的组合。阅读他的答案本身就是一个很好的Perl技巧。
丹尼斯2015年

1
}for(...){在花括号之间插入一个通常也很方便,例如codegolf.stackexchange.com/a/25632
primo

我发现与-n结合使用的一件事是|| =默认值赋值运算符。避免无法在循环前分配值。
deltaray

18

使用$_消除标引用。这是大多数函数默认使用的特殊变量,仅保留参数是引用此变量的捷径。

通过更改$n$_,您可以更改$n=<>;chop$n;print$n$_=<>;chop;print

在此,该print功能$_默认情况下会打印的内容,并且chop也可在上使用$_


$_=<>;必需的,不自动<>;将行读入$_吗?
2013年

不,我不这么认为。我比较了程序$_=<>;print<>;print。第一个重复我输入的内容,而另一个不重复。
PhiNotPi

哦,谢谢,原来只有在的情况下才会发生这种情况print while(<>)。不知道这是一个特殊的情况下,或有一些连贯的逻辑背后,无论是<>在的一部分perlop,也不while部分perlsyn似乎提这种行为。
2013年

1
@sundar while(<>)是一种特殊情况,在perlsyn,I / O运算符中有记录:'当且仅当输入符号是“ while”语句条件内的唯一内容时(即使伪装成“ for(;;)”,)循环),则该值会自动分配给全局变量$ _,破坏之前的值。”
kernigh 2014年

17

尽可能使用Perl的特殊变量,例如:

  • 使用$"代替" "
  • 使用$/代替"\n"

在词法分析器的帮助下,它们的另一个好处是可以保证一个字符的长标识符。这样就可以将其粘贴到其后的关键字,如下所示:print$.for@_

所有特殊变量的列表在此处提供:特殊变量


15

不要使用qw。这浪费了可以更好地使用的两个字符。例如,不要写以下内容。

@i=qw(unique value);

而是使用裸词。

@i=(unique,value);

或者,如果您不能使用裸字,请使用glob语法。

@i=<unique value>;

glob 语法也可以用于产生有趣的效果。

@i=<item{1,2,3}>;

12

使用语句修饰符而不是复合语句。

复合语句倾向于在参数中使用括号,并在块中使用大括号,而语句修饰符则不需要。

相比:

  • $a++,$b++while$n--while($n--){$a++;$b++}
  • chop$,if$cif($c){chop$,}

请注意,最后一个示例与绑定$c&&chop$,,但是对于大多数多语句操作而言,它确实开始发光。基本上所有失去运算符优先级的内容&&


11

不要use strict。(不要在此引用我,PCG.SE上下文还很重要),更重要的是,不要像在严格的条件下那样进行编码。普通嫌疑犯:

  • my如果可以避免的话,请不要声明变量。真正需要的唯一变量my是您希望按词法作用域限定的变量。打高尔夫球时几乎没有这些东西,您不需要示波器保护,而可以完全控制递归。
  • 不要引用单字字符串:(示例)。但是,请确保您没有同名的函数。

5
print hello将无法正常工作。它实际上意味着print hello $_(打印$_到filehandle hello)。
Konrad Borowski 2012年

@GlitchMr谢谢!(现在我很沮丧,因为我的观点仍然有效,只是不适用,print现在我找不到一个简短的例子)
JB 2012年

@JB这是一个很好的例子:codegolf.stackexchange.com/a/8746/3348
ardnew 2012年

11

我确定其中一些具有正式名称,但我只是不知道它们。

  • 如果您有一个while循环(或for循环,可以进入while循环),则可以在命令后定义“ while”: print $n++ while ($n < 10)
  • 如果您需要将所有从STDIN读取为字符串: $var = join('',<>)
  • 正如CeilingSpy指出的那样,在某些情况下,使用$ /代替\ n更快:print ('X'*10) . "\n";print ('X'*10) . $/;
  • Perl的say功能比短print,但您必须使用-E代替-e
  • 使用范围a..z,甚至aa..zz。如果需要作为字符串,请使用join
  • 递增字符串:$z = 'z'; print ++$z;将显示aa

我现在能想到的就是这些。我可能以后再添加一些。


1
什么是print ('X'*10) . $/;应该做的?对我来说,它打印0,没有换行符。一方面,括号成为的函数样式调用参数print,其绑定比紧密.。您是说x而不是*什么意思吗?
aschepler's

postfix中不需要括号whilejoin'',<>;没有它们也可以使用。
choroba

10

使用非单词字符作为变量名

使用$%代替代替$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

9

redo将循环行为添加到不带for或的块中while{redo}是一个无限循环。


7

不要括号函数调用。

Perl使您可以使用NAME LIST语法调用已知(核心或预先声明的)函数。这样,您就可以放下&标记(如果仍在使用)和括号。

例如: $v=join'',<>

完整文件


5

尝试使用赋值表达式的值,如下所示:

# 14 characters
$n=pop;$o=$n&1

# 13 characters, saves 1
$o=($n=pop)&1

之所以有效,$n是因为在Perl中为2个字符。您可以免费更改$n(),并通过将分配移到括号中来保存1个分号。


5

您可以在嵌套三元逻辑中运行多个不同的语句。

假设你有一个大的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)

4

使用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个字节,这在大多数情况下无疑是一个改进。


1

缩短打印语句

除非挑战另有说明,否则您不需要尾随换行符。
我们的“挑战”说“将0到9的随机数输出到STDOUT”。我们可以使用以下代码(28个字节):

$s=int(rand(10));print"$s\n"

并将其缩短为此(25个字节):

$s=int(rand(10));print $s

通过简单地打印变量。最后一个仅专门适用于此挑战(19个字节):

print int(rand(10))

但这仅在您不必在赋值和打印之间对变量执行任何操作时才有效。


最后一个已经在这里列出。
Sp3000

@ Sp3000谢谢,我已经更新了答案。
ASCIIThenANSI

1

使用glob之类的字符串文字

有时候(通常在处理挑战时),您可以从嵌套字符串文字的功能中受益匪浅。通常,您可以使用进行此操作q(…)。但是,根据字符串中需要的字符,您可以保存一个字节并使用<…>glob运算符。(请注意,尖括号内的内容看起来不像文件句柄,并且看起来也不像是要扩展为文件名列表,这意味着很多字符将无法正常工作。)


0

使用表达式regexe。

以下是将输入整形为正弦波的代码:

s/./print" "x(sin($i+=.5)*5+5).$&/eg;

如您所见,这是在标准输入中遍历字符的非常紧凑的方式。您可以使用其他正则表达式来更改事物的匹配方式。

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.