Câu trả lời:
Đó là mẹo chơi gôn Perl quan trọng nhất mà bạn cần biết. Bất cứ khi nào bạn nhìn vào một chuỗi các ký tự quá dài mà bạn hoàn toàn phải có để hoàn thành nhiệm vụ của mình, hãy tự hỏi nếu không có cách nào khác để có được hiệu ứng tương tự bằng cách sử dụng một tính năng khác. Thường có. Đây chỉ là một số ít:
~~
thực thi một bối cảnh vô hướng và ngắn hơn 4 ký tự scalar
.
y///c
là một char ngắn hơn so với length
khi nhận được chiều dài $_
.
Cần lặp đi lặp lại trong ký tự trong $_
? Thay thế split//
bằng /./gs
. (Hoặc sử dụng /./g
nếu bạn cũng muốn bỏ qua dòng mới.) Điều này hoạt động với các biến khác: thay thế split//,$x
bằng $x=~/./gs
.
Mỗi Perin dựng sẵn trả lại một cái gì đó. print
trả về 1, ví dụ, để chỉ ra I / O thành công. $_
Ví dụ, nếu bạn cần khởi tạo một giá trị thực, cho $_=print$foo
phép bạn giết hai con chim bằng một hòn đá.
Hầu như mọi câu lệnh trong Perl đều có thể được viết dưới dạng một biểu thức, cho phép nó được sử dụng trong nhiều ngữ cảnh khác nhau. Nhiều câu lệnh có thể trở thành nhiều biểu thức được xâu chuỗi cùng với dấu phẩy. Các thử nghiệm có thể được thực hiện với các toán tử ngắn mạch ?:
&&
||
, và cũng với and
và or
, thực hiện điều tương tự nhưng có độ ưu tiên thấp hơn tất cả các toán tử khác (bao gồm cả phép gán). Vòng lặp có thể được thực hiện thông qua map
hoặc grep
. Ngay cả các từ khóa như next
, last
và return
có thể được sử dụng trong một bối cảnh biểu hiện, mặc dù họ không trở lại! Giữ các loại biến đổi này trong tâm trí cho bạn cơ hội để thay thế các khối mã bằng các biểu thức có thể được nhồi vào nhiều ngữ cảnh khác nhau.
$foo
. Mặt khác, $_=1
ngắn hơn nhiều $_=print""
và có tác dụng tương tự.
$x
? Nếu không bạn chỉ có thể làm /./gs
và /./g
.
Lạm dụng các biến đặc biệt của Perl!
Như đã lưu ý trong một câu trả lời trước đó $/
và $"
được khởi tạo theo mặc định "\n"
và " "
, tương ứng.
$,
và $\
cả hai đều được đặt thành undef
mặc định và ngắn hơn 3 ký tự.
Đặt $\
thành một giá trị sẽ khiến nó được gắn vào mỗi print
. Ví dụ: perl -ple '$\="=".hex.$/'
là một công cụ chuyển đổi hex sang thập phân tiện dụng.
Nếu bạn không đọc tệp từ dòng lệnh, bạn có thể sử dụng -i
công tắc dòng lệnh làm kênh bổ sung để nhập chuỗi. Giá trị của nó sẽ được lưu trữ trong $^I
.
$=
buộc bất cứ điều gì được gán cho nó là một số nguyên. Hãy thử chạy perl -ple '$_=$==$_'
và cung cấp cho nó nhiều lần khởi động. Tương tự, $-
buộc giá trị của nó là một số nguyên không âm (nghĩa là một dấu gạch đầu dòng được coi là một ký tự không phải là số).
Bạn có thể sử dụng $.
như một cờ boolean được tự động đặt lại thành giá trị thực (không khác) trên mỗi lần lặp của while(<>)
vòng lặp.
-n
và dấu ngoặc nhọn chưa từng cóNgười ta biết rằng chuyển đổi dòng lệnh -n
có thể được sử dụng để thực thi tập lệnh một lần cho mỗi dòng.
perl --help
nói:
-n assume "while (<>) { ... }" loop around program
Những gì nó không nói rõ ràng là Perl không chỉ giả định một vòng lặp xung quanh chương trình; nó thực sự bao bọc while (<>) { ... }
xung quanh nó.
Theo cách này, các lệnh sau tương đương với nhau:
perl -e 'while(<>){code}morecode'
perl -ne 'code;END{morecode}'
perl -ne 'code}{morecode'
-p
và dấu ngoặc nhọn chưa từng cóTương tự như trên, công -p
tắc kết thúc tốt đẹp while (<>) { ... ; print }
xung quanh chương trình.
Bằng cách sử dụng dấu ngoặc nhọn chưa từng có, perl -p 'code}{morecode'
sẽ chỉ in một lần sau khi thực hiện code
cho tất cả các dòng đầu vào, theo sau morecode
.
Vì $_
không được xác định khi morecode;print
được thực thi, bộ tách bản ghi đầu ra $\
có thể bị lạm dụng để in đầu ra thực tế.
Ví dụ
perl -pe '$\+=$_}{'
đọc một số trên mỗi dòng từ STDIN và in tổng của chúng.
#!perl -n
dòng đầu tiên, phải không?
}for(...){
vào giữa các niềng răng cũng thường khá tiện dụng, ví dụ codegolf.stackexchange.com/a/25632
Sử dụng $_
để loại bỏ các tham chiếu vô hướng. Đây là biến đặc biệt được sử dụng làm mặc định bởi hầu hết các hàm và chỉ cần bỏ đi các tham số là một phím tắt để tham chiếu biến này.
Bằng cách thay đổi $n
thành $_
, bạn có thể thay đổi $n=<>;chop$n;print$n
thành$_=<>;chop;print
Ở đây, print
chức năng in các nội dung $_
theo mặc định, và chop
cũng hoạt động trên $_
.
$_=<>;
yêu cầu, không <>;
đọc dòng $_
tự động?
$_=<>;print
và <>;print
. Cái đầu tiên lặp lại cho tôi những gì tôi gõ, trong khi cái còn lại thì không.
print while(<>)
. Không chắc chắn nếu đó là một trường hợp đặc biệt hoặc có một số logic mạch lạc đằng sau nó, không phải <>
là một phần trong perlop
và cũng không phải while
là một phần của perlsyn
hành vi này.
while(<>)
là trường hợp đặc biệt, được ghi lại bằng perlsyn, Toán tử I / O: 'Nếu và chỉ khi ký hiệu đầu vào là thứ duy nhất trong điều kiện của câu lệnh "while" (ngay cả khi được ngụy trang thành "for (;;)" vòng lặp), giá trị được tự động gán cho biến toàn cầu $ _, phá hủy mọi thứ đã có trước đó. "
Sử dụng các biến đặc biệt của Perl bất cứ khi nào bạn có thể, ví dụ:
$"
thay vì" "
$/
thay vì"\n"
Họ có thêm lợi ích là một định danh dài một ký tự được bảo đảm, với sự giúp đỡ từ nhà từ vựng. Điều này làm cho nó có thể dán nó vào từ khóa theo sau nó, như trong:print$.for@_
Danh sách tất cả các biến đặc biệt có sẵn ở đây: Biến đặc biệt
Đừng sử dụng qw
. Đây là sự lãng phí của hai nhân vật có thể được sử dụng theo cách tốt hơn. Ví dụ, đừng viết như sau.
@i=qw(unique value);
Thay vào đó hãy sử dụng barewords.
@i=(unique,value);
Hoặc nếu bạn không thể sử dụng barewords, hãy sử dụng glob
cú pháp.
@i=<unique value>;
glob
cú pháp cũng có thể được sử dụng cho các hiệu ứng thú vị.
@i=<item{1,2,3}>;
Sử dụng công cụ sửa đổi câu lệnh thay vì câu lệnh ghép.
Các câu lệnh ghép có xu hướng yêu cầu dấu ngoặc đơn cho đối số và dấu ngoặc cho khối, trong khi đó, bộ sửa đổi câu lệnh không cần.
So sánh:
$a++,$b++while$n--
đấu với while($n--){$a++;$b++}
chop$,if$c
đấu với if($c){chop$,}
Lưu ý rằng ví dụ cuối cùng liên kết với $c&&chop$,
, nhưng bắt đầu thực sự tỏa sáng cho hầu hết các hoạt động đa câu lệnh. Về cơ bản bất cứ điều gì mất quyền ưu tiên nhà điều hành &&
.
Đừng use strict
. (đừng trích dẫn tôi về vấn đề này, PCG.SE bối cảnh có vấn đề) Và, quan trọng hơn, đừng viết mã như thể dưới sự nghiêm ngặt. Các nghi phạm thông thường:
my
biến -declare nếu bạn có thể tránh nó. Các biến duy nhất thực sự cần my
là những biến bạn muốn theo phạm vi từ vựng. Đó là hầu như không có ai trong số họ khi chơi golf, nơi bạn không cần bảo vệ phạm vi và có xu hướng kiểm soát hoàn toàn đệ quy.print hello
sẽ không làm việc Nó thực sự có nghĩa là print hello $_
(in $_
ra filehandle hello
).
print
, và bây giờ tôi không thể tìm thấy một ví dụ hay và ngắn)
Tôi chắc rằng một số trong số này có tên chính thức và tôi chỉ không biết về chúng.
print $n++ while ($n < 10)
$var = join('',<>)
print ('X'*10) . "\n";
dài hơnprint ('X'*10) . $/;
say
Chức năng của Perl ngắn hơn print
, nhưng bạn sẽ phải chạy mã -E
thay vì-e
a..z
hoặc thậm chí aa..zz
. Nếu cần như một chuỗi, sử dụng join
.$z = 'z'; print ++$z;
sẽ hiển thịaa
Đó là tất cả những gì tôi có thể nghĩ ra ngay bây giờ. Tôi có thể thêm một số sau.
print ('X'*10) . $/;
làm gì đây? Đối với tôi nó in 0
và không có dòng mới. Đối với một điều, các dấu ngoặc đơn trở thành một đối số cuộc gọi kiểu hàm print
, liên kết chặt chẽ hơn .
. Và bạn có nghĩa là x
thay vì *
hoặc một cái gì đó?
while
, join'',<>;
cũng hoạt động mà không có chúng.
Sử dụng $%
thay vì $a
có thể cho phép bạn đặt tên biến ngay bên cạnh một if
, for
hoặc while
xây dựng như trong:
@r=(1,2,3,4,5);$%=4;
print$_*$%for@r
Nhiều thứ có thể được sử dụng, nhưng hãy kiểm tra tài liệu và câu trả lời của @ BreadBox xem cái nào có hiệu ứng ma thuật!
Nếu bạn không thể sử dụng các bộ điều chỉnh câu lệnh theo câu trả lời của @ JB , bản đồ có thể lưu một byte:
for(@c){}
so với map{}@c;
và rất hữu ích nếu bạn muốn thực hiện các lần lặp lồng nhau vì bạn có thể đặt for
các vòng lặp postfix bên trong map
.
Perl có các biến ma thuật cho 'văn bản trước khi khớp' và 'văn bản sau khi khớp' để có thể chia thành các nhóm có hai ký tự có khả năng ít hơn:
($x,$y)=split/,/,$_;
($x,$y)=/(.+),(.+)/;
/,/; # $x=$`, $y=$'
# Note: you will need to save the variables if you'll be using more regex matches!
Điều này cũng có thể hoạt động tốt như là một thay thế cho substr
:
$s=substr$_,1;
/./;# $s=$'
$s=substr$_,4;
/.{4}/;# $s=$'
Nếu bạn cần nội dung của trận đấu, $&
có thể được sử dụng, ví dụ:
# assume input like '10 PRINT "Hello, World!"'
($n,$c,$x)=split/ /,$_;
/ .+ /; # $n=$`, $c=$&, $x=$'
Nếu bạn gọi nói print
bốn hoặc nhiều lần trong mã của bạn (điều này rõ ràng thay đổi theo độ dài của thói quen bạn gọi), hãy thay thế bằng tên phụ ngắn hơn:
sub p{print@_}p;p;p;p
so với
print;print;print;print
Nếu bạn có mã như:
$i--if$i>0
bạn có thể dùng:
$i-=$i>0
thay vào đó để lưu một số byte.
Nếu bạn không gán cho một biến và vì vậy không thể sử dụng mẹo của Breadbox , bạn có thể sử dụng biểu thức 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
Tuy nhiên, đáng chú ý hơn là bạn không cần sử dụng số nguyên để truy cập vào một chỉ mục mảng:
@letters = A..Z;
$letters[rand 26]; # random letter
redo
thêm hành vi vòng lặp vào một khối mà không có for
hoặc while
. {redo}
là một vòng lặp vô hạn.
Không ngoặc đơn gọi hàm.
Perl cho phép bạn gọi một hàm đã biết (lõi hoặc được khai báo trước) bằng NAME LIST
cú pháp. Điều này cho phép bạn bỏ &
sigil (nếu bạn vẫn đang sử dụng nó) cũng như dấu ngoặc đơn.
Ví dụ: $v=join'',<>
Bạn có thể chạy nhiều câu lệnh khác nhau trong logic ternary lồng nhau.
Giả sử bạn có một lớn if
- elsif
tuyên bố. Đây có thể là bất kỳ logic và số lượng báo cáo.
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;
}
Bạn có thể sử dụng (cmd1, cmd2, cmd3)
bên trong toán tử ternary để chạy tất cả các lệnh.
$_ < 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
Đây là một ví dụ giả:
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)
thay vìTime::HiRes
(Lấy từ https://stackoverflow.com/a/896928/4739548 )
Nhiều thử thách đòi hỏi bạn phải ngủ với độ chính xác cao hơn số nguyên. select()
Đối số hết thời gian có thể làm điều đó.
select($u,$u,$u,0.1)
hiệu quả hơn nhiều so với:
import Time::HiRes qw(sleep);sleep(0.1)
Cái trước chỉ có 20 byte, trong khi cái sau chiếm 39. Tuy nhiên, cái trước đòi hỏi bạn không sử dụng $u
và chưa bao giờ định nghĩa nó.
Nếu bạn sẽ sử dụng nó, việc nhập khẩu sẽ được Time::HiRes
đền đáp, nhưng nếu bạn chỉ cần một lần, sử dụng select($u,$u,$u,0.1)
tiết kiệm 19 byte, đó chắc chắn là một sự cải thiện trong hầu hết các trường hợp.
Trừ khi thử thách chỉ định khác, bạn không cần theo dõi dòng mới.
'Thử thách' của chúng tôi cho biết 'xuất ra một số ngẫu nhiên từ 0 đến 9 đến STDOUT'. Chúng ta có thể lấy mã này (28 byte):
$s=int(rand(10));print"$s\n"
Và rút ngắn nó xuống (25 byte):
$s=int(rand(10));print $s
bằng cách đơn giản là in biến. Điều cuối cùng này chỉ áp dụng cho thử thách này một cách cụ thể (19 byte):
print int(rand(10))
nhưng điều đó chỉ hoạt động khi bạn không phải làm bất cứ điều gì với biến giữa việc gán và in.
Thỉnh thoảng (thường khi giao dịch với Quine hoặc hạn chế nguồn thách thức), bạn được hưởng lợi rất nhiều từ khả năng xâu tổ. Thông thường, bạn sẽ làm điều này với q(…)
. Tuy nhiên, tùy thuộc vào những ký tự bạn cần trong chuỗi, bạn có thể lưu một byte và sử dụng <…>
toán tử toàn cục. .
Minh họa cho điều này là theo mã định hình đầu vào thành sóng hình sin:
s/./print" "x(sin($i+=.5)*5+5).$&/eg;
Như bạn có thể thấy, đó là cách lặp lại khá nhỏ gọn qua các ký tự trong đầu vào tiêu chuẩn. Bạn có thể sử dụng regex khác để thay đổi cách mọi thứ được khớp.
$_=print""
ngắn hơn$_=print$foo
.