펄에서의 골프 팁?


47

펄 골프에 대한 일반적인 팁이 있습니까? 저는 적어도 Perl에 특정한 코드 골프 문제에 적용될 수있는 아이디어를 찾고 있습니다 (예 : "댓글 제거"는 답이 아닙니다). 답변 당 하나의 팁을 게시하십시오.

답변:


26

TMTOWTDI

이것이 가장 중요한 Perl 골프 팁입니다. 작업을 수행하기 위해 너무 긴 문자 시퀀스를 볼 때마다 다른 기능을 사용하여 동일한 효과를 얻을 수있는 다른 방법이 없는지 자문 해보십시오. 보통 있습니다. 다음은 소수입니다.

  • ~~스칼라 컨텍스트를 적용하고보다 짧은 4 자 scalar입니다.

  • y///clength의 길이를 가져올 때보 다 한 문자가 짧습니다 $_.

  • 의 문자를 반복해야 $_합니까? 교체 split//와 함께 /./gs. (또는 사용 /./g당신은 또한 줄 바꿈을 건너 뛰십시오.) 다른 변수와이 작품 : 교체 split//,$x와 함께 $x=~/./gs.

  • 모든 Perl 내장은 무언가를 반환합니다. print예를 들어 성공적인 I / O를 나타 내기 위해 1을 반환합니다. $_예를 들어, 실제 값 으로 초기화해야하는 경우 $_=print$foo하나의 돌로 두 마리의 새를 죽일 수 있습니다.

  • Perl의 거의 모든 문장은 표현식으로 작성되어보다 다양한 상황에서 사용될 수 있습니다. 여러 명령문은 쉼표로 묶인 여러 표현식이 될 수 있습니다. 시험은 단락 운영자 수행 할 수 있습니다 ?: && ||, 또한으로 and하고 or, 같은 일을하는 그러나 우선 순위가 (할당 포함) 모든 다른 연산자보다 낮 춥니 다. 루프를 통해 수행 할 수 있습니다 map또는 grep. 심지어 키워드와 같은 next, last그리고 return그들이 반환하지 않더라도, 표현 컨텍스트에서 사용할 수 있습니다! 이러한 종류의 변환을 염두에두면 코드 블록을보다 다양한 컨텍스트에 채워질 수있는 표현식으로 대체 할 수 있습니다.


$_=print""보다 짧습니다 $_=print$foo.
ASCIIThenANSI

5
아이디어는 이미 인쇄해야한다는 것 $foo입니다. 그렇지 않으면, $_=1훨씬 짧고 $_=print""같은 효과가 있습니다.
breadbox

3
세 번째는에서 문자를 반복한다는 의미 $x입니까? 그렇지 않으면 당신은 할 수 /./gs/./g.
redbmk

25

펄의 특수 변수 남용!

  • 이전 답변에서 언급 한 바와 같이 $/그리고 $"기본적으로 초기화 "\n"하고 " "각각.

  • $,그리고 $\으로 설정되어 undef기본적으로 3 개 문자 짧다.

  • $\을 설정 하면 모든에 추가됩니다 print. 예를 들면 다음과 같습니다 perl -ple '$\="=".hex.$/'. 편리한 16 진수 변환기입니다.

  • 명령 줄에서 파일을 읽지 않는 경우 -i명령 줄 스위치를 문자열 입력을위한 추가 채널로 사용할 수 있습니다 . 그 값은에 저장됩니다 $^I.

  • $=할당 된 모든 것을 정수로 만듭니다. 달리고 perl -ple '$_=$==$_'다양한 종류의 inupts를 제공하십시오. 마찬가지로, $-값을 음이 아닌 정수로 만듭니다 (즉, 선행 대시는 숫자가 아닌 문자로 처리됨).

  • 루프를 $.반복 할 때마다 0이 아닌 값으로 자동 재설정되는 부울 플래그로 사용할 수 있습니다 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.

가 실행될 $_때 undefined 이므로 morecode;print출력 레코드 구분 기호 $\가 남용되어 실제 출력을 인쇄 할 수 있습니다.

예를 들어

perl -pe '$\+=$_}{'

STDIN에서 한 줄에 하나의 숫자를 읽고 합계를 인쇄합니다.


#!perl -n첫 번째 줄 에서이 작업을 수행 할 수 있다고 가정합니다 .
ASCIIThenANSI

@ASCIIThenANSI : 그렇습니다. (늦은 답변에 대해 죄송합니다.)
Dennis

1
신용이 필요한 곳에서 신용을 줄 때, 나는 @primo 의 Perl 답변 중 하나에서 처음 으로이 세 가지 트릭 ( 비대칭 중괄호 -p$\​) 의 조합을 보았습니다 . 그의 답변을 읽는 것은 좋은 Perl 팁입니다.
Dennis

1
을 던지는 }for(...){중괄호 사이에 종종뿐만 아니라, 예를 들면 매우 편리 codegolf.stackexchange.com/a/25632
프리모

-n과 함께 유용하다고 생각한 것은 || = 기본값 할당 연산자입니다. 루프 전에 값을 할당 할 수 없게됩니다.
deltaray

18

$_스칼라 참조를 제거하는 데 사용하십시오 . 대부분의 함수에서 기본값으로 사용되는 특수 변수이며 매개 변수를 생략하면이 변수를 참조 할 수 있습니다.

로 변경 $n하면 $_다음으로 변경할 수 있습니다 $n=<>;chop$n;print$n.$_=<>;chop;print

print함수는 $_기본적으로 내용을 인쇄하고에서 chop작동합니다 $_.


인가 $_=<>;하지 않고, 필요 <>;로 라인을 읽어 $_자동으로?
sundar

아니요, 그렇게 생각하지 않습니다. 나는 프로그램을 비교 $_=<>;print하고 <>;print. 첫 번째는 내가 입력 한 내용을 다시 반복하지만 다른 하나는 입력하지 않습니다.
PhiNotPi

고마워, 같은 경우에만 발생합니다 print while(<>). 확실하지, 어느 쪽이 특별한 경우 나 일부 일관된 논리가 배후에있을 경우 <>의에 참여 perlopwhile의 일부가 perlsyn이 문제를 언급하는 것 같다.
sundar

1
@sundar while(<>)는 perlsyn, I / O Operators에 설명 된 특수한 경우입니다. loop), 값이 자동으로 전역 변수 $ _에 할당되어 이전에 있던 것을 파괴합니다. "
kernigh

17

가능한 한 Perl의 특수 변수를 사용하십시오. 예 :

  • $"대신 사용" "
  • $/대신 사용"\n"

그들은 어휘 분석기의 도움으로 보장 된 1 문자 길이의 식별자라는 추가적인 이점을 가지고 있습니다. 이렇게하면 다음과 같이 다음 키워드에 붙일 수 있습니다.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-- vs while($n--){$a++;$b++}
  • chop$,if$c vs if($c){chop$,}

마지막 예제는와 연결 $c&&chop$,되지만 대부분의 다중 문 작업에 실제로 적용됩니다. 기본적으로 연산자 우선 순위를 잃는 모든 것 &&.


11

하지 마십시오 use strict. (PCG.SE 컨텍스트에 대해 인용하지 마십시오.) 더 중요한 것은 엄격한 조건 에서처럼 코딩하지 마십시오. 일반적인 용의자 :

  • my피할 수 있다면 변수를 선언 하지 마십시오 . 실제로 필요한 유일한 변수는 my어휘 범위를 원하는 변수입니다 . 스코프 보호가 필요하지 않고 재귀를 완전히 제어하는 ​​경향이있는 골프를 타는 경우는 거의 없습니다.
  • 한 단어로 된 문자열을 인용하지 마십시오 : ( example ). 그래도 같은 이름의 함수가 없는지 확인하십시오.

5
print hello작동하지 않습니다. 실제로는 print hello $_( $_filehandle로 인쇄) 를 의미 hello합니다.
Konrad Borowski

@GlitchMr 감사합니다! (그리고 지금은 여전히 ​​유효하지 않기 때문에 내 말이 부끄럽다. print지금은 훌륭하고 짧은 예를 찾을 수 없다)
JB

@ JB 좋은 예가 있습니다 : codegolf.stackexchange.com/a/8746/3348
ardnew

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

어떤 괄호 후위에 필요하지 while, join'',<>;또한 그들없이 작동합니다.
choroba

10

단어가 아닌 문자를 변수 이름으로 사용

사용 $%대신하는 것은 $a당신이 바로 옆에 변수 이름을 배치하도록 할 수 있습니다 if, for또는 while같이 구성 :

@r=(1,2,3,4,5);$%=4; print$_*$%for@r

많은 것을 사용할 수 있지만 어떤 마법 효과가 있는지에 대한 문서@BreadBox의 답변 을 확인하십시오 !


명령문 수정자를 사용할 수없는 경우 맵 사용

@JB 의 답변에 따라 명령문 수정자를 사용할 수 없으면 map이 바이트를 저장할 수 있습니다.

for(@c){} vs. map{}@c;

postfix for루프를 안에 넣을 수 있으므로 중첩 반복을 수행하려는 경우 유용 합니다 map.


모든 매직 정규식 변수 사용

Perl은 'text before match'와 'match after text'에 대한 마법 변수를 가지고 있으므로 잠재적으로 더 적은 문자로 두 그룹으로 나눌 수 있습니다.

($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

vs.

print;print;print;print

조건부 증분 / 감쇄기 교체

다음과 같은 코드가있는 경우 :

$i--if$i>0

당신이 사용할 수있는:

$i-=$i>0

대신 일부 바이트를 저장하십시오.


정수로 변환

변수에 할당하지 않고 breadbox의 팁을 사용할 수없는 경우 표현식을 사용할 수 있습니다 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

redofor또는 없이 블록에 루프 동작을 추가합니다 while. {redo}무한 루프입니다.


7

함수 호출을 괄호로 묶지 마십시오.

Perl에서는 NAME LIST구문을 사용하여 알려진 (핵심 또는 사전 선언 된) 함수를 호출 할 수 있습니다 . 이렇게하면 &시길 (아직 사용중인 경우)과 괄호 를 삭제할 수 있습니다 .

예를 들면 다음과 같습니다. $v=join'',<>

전체 문서


5

다음과 같이 대입 표현식의 값을 사용하십시오.

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

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

이것은 $nPerl에서 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()의 타임 아웃 인수가 바로 그렇게 할 수 있습니다.

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

문자열 리터럴과 같은 글롭 사용

때때로 (종종 또는 문제를 처리 할 때 ) 문자열 리터럴을 중첩하는 기능의 이점이 있습니다. 일반적으로으로이 작업을 수행합니다 q(…). 그러나 문자열 내에서 필요한 문자에 따라 바이트를 저장 <…>하고 glob 연산자를 사용할 수 있습니다. (앵글 괄호 안에있는 것은 파일 핸들처럼 보이지 않으며 파일 이름 목록으로 확장되는 것처럼 보일 수 없으므로 많은 문자가 올바르게 작동하지 않습니다.)


0

정규식을 사용하십시오.

이에 대한 적절한 설명은 입력을 사인파로 형성하는 코드입니다.

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

보시다시피 표준 입력에서 문자를 반복하는 매우 간단한 방법입니다. 다른 정규식을 사용하여 일치하는 방식을 변경할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.