Lời khuyên cho việc chơi golf ở Perl?


47

Bạn có lời khuyên chung nào cho việc chơi golf ở Perl? Tôi đang tìm kiếm những ý tưởng có thể được áp dụng cho các vấn đề về golf nói chung ít nhất là cụ thể đối với Perl (ví dụ: "xóa bình luận" không phải là một câu trả lời). Xin vui lòng gửi một lời khuyên cho mỗi câu trả lời.

Câu trả lời:


26

TMTOWTDI

Đó 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///clà một char ngắn hơn so với lengthkhi 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 /./gnế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//,$xbằng $x=~/./gs.

  • Mỗi Perin dựng sẵn trả lại một cái gì đó. printtrả 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$foophé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 andor, 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 maphoặc grep. Ngay cả các từ khóa như next, lastreturncó 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.


$_=print""ngắn hơn $_=print$foo.
ASCIIThenANSI

5
Ý tưởng là bạn đã cần in $foo. Mặt khác, $_=1ngắn hơn nhiều $_=print""và có tác dụng tương tự.
hộp bánh mì

3
Đối với thứ ba, bạn có nghĩa là lặp đi lặp lại trong ký tự trong $x? Nếu không bạn chỉ có thể làm /./gs/./g.
redbmk

25

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 đó $/$"được khởi tạo theo mặc định "\n"" ", tương ứng.

  • $,$\cả hai đều được đặt thành undefmặ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 -icô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.


20

-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 -ncó 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 -ptắ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 codecho tất cả các dòng đầu vào, theo sau morecode.

$_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.


Tôi đang giả sử bạn có thể thực hiện điều này với #!perl -ndòng đầu tiên, phải không?
ASCIIThenANSI

@ASCIIThenANSI: Vâng, đúng vậy. (Xin lỗi vì câu trả lời muộn.)
Dennis

1
Cung cấp tín dụng khi tín dụng đáo hạn, tôi nghĩ rằng tôi đã thấy sự kết hợp của ba thủ thuật này (dấu ngoặc nhọn không khớp-p$\​) lần đầu tiên trong một trong các câu trả lời Perl của @primo . Để đọc câu trả lời của anh ấy là một mẹo Perl tốt của riêng mình.
Dennis

1
Ném một cái }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
primo

Một điều tôi thấy hữu ích khi kết hợp với -n là toán tử gán giá trị mặc định | | =. Nhận được xung quanh không thể chỉ định một giá trị trước vòng lặp.
deltaray

18

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 $nthành $_, bạn có thể thay đổi $n=<>;chop$n;print$nthành$_=<>;chop;print

Ở đây, printchức năng in các nội dung $_theo mặc định, và chopcũng hoạt động trên $_.


Được $_=<>;yêu cầu, không <>;đọc dòng $_tự động?
Sundar

Không, tôi không nghĩ vậy. Tôi so sánh các chương trình $_=<>;print<>;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.
PhiNotPi

Ồ, cảm ơn, hóa ra chỉ xảy ra trong trường hợp như thế 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 perlopvà cũng không phải whilelà một phần của perlsynhành vi này.
Sundar

1
@sundar 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 đó. "
kernigh

17

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ụ:

  • Sử dụng $"thay vì" "
  • Sử dụng $/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


15

Đừ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 globcú 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}>;

12

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 &&.


11

Đừ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:

  • đừng mybiến -declare nếu bạn có thể tránh nó. Các biến duy nhất thực sự cần mylà 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.
  • không trích dẫn chuỗi một từ: ( ví dụ ). Tuy nhiên, hãy đảm bảo rằng bạn không có chức năng có cùng tên.

5
print hellosẽ không làm việc Nó thực sự có nghĩa là print hello $_(in $_ra filehandle hello).
Konrad Borowski

@GlitchMr cảm ơn! (và bây giờ tôi đang bị choáng váng, bởi vì quan điểm của tôi vẫn còn hiệu lực, chỉ là không có print, và bây giờ tôi không thể tìm thấy một ví dụ hay và ngắn)
JB

@JB đây là một ví dụ hay: codegolf.stackexchange.com/a/8746/3348
ardew

11

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.

  • Nếu bạn có một vòng lặp while (hoặc một vòng lặp for, bạn có thể tạo thành một vòng lặp while), bạn có thể định nghĩa "while" sau lệnh: print $n++ while ($n < 10)
  • Nếu bạn cần đọc mọi thứ từ STDIN thành một chuỗi: $var = join('',<>)
  • Như BareSpy đã chỉ ra, sử dụng $ / thay vì \ n sẽ nhanh hơn trong một số trường hợp: print ('X'*10) . "\n";dài hơnprint ('X'*10) . $/;
  • sayChức năng của Perl ngắn hơn print, nhưng bạn sẽ phải chạy mã -Ethay vì-e
  • Sử dụng phạm vi như a..zhoặc thậm chí aa..zz. Nếu cần như một chuỗi, sử dụng join.
  • Chuỗi tăng: $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.


1
Phải print ('X'*10) . $/;làm gì đây? Đối với tôi nó in 0và 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à xthay vì *hoặc một cái gì đó?
aschepler

Không có dấu ngoặc đơn cần thiết trong postfix while, join'',<>;cũng hoạt động mà không có chúng.
choroba

10

Sử dụng các ký tự không phải từ làm tên biến

Sử dụng $%thay vì $acó thể cho phép bạn đặt tên biến ngay bên cạnh một if, forhoặc whilexâ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ệucâu trả lời của @ BreadBox xem cái nào có hiệu ứng ma thuật!


Sử dụng bản đồ khi bạn không thể sử dụng công cụ sửa đổi câu lệnh

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 forcác vòng lặp postfix bên trong map.


Sử dụng tất cả các biến biểu thức chính quy

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=$'

Thay thế tên phụ bằng tên dài bằng tên ngắn hơn

Nếu bạn gọi nói printbố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

Thay thế tăng / giảm có điều kiện

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.


Chuyển đổi sang số nguyên

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

9

redothêm hành vi vòng lặp vào một khối mà không có forhoặc while. {redo}là một vòng lặp vô hạn.


7

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 LISTcú 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'',<>

Tài liệu đầy đủ


5

Cố gắng sử dụng giá trị của biểu thức gán, như vậy:

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

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

Điều này hoạt động vì $ncó 2 ký tự trong Perl. Bạn có thể thay đổi $nthành ()miễn phí và lưu 1 dấu chấm phẩy bằng cách di chuyển bài tập vào dấu ngoặc đơn.


5

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- elsiftuyê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)

4

Sử dụng 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 $uvà 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.


1

Rút ngắn báo cáo in của bạn

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.


Người cuối cùng đã được liệt kê ở đây .
Sp3000

@ Sp3000 Cảm ơn bạn, tôi đã cập nhật câu trả lời của tôi.
ASCIIThenANSI

1

Sử dụng các chuỗi như chuỗi ký tự

Thỉnh thoảng (thường khi giao dịch với hoặc 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. .


0

Sử dụng biểu thức regexe.

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.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.