Answers:
了解变量和空格如何与PHP的语言构造交互。
在我(很短的时间)打高尔夫球中,我发现与变量和空白进行交互时,PHP的语言构造(例如,回声,返回,for,while等)表现得不那么直观。
echo$v;
例如,return$v;
和其他类似构造一样,它是完全有效的。空格的这些小幅减少会导致长度的显着累积减少。
但是请记住,在语言构造之前的变量在后面需要空格,如以下示例所示:
foreach($a AS$b){}
因为AS
是一种语言构造,所以变量之前不需要空格$b
,但是如果要忽略它之前的空格(导致)$aAS
,则将其解析为变量名称并导致语法错误。
foreach($a[1]as$b)
不需要空格。这不是关于语言的构造和变量,而是关于不同单词的单词字符之间的空格。
echo $a+5." text"
将不起作用,因为PHP认为.
是的小数点5
。要使其正常工作,您需要添加一个这样的空间:echo $a+5 ." text"
echo$a+5," text";
。该echo
构造允许您传递多个参数。echo"result: ".($a+5)."!";
你不得不写的地方可以写echo"result: ",$a+5,"!";
。实际上,将多个参数传递给echo
是一个微优化,因为代码运行的速度会更快一点(因为您无需连接输出,而是将其单独发送)。对于编写最快的代码所遇到的挑战,这可能会有所帮助。
echo
,但不能与print
(将其放入表达式中时需要使用:它echo
是无返回值的纯结构,同时print
可以充当函数:它不需要括号,但始终返回int(1)
。)
print
。
明智地使用琴弦。
这个答案有两个方面。第一部分是声明字符串时,可以利用PHP将未知常量隐式转换为字符串以节省空间,例如:
@$s=string;
该@
覆盖,这将产生的警告是必要的。总体而言,您最终只能减少一个字符。
有时,将变量设置为常用函数的名称可能对空间有效。通常,您可能会:
preg_match(..);preg_match(..);
但是打高尔夫球时,可以轻松地将其缩短为:
@$p=preg_match;$p(..);$p(..);
只有两个“ preg_match”实例,您只保存一个字符,但是使用函数的次数越多,节省的空间就越大。
E_DEPRECATED
)是可以接受的
php.ini
文件中隐藏它们
您不必总是写出条件检查。例如,某些框架在其文件顶部使用此功能来阻止访问:
<?php defined('BASE_PATH')||die('not allowed');
或正常功能
$value && run_this();
代替
if($value) { run_this(); }
从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);
[,$a,$b,$c]=$argv;
。
除非您要执行数组操作,否则大多数对数组索引的引用$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;);
学习库函数的很大一部分。
PHP的库非常庞大,并提供了大量方便的功能,可以大大缩短各种任务。您每次尝试做某事时都可以进行搜索,但是浪费时间之外,您可能找不到与您的特定搜索相匹配的内容。最好的方法是熟悉库并记住函数名称及其作用。
在字符串中运行函数。
尝试这个:
$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引擎如何将它们组合在一起
!!$foo
会将任何真实值转换为true
(或1
在输出中),将虚假值(0,空字符串,空数组)转换为false
(或为空输出)
这在代码高尔夫中很少需要,在大多数情况下,您需要布尔值,无论如何都隐式转换。
(int)$foo
可以写为$foo|0
或foo^0
,但可能需要括号。
用于布尔值和字符串,$foo*1
或+$foo
可用于强制转换为int。
10
,你可以追加一个零:*10
- > .0
。但是在这种情况下,PHP会将点作为小数点并抱怨。(但是,如果字符串中的可变数量为零,则情况有所不同。)join
代替implode
。join($a)
与join('',$a)
$s=a;$s++;
产生$s=b;
。这适用于大写和小写字符。$s=Z;$s++;
结果$s=AA;
。aZ
to bA
,A1
to A2
,A9
to B0
和z99Z
to aa00A
。NULL
)。$n="001";$n++;
产生了$n="002";
。我有点难过,他们将其删除。无论您打什么高尔夫:始终要准备好操作员优先表。
在普通代码中,使用<?php
和是一个好习惯?>
。但是,这不是正常的代码-您正在编写代码高尔夫球代码。相反<?php
,写<?
。相反<?php echo
,写<?=
。不要?>
在最后输入-这是完全可选的。如果您?>
出于某种原因(例如,输出文本,并且以某种方式变短了),请不要在其前加上分号-不需要,这?>
意味着分号。
错误(肯定太长):
<?php echo ucfirst(trim(fgets(STDIN)));?>s!
正确:
<?=ucfirst(trim(fgets(STDIN)))?>s!
遍历字符串
可以用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/...
~
为你工作只与数字的情况下
~$c
方法产生警告。
if(a==2){some code;}else{some other code;}
可以缩写为:
(a==2?some code:some other code);
矮一点吧?
a?aa:ab?aba:abb:b
评估(a?aa:ab)?(aba):(abb)
或类似的东西。
$a?:$b
与相同$a?$a:$b
。
||
在PHP中转换为布尔值。
使用 ...
join
代替 implode
chop
而不是rtrim
(chop
在PERL中有所不同!)die
代替 exit
fputs
代替 fwrite
is_int
代替is_integer
或is_long
is_real
代替is_float
或is_double
key_exists
代替 array_key_exists
mysql
代替 mysql_db_query
...命名最重要的别名。进一步了解http://php.net/aliases。
die
使用和不使用参数的情况吗?die(1)
将以错误代码退出程序1
(对此不完全确定;需要测试);die
将以代码退出0
,并在打印后die("Hello")
以代码退出。0
Hello
+
运算符合并。代替:
$merged = array_merge($a, $b);
使用:
$merged = $a + $b;
请注意,该+
运算符也可以使用索引数组,但是可能不会执行您想要的操作。
+
只要索引不同,数字数组也可以使用合并。如果不是,则第一个数组中的值将被第二个数组中的值覆盖(就像array_merge一样)。区别:+
不对索引重新排序。
关于变量的一些有趣事实
我只需要分享它们(甚至在我确认其中至少有一个有助于打高尔夫球之前):
$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=1
set ${NULL}
,与${false}
和相同${""}
。 $a=1;$$a=5;
不仅设置${1}
,而且${true}
。
还有一个,是我到目前为止发现的最奇怪的一个:试试看$a=[];$$a=3;echo${[]};
。是的,它会打印3
!
造成这种情况的主要原因是:变量名始终被评估为字符串。
(感谢@Christoph指出。)
因此,无论您得到什么表达式print
或echo
表达式,无论得到什么,这就是变量名。
[]
转换为Array
:${[]} = 5;echo $Array;
印刷品5
。我敢肯定,您知道这一点,但可能并非所有人都知道:)
尽可能避免引用
PHP将未知单词隐式转换为文字字符串。
$foo=foo;
与$foo='foo';
(假定foo
既不是关键字也不是定义的常数)相同:$foo=echo;
不起作用。
但是:$p=str_pad;
确实;并$p(ab,3,c)
计算为abc
。
使用不带引号的字符串文字会产生的通知Use of undefined constant
;但是,如果您使用error_reporting
(CLI参数-n
)的默认值,则不会显示。
-n
标记来压制);7.2产生警告;更高版本将引发错误!
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)
照常打电话。
在这种特殊情况下,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]=$p
double array_flip之后,不再需要
array_unique
。好极了!
相交的字符串
您曾经用过
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
我喜欢的想法添加一个示例。
在某些情况下,您需要输入字符,因此您应该重复输出这些字符,每个字符的输入应大于零。
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 - >环断裂
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
由于两个变量均未定义,因此将首次失败。
join(explode(" ",$string));
比起节省1个字符
str_replace(" ","",$string);
""
,反正不是很有用。
)
。而且strtr($string,[" "=>""])
更短。
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
长度相等)。