PHP高尔夫技巧


37

您使用PHP打高尔夫球有哪些一般技巧?我正在寻找可以应用于编码高尔夫球问题的想法,这些想法至少在某种程度上特定于PHP(例如,“删除注释”不是答案)。请为每个答案发布一个提示。


等等,我做对了吗?...无论如何,我对此真的很好奇。许多人和高尔夫球手都使用PHP,但是我几乎不知道如何使用PHP代码。
JiminP 2011年

使用短标签<??>可以节省一些字节。
暴徒

Answers:


22

了解变量和空格如何与PHP的语言构造交互。

在我(很短的时间)打高尔夫球中,我发现与变量和空白进行交互时,PHP的语言构造(例如,回声,返回,for,while等)表现得不那么直观。

echo$v;例如,return$v;和其他类似构造一样,它是完全有效的。空格的这些小幅减少会导致长度的显着累积减少。

但是请记住,语言构造之前的变量后面需要空格,如以下示例所示:

foreach($a AS$b){}

因为AS是一种语言构造,所以变量之前不需要空格$b,但是如果要忽略它之前的空格(导致)$aAS,则将其解析为变量名称并导致语法错误。


3
foreach($a[1]as$b)不需要空格。这不是关于语言的构造和变量,而是关于不同单词的单词字符之间的空格。
泰特斯(Titus),2016年

1
您需要空格的另一个实例是字符串连接。例如,echo $a+5." text"将不起作用,因为PHP认为.是的小数点5。要使其正常工作,您需要添加一个这样的空间:echo $a+5 ." text"
Business Cat

@BasicSunset该语句可以写为echo$a+5," text";。该echo构造允许您传递多个参数。echo"result: ".($a+5)."!";你不得不写的地方可以写echo"result: ",$a+5,"!";。实际上,将多个参数传递给echo是一个微优化,因为代码运行的速度会更快一点(因为您无需连接输出,而是将其单独发送)。对于编写最快的代码所遇到的挑战,这可能会有所帮助。
伊斯梅尔·米格尔

@IsmaelMiguel它可与一起使用echo,但不能与print(将其放入表达式中时需要使用:它echo是无返回值的纯结构,同时print 可以充当函数:它不需要括号,但始终返回int(1)。)
Titus

@Titus我什么也没说print
伊斯梅尔·米格尔

22

明智地使用琴弦。

这个答案有两个方面。第一部分是声明字符串时,可以利用PHP将未知常量隐式转换为字符串以节省空间,例如:

@$s=string;

@覆盖,这将产生的警告是必要的。总体而言,您最终只能减少一个字符。

有时,将变量设置为常用函数的名称可能对空间有效。通常,您可能会:

preg_match(..);preg_match(..);

但是打高尔夫球时,可以轻松地将其缩短为:

@$p=preg_match;$p(..);$p(..);

只有两个“ preg_match”实例,您只保存一个字符,但是使用函数的次数越多,节省的空间就越大。


10
在代码高尔夫中不需要@;通知和警告(包括E_DEPRECATED)是可以接受的
Titus

3
@Titus但是在PHP中,警告会输出到标准文件输出中,因此是必需的。
brianush1'1

1
@Titus我相信您可以在php.ini文件中隐藏它们
Stan Strum

12

您不必总是写出条件检查。例如,某些框架在其文件顶部使用此功能来阻止访问:

<?php defined('BASE_PATH')||die('not allowed');

或正常功能

$value && run_this();

代替

if($value) { run_this(); }

它的工作原理也是JS
ЕвгенийНовиков

8

使用短数组语法

从PHP 5.4开始,可以使用方括号(就像JavaScript)而不是array()函数来声明数组:

$arr=['foo','bar','baz'];
// instead of
$arr=array('foo','bar','baz');

它将保存五个字节。


但是,如果在关联数组中有“空洞”,则可能会花费字节:

$arr=array(,1,,3,,5);
// is one byte shorter than
$arr=[1=>1,3=>3,5=>5];

如果您可以使用“空”值填充漏洞,则缺点会在稍后出现:

$arr=[0,1,0,3,0,5,0,7,0,9,10,11];
// costs two byte more than
$arr=array(,1,,3,,5,,7,,9,,11);

2
PHP 7.1还引入了简短列表分配:[,$a,$b,$c]=$argv;
泰特斯

7

使用$ {0},$ {1},$ {2},...代替$ a [0],$ a [1],$ a [2],...

除非您要执行数组操作,否则大多数对数组索引的引用$a[$i]都可以替换为simple $$i。如果索引是整数,甚至是正确的,因为整数在PHP中是有效的变量名称(尽管文字需要括号,例如${0})。

考虑以下Rabonowitz Wagon插口的实现:

3.<?for(;$g?$d=0|($a[$g]=$d*$g--/2+($a[$g]?:2)%$g*1e4)/$g--:238<<printf($e?'%04d':'',$e+$d/$g=1e4)^$e=$d%$g;);

这可以通过6个字节,简单地通过更换两个数组引用加以改进$a[$g]$$g代替:

3.<?for(;$g?$d=0|($$g=$d*$g--/2+($$g?:2)%$g*1e4)/$g--:238<<printf($e?'%04d':'',$e+$d/$g=1e4)^$e=$d%$g;);

1
我只保存了3个字节:Showcase
泰特斯

6

学习库函数的很大一部分

PHP的库非常庞大,并提供了大量方便的功能,可以大大缩短各种任务。您每次尝试做某事时都可以进行搜索,但是浪费时间之外,您可能找不到与您的特定搜索相匹配的内容。最好的方法是熟悉库并记住函数名称及其作用。


6
这要记住很多,尤其是考虑到很多功能的命名不一致时;-)
Joey

@乔伊同意。类似于记住Java库,除了可以说它用起来更冗长之外,可以说用处不大。
马修(Matthew)

3
我发现到目前为止,我遇到的挑战中最重要的功能是字符串操作和数组操作功能。创造性地使用它们可以真正减少代码。
migimaru 2011年

6

在字符串中运行函数。

尝试这个:

$a='strlen';
echo "This text has {$a('15')} chars";

或尝试以下方法:

//only php>=5.3
$if=function($c,$t,$f){return$c?$t:$f;};
echo <<<HEREDOCS
    Heredocs can{$if(true,' be','not be')} used too and can{$if(<<<BE
{$if(true,1,0)}
BE
,'','not')} be nested
HEREDOCS;
//Expected output: Heredocs can be used too and can be nested

这仅适用于使用""和heredocs的字符串(不要与nowdocs混淆)。

仅在嵌套的heredocs中可以使用嵌套函数(否则您将遇到解析错误)!


you will run into parse errors我自己看不懂吗?令人讨厌的Zend引擎如何将它们组合在一起
Stan Strum,

下次我遇到“ PHP是一种好的编程语言”的论点时,我将使用它作为对点。哇。
primo

@primo不好吗?:O
Ismael Miguel,

5

打字的乐趣

  • !!$foo会将任何真实值转换为true(或1在输出中),将虚假值(0,空字符串,空数组)转换为false(或为空输出)
    这在代码高尔夫中很少需要,在大多数情况下,您需要布尔值,无论如何都隐式转换。

  • (int)$foo可以写为$foo|0foo^0,但可能需要括号。
    用于布尔值和字符串,$foo*1+$foo可用于强制转换为int。

  • 与大多数其他语言不同,PHP将数字值作为数字的字符串进行处理。因此,如果您有任何包含要计算的数字的字符串,则只需计算即可。
  • 另一种方法做工作:要在乘用可变任意数量10,你可以追加一个零:*10- > .0。但是在这种情况下,PHP会将点作为小数点并抱怨。(但是,如果字符串中的可变数量为零,则情况有所不同。)
  • 要将数组转换为字符串,请使用join代替implode
    如果不需要定界符,请不要使用它:join($a)join('',$a)
  • 递增字符串:imo最神奇的功能是$s=a;$s++;产生$s=b;。这适用于大写和小写字符。$s=Z;$s++;结果$s=AA;
    这也适用于混合大小写:aZto bAA1to A2A9to B0z99Zto aa00A
    减量并没有对字符串工作。(并且它没有打开NULL)。
    回到PHP 3,$n="001";$n++;产生了$n="002";。我有点难过,他们将其删除。

无论您打什么高尔夫:始终要准备好操作员优先表


4

使用短标签

在普通代码中,使用<?php和是一个好习惯?>。但是,这不是正常的代码-您正在编写代码高尔夫球代码。相反<?php,写<?。相反<?php echo,写<?=。不要?>在最后输入-这是完全可选的。如果您?>出于某种原因(例如,输出文本,并且以某种方式变短了),请不要在其前加上分号-不需要,这?>意味着分号。

错误(肯定太长):

<?php echo ucfirst(trim(fgets(STDIN)));?>s!

正确:

<?=ucfirst(trim(fgets(STDIN)))?>s!

使用-r标志(免费提供),您甚至根本没有任何标签(并且不允许使用任何标签)。
泰特斯

4

遍历字符串

可以用26个字节或24个字节(最多18个字节)完成:

foreach(str_split($s)as$c)  # A) 26 - general
for($p=0;a&$c=$s[$p++];)    # B) 24 - general
for($p=0;$c=$s[$p++];)      # C) 22 - if $s has no `0` character
for(;a&$c=$s[$p++];)        # D) 20 - if $p is already NULL or 0 (does NOT work for false)
for(;$c=$s[$p++];)          # E) 18 - both C and D

for(;$o=ord($s[$p++]);)     # F) 23 - work on ASCII codes, if $s has no NULL byte and D
for(;~$c=$s[$p++];)         # G) 19 - if $s has no chr(207) and D

$a&$b执行按位与第(ASCII码)中的字符$a$b
在具有相同长度的较短的字符串和结果$a$b


可以请你加ord($s[$p++])为替代for(;$s+=ord($argv[++$i])%32?:die($s==100););反对for(;$c=$argv[++$i];)$s+=ord($c)%32;echo$s==100;在这个问题codegolf.stackexchange.com/questions/116933/...
约尔格Hülsermann

请添加~为你工作只与数字的情况下
约尔格Hülsermann

请注意,PHP 7.2会对该~$c方法产生警告。
泰特斯

4

使用三元运算符

if(a==2){some code;}else{some other code;}

可以缩写为:

(a==2?some code:some other code);

矮一点吧?


“条件速记”?最好说出它的真实名称,以便对更多详细信息感兴趣的人可以在文档中找到它:三元运算符
manatwork 2014年

3
该问题要求提供一些特定于PHP的技巧。这是所有语言提示中包含的一种
彼得·泰勒

3
如果将其嵌套,则三元运算符在PHP中具有怪异的行为。a?aa:ab?aba:abb:b评估(a?aa:ab)?(aba):(abb)或类似的东西。
泰特斯(Titus)

1
从PHP 5.3开始,您可以省略第二个运算符:$a?:$b与相同$a?$a:$b
泰特斯(Titus)

1
@Cyoce ||在PHP中转换为布尔值。
泰特斯

3

用任何其他名称...函数别名

使用 ...

  • join 代替 implode
  • chop而不是rtrimchop在PERL中有所不同!)
  • die 代替 exit
  • fputs 代替 fwrite
  • is_int代替is_integeris_long
  • is_real代替is_floatis_double
  • key_exists 代替 array_key_exists
  • mysql 代替 mysql_db_query

...命名最重要的别名。进一步了解http://php.net/aliases


哦...,您知道die使用和不使用参数的情况吗?die(1)将以错误代码退出程序1(对此不完全确定;需要测试);die将以代码退出0,并在打印后die("Hello")以代码退出。0Hello
泰特斯(Titus)

3

关联数组可以与+运算符合并。

代替:

$merged = array_merge($a, $b);

使用:

$merged = $a + $b;

请注意,该+运算符也可以使用索引数组,但是可能不会执行您想要的操作。


确实,虽然不是完全一样,但通常是一个很好的替代品:pastebin.com/seYeaP38
manatwork

嗯,对了,我原来的标题是“ associative arrays ...”,然后将其删除。我会澄清,谢谢。
Alex Howansky

+只要索引不同,数字数组也可以使用合并。如果不是,则第一个数组中的值将被第二个数组中的值覆盖(就像array_merge一样)。区别:+不对索引重新排序。
泰特斯

3

array_flip与array_search

使用

array_flip($array)[$value]

代替

array_search($value,$array)

在每个值的出现唯一的数组中保存1个字节


3

关于变量的一些有趣事实

我只需要分享它们(甚至在我确认其中至少有一个有助于打高尔夫球之前):

  • 使用字母:$x=a;$$x=1;$x++;$$x=2;echo"$a,$b";打印1,2
    但其他算术运算不适用于字母。
  • 由于普里莫前面提到的,你可以使用纯数字作为变量名:
    $a=1;$$a=5;$a++;$$a=4;${++$a}=3;echo${1},${2},${3};打印543
  • 您不仅可以使用[0-9a-zA-Z_]变量名,还可以使用每个字符串:
    $x="Hello!";$$x="Goodbye.";echo${"Hello!"};prints Goodbye.
  • 但是:除了[a-zA-Z_][a-zA-Z_0-9]*变量名以外的所有内容都需要大括号以用于原义使用。
  • 未定义变量$$x=1set ${NULL},与${false}和相同${""}
  • $a=1;$$a=5;不仅设置${1},而且${true}

  • 还有一个,是我到目前为止发现的最奇怪的一个:试试看$a=[];$$a=3;echo${[]};。是的,它会打印3

造成这种情况的主要原因是:变量名始终被评估为字符串。
(感谢@Christoph指出。)
因此,无论您得到什么表达式printecho表达式,无论得到什么,这就是变量名。


1
变量名称将转换为解释列表中最后三点的字符串。[]转换为Array${[]} = 5;echo $Array;印刷品5。我敢肯定,您知道这一点,但可能并非所有人都知道:)
Christoph

@Jeff我修正了错字。感谢您的关注。
泰特斯(Titus),

2

换行符
如果输出需要换行符,请使用物理换行符(1字节)代替。"\n"
这也使您有可能在单引号和双引号之间进行选择。


2

尽可能避免引用

PHP将未知单词隐式转换为文字字符串。

$foo=foo;$foo='foo';(假定foo既不是关键字也不是定义的常数)相同:$foo=echo;不起作用。

但是:$p=str_pad;确实;并$p(ab,3,c)计算为abc

使用不带引号的字符串文字会产生的通知Use of undefined constant;但是,如果您使用error_reporting(CLI参数-n)的默认值,则不会显示。


我刚刚注意到:这个答案是codegolf.stackexchange.com/a/2916/55735的某种扩展/更新的副本。
泰特斯

注意:7.2之前的PHP产生了Notices(您可以通过-n标记来压制);7.2产生警告;更高版本将引发错误!
泰特斯

2

PHP 7.4中的箭头功能

PHP 7.4现在是RC2版本,希望大约2个月后发布。新功能列表在此处(此版本在7.4发布时实际上可以更新)。在7.4中,PHP终于有了箭头函数,因此,不仅现在函数答案可以更短,而且将闭包传递给其他函数也可以更短。这里有一些例子:

返回输入+ 1:

匿名函数(关闭)-25个字节- 在线尝试!

function($n){return$n+1;}

箭头功能-12个字节- 在线尝试!

fn($n)=>$n+1

将第一个输入项(整数数组)乘以第二个输入项(整数):

匿名函数(关闭)-72个字节- 在线尝试!

function($a,$n){return array_map(function($b)use($n){return$b*$n;},$a);}

箭头功能-38个字节- 在线尝试!

fn($a,$n)=>array_map(fn($b)=>$b*$n,$a)

您是否注意到没有声明$n就可以在内部函数中访问它use $n?是的,这是箭头功能之一。


附带说明一下,我无法让箭头函数递归工作(在内部调用相同的箭头函数),因为我们无法给它们命名,也无法将它们作为闭包存储在变量中,就像$f无法$f随身访问(悲伤) )。因此,此示例不起作用$f在第一行中使用会导致致命错误:

$f=fn($n)=>$n?$f($n-1):0;
$f(5); // Causes error: "PHP Notice: Undefined variable: f" + "PHP Fatal error: Uncaught Error: Function name must be a string"

但是,调用带有不同箭头功能的箭头功能可以:

$f1=fn($n)=>$n+1;
$f2=fn($n)=>$f1($n-1);
$f1(2) // Returns 3
$f2(2) // Returns 2

如果代替$f=fn($n)=>$n?$f($n-1):0;您怎么办$f=$F=fn($n)=>$n?$F($n-1):0;?那行得通吗?然后您$(5)照常打电话。
Ismael Miguel

@IsmaelMiguel似乎仍然抛出相同的错误。实际上,您可以自己尝试tio.run#php,因为Dennis 不久前已将其PHP更新到7.4 RC2。
2

无法正常工作。似乎只有之前定义的变量可用。
Ismael Miguel


1

直接取消引用从函数返回的数组。

例如,代替此:

$a = foo();
echo $a[$n];

你可以做:

echo foo()[$n];

这也适用于方法:

echo $obj->foo()[$n];

您还可以直接取消引用数组声明:

echo [1, 2, 3, 4, 5][$n];

1

使用end()代替array_pop()

end()函数不仅将内部指针移动到数组的末尾,还返回最后一个值。当然请注意,它不会删除该值,因此,如果您以后不关心数组包含的内容,则可以使用它代替array_pop()


1

double array_flip与in_array与array_unique

在这种特殊情况下,double array_flip可以节省10个字节

($f=array_flip)($k=$f($c)))删除数组中的所有double值,我删除了它$c=[],|in_array($o,$c)并替换array_keys($c)$k

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)
$x[$i]==$o?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo strtr($x,($f=array_flip)($k=$f($c)))==$y # boolean replacement string 1 equal to string 2
    ?join($k)." ".join($c) # output for true cases
:0; #Output false cases

在线版本

反对

for($c=[],[,$x,$y]=$argv;a&$o=$y[$i];$i++)
  $x[$i]==$o|in_array($o,$c)?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo strtr($x,$c)==$y # boolean replacement string 1 equal to string 2
  ?join(array_keys($c))." ".join($c) # output for true cases
  :0; #Output false cases

在线版

针对array_unique节省2个字节

for([,$x,$y]=$argv;a&$o=$y[$i];$i++)
  $x[$i]==$o?:$c[$x[$i]]=$o; # if char string 1 not equal char string 2 set key=char1 value=char2
echo strtr($x,array_unique($c))==$y # boolean replacement string 1 equal to string 2
  ?join(array_keys($c))." ".join($c) # output for true cases
  :0; #Output false cases

在线版本

在此程序中找到错误并替换$x[$i]==$o?:$c[$x[$i]]=$o($p=$x[$i])==$o?:$k[$c[$p]=$o]=$pdouble array_flip之后,不再需要


关联安全array_unique。好极了!
泰特斯

@Titus我有加你的建议
约尔格Hülsermann

1

相交的字符串

您曾经用过
join("DELIMITER",str_split($s))(31个字节)甚至
preg_replace(".","DELIMITER",$s)(32个字节)
吗?

有一个内置的:

尝试chunk_split($s,1,"DELIMITER")(29个字节)。


如果省略第三个参数,chunk_split将使用\r\n; 可以节省7或8个字节。

但是要当心:chunk_split还将定界符追加到字符串中,
因此您可能无法完全获得所需的内容。

(如果您不提供块长度,则将使用76。对于代码高尔夫来说,这是很不常见的,但谁知道。)


也许您应该结合strtr我喜欢的想法添加一个示例。
—JörgHülsermann17年

1

unset()与INF

在某种情况下,搜索数组中的最小值可以代替

unset($var[$k]);

$var[$k]=INF;

保存3个字节


1

str_repeat

在某些情况下,您需要输入字符,因此您应该重复输出这些字符,每个字符的输入应大于零。

for(;--$z?:($c=$argn[$i++]).$z=$argn[$i++];)echo$c;

(52个字节)短于

for(;~$c=$argn[$i++];)echo str_repeat($c,$argn[$i++]);

要么

for(;~$c=$argn[$i++];)echo str_pad($c,$argn[$i++],$c);

(每个54字节)

例如输入如何工作 a1b2c1

$z没有设置(隐式NULL),所以--$z什么也没有做,而且是虚假的;

$c="a"$z="1"并且$i=2-> $c.$z="a1"是真实的->输出"a"

--$z=0; 所以我们设置$c="b"$z="2"(和$i=4)-> $c.$z="b2"是真实的->输出"ab"

--$z=1 ->输出 "abb"

--$z=0; 所以我们设置$c="c"$z=1 $c.$z="c1"是真实的输出"abbc"

--$z=0所以$c=""$z=""- > $c.$z=""是falsy - >环断裂


1

结合 for循环

假设您具有以下形式的代码:

for($pre1; $cond1; $post1) for($pre2; $cond2; $post2) $code;

通常可以按以下形式重新滚动:

for($pre1; $cond2  $post2 || $cond1  $pre2  $post1; ) $code;

其中表示通用组合运算符。这通常会导致字节数减少,但可能需要一些创造力。$cond2将需要编写,以便第一次失败。$post1也应该第一次执行失败,尽管事前重构可能会更容易,$post1不存在。

如果使用三个或更多嵌套循环,则也可以先组合两个,然后再组合到另一个,依此类推。我发现从内部向外组合通常比较容易。


例如,请考虑以下解决方案:H形地毯的分形97个字节):

for(;$i<$n=3**$argn;$i+=print"$s\n")for($s=H,$e=1;$e<$n;$e*=3)$s.=str_pad($i/$e%3&1?$s:'',$e).$s;

可以通过以下方式重新制定:

for(;($i+=$e&&print"$s\n")<$n=3**$argn;)for($s=H,$e=1;$e<$n;$e*=3)$s.=str_pad($i/$e%3&1?$s:'',$e).$s;

$e&&print防止print第一次迭代,也不会递增$i

最后(93个字节):

for(;$H>$e*=3or$e=($i+=$e&&print"$s\n")<${$s=H}=3**$argn;)$s.=str_pad($i/$e%3&1?$s:'',$e).$s;

$H>$e*=3 由于两个变量均未定义,因此将首次失败。


1

删除字符串中的字符

join(explode(" ",$string));

比起节省1个字符

str_replace(" ","",$string);

请注意,这适用于所有(非空)字符串,而不仅仅是字符。
CalculatorFeline

@CalculatorFeline为什么它不能用于空字符串?没有道理或这种情况。
约尔格Hülsermann

好吧,第一个版本无法使用"",反正不是很有用。
CalculatorFeline

1
@CalculatorFeline在这种情况下,零字节解决方案要好得多。这样做是没有意义的。
约尔格Hülsermann

3
您的加入示例缺少)。而且strtr($string,[" "=>""])更短。
泰特斯


1

使用布尔运算符代替strtoupper()strtolower()

如果仅使用由字母字符组成的字符串,则可以使用布尔运算符以比PHP内置函数少的键击将它们更改为大写或小写。

例:

// Convert lowercase to uppercase
$s = "g";
echo strtoupper($s);  // Outputs 'G', uses 20 characters
echo~" "&$s;          // Outputs 'G', uses 12 characters

// Convert uppercase to lowercase
$s = "G";
echo strtolower($s);  // Outputs 'g', uses 20 characters
echo$s^" ";           // Outputs 'g', uses 11 characters

// Switch case of each character
$s = "Gg";
echo$s^"  ";          // Outputs 'gG', uses 12 characters

对于任意长度的字符串而言,事情有些棘手,但是&and ^运算符会将结果截断为较短输入字符串的长度。因此,例如,如果if $W是一串至少与任何input一样长的空格$s,则~$W&$s等效于strtoupper($s)$s|$W^$s等效strtolower($s)(而$s|$W它本身将产生带有附加空格的字符串,除非$s$W长度相等)。


0

使用不推荐使用的函数
如果可以使用POSIX代替PERL正则表达式而不会在表达式上浪费5个以上的字节,请使用eregeregi代替preg_matchsplit或者spliti代替preg_split。
split也可以用作的同义词explode大多数定界符。

这些功能被标记为已弃用,并且会引发E_DEPRECATED通知,但是(现在找不到源)我想我已经读过警告和通知是可以的。


2
小心!这些已在PHP 7.0中删除。
Umbrella
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.