Perlでのゴルフのヒント?


47

Perlでゴルフをするための一般的なヒントは何ですか?私は、少なくともある程度Perlに固有のゴルフ問題全般のコーディングに適用できるアイデアを探しています(たとえば、「コメントの削除」は答えではありません)。回答ごとに1つのヒントを投稿してください。

回答:


26

TMTOWTDI

それはあなたが知る必要がある最も重要なPerlゴルフのヒントです。タスクを達成するために絶対に必要な長すぎる文字のシーケンスを見ているときはいつでも、異なる機能を使用して同じ効果を得る他の方法がないかどうかを自問してください。通常あります。ここにほんの一握りがあります:

  • ~~はスカラーコンテキストを強制し、は4文字より短いですscalar

  • y///clength、の長さを取得するときよりも1文字短くなり$_ます。

  • の文字を反復処理する必要があり$_ますか?交換してくださいsplit///./gs。(または、/./g改行もスキップしたい場合に使用します。)これは他の変数で機能します:replace split//,$xで置き換え$x=~/./gsます。

  • すべてのPerlビルトインは何かを返します。printたとえば、成功したI / Oを示すために1を返します。$_たとえば、真の値に初期化する必要がある場合、$_=print$foo1つの石で2羽の鳥を殺すことができます。

  • Perlのほとんどすべてのステートメントは式として記述できるため、さまざまなコンテキストで使用できます。複数のステートメントは、コンマで連結された複数の式になることができます。テストは?: && ||、短絡演算子、およびandand orを使用して実行できます。これらは、同じことを行いますが、他のすべての演算子(割り当てを含む)よりも優先順位が低くなります。ループはmapまたはを介して実行できますgrep。でも、キーワードのようにnextlastそしてreturn、彼らは戻りませんが、表現コンテキストで使用することができます!この種の変換を念頭に置いておくと、コードブロックを、さまざまなコンテキストに詰め込むことができる式に置き換えることができます。


$_=print""はより短いです$_=print$foo
ASCIIThenANSI

5
アイデアは、すでに印刷する必要があるということです$foo。それ以外の場合は、$_=1よりもずっと短く$_=print""、同じ効果があります。
パンボックス

3
3番目については、charsを繰り返し処理するということ$xですか?それ以外の場合は/./gs、とすることができます/./g
redbmk

25

Perlの特殊変数を乱用します!

  • 前の回答で述べたように$/して$"にデフォルトで初期化され"\n"そして" "、それぞれ。

  • $,そして、$\の両方が設定されているundefデフォルトでは、3つの文字短くなっています。

  • $\値に設定すると、値がeveryに追加されますprint。例:perl -ple '$\="=".hex.$/'便利な16進数から10進数へのコンバーターです。

  • コマンドラインからファイルを読み取っていない場合は-i、文字列を入力するための追加のチャネルとしてコマンドラインスイッチを使用できます。その値はに保存され$^Iます。

  • $=割り当てられたものはすべて強制的に整数になります。実行perl -ple '$_=$==$_'してさまざまなinuptsを与えてみてください。同様に、$-その値を強制的に負でない整数にします(つまり、先頭のダッシュは非数値文字として扱われます)。

  • ループの$.繰り返しごとに真(ゼロ以外)の値に自動的にリセットされるブールフラグとして使用できwhile(<>)ます。


20

-n 比類のない中括弧

コマンドラインスイッチ-nを使用して、1行ごとに1回スクリプトを実行できることはよく知られています。

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と、入力のすべての行に対して実行した後に1回だけ出力され、その後にが続きmorecodeます。

は実行$_時に未定義であるためmorecode;print、出力レコードのセパレータ$\を悪用して実際の出力を印刷できます。

例えば

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

STDINから行ごとに1つの数値を読み取り、その合計を出力します。


#!perl -n最初の行でこれを達成できると思いますよね?
ASCIIThenANSI

@ASCIIThenANSI:はい、それは正しいです。(回答が遅れて申し訳ありません。)
デニス

1
クレジットが支払われるべき場所にクレジットを与える-p$\​@ primoのPerlの回答の1つで、これらの3つのトリック(中かっこなど)の組み合わせが初めて見られたと思います。彼の答えを読むことは、Perlの良いヒントです。
デニス

1
}for(...){中括弧の間にa を入れる
primo

-nと組み合わせて便利だと思ったものの1つは、|| =デフォルト値割り当て演算子です。ループの前に値を割り当てることができないことを回避します。
deltaray 16

18

$_スカラー参照を排除するために使用します。ほとんどの関数でデフォルトとして使用されるのは特別な変数であり、パラメーターを省略するだけでこの変数を参照することができます。

変更することにより$n$_、あなたは変更することができます$n=<>;chop$n;print$n$_=<>;chop;print

ここで、このprint関数は$_デフォルトでのコンテンツを印刷し、chopでも動作し$_ます。


され$_=<>;ていない、必要<>;にラインを読んで$_自動的に?
スンダ

いいえ、そうは思いません。私はプログラム$_=<>;printとを比較しました<>;print。最初のものは私がタイプしたものを私に繰り返し返しますが、他のものはそうではありません。
PhiNotPi

ああ、ありがとう、のような場合にのみ起こることが判明しましたprint while(<>)。それは特別なケースだか、その背後にあるいくつかのコヒーレントロジックがありますかどうかわから、どちらない<>で一部のperlopwhileの一部は、perlsynこの動作を言及しているようです。
スンダ

1
@sundar while(<>)は、perlsyn、I / O Operatorsに文書化されている特殊なケースです。ループ)、値は自動的にグローバル変数$ _に割り当てられ、以前に存在していたものはすべて破壊されます。」
kernigh 14年

17

可能な限り、Perlの特殊変数を使用します。例:

  • $"代わりに使用" "
  • $/代わりに使用"\n"

字句解析器の助けを借りて、保証された1文字の長い識別子であるという追加の利点があります。これにより、次のように、それに続くキーワードに貼り付けることができます。print$.for@_

すべての特殊変数のリストは、ここから入手できます:特殊変数


15

使用しないでくださいqw。これは、より良い方法で使用できる2つの文字の無駄です。たとえば、次のように書かないでください。

@i=qw(unique value);

代わりに、ベアワードを使用します。

@i=(unique,value);

または、ベアワードを使用できない場合は、glob構文を使用します。

@i=<unique value>;

glob 構文は興味深い効果にも使用できます。

@i=<item{1,2,3}>;

12

複合ステートメントの代わりにステートメント修飾子を使用します。

複合ステートメントでは、引数に括弧を、ブロックに括弧を必要とする傾向がありますが、ステートメント修飾子には必要ありません。

比較:

  • $a++,$b++while$n--while($n--){$a++;$b++}
  • chop$,if$cif($c){chop$,}

最後の例はに関連付けられて$c&&chop$,いますが、ほとんどの複数ステートメント操作で本当に輝いていることに注意してください。基本的に、演算子の優先順位を失うすべてのもの&&


11

しないでくださいuse strict。(これについては引用しないでください、PCG.SEのコンテキストはちょっと重要です)そして、もっと重要なことは、あたかも厳格なものであるかのようにコーディングしないでください。通常の容疑者:

  • my回避できる場合は、変数を宣言しないでください。本当に必要な変数は、myレキシカルスコープにしたいものだけです。ゴルフでは、スコープ保護は必要なく、再帰を完全に制御する傾向があるので、それはほとんどありません。
  • 1単語の文字列を引用しないでください()。ただし、同じ名前の関数がないことを確認してください。

5
print hello動作しません。実際にはprint hello $_$_filehandleに出力するhello)という意味です。
コンラッドボロスキー

@GlitchMrありがとう!(私のポイントだけではないと、まだ有効であるので、今、私は、がっかりだprint、と今私は素晴らしく、簡単な例を見つけることができません)
JB


11

これらのいくつかは正式な名前を持っていると確信しており、私はそれらを知らないだけです。

  • whileループ(またはwhileループにできるforループ)がある場合、コマンドの後に「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代わりに*何かを意味しましたか?
アシェプラー

postfixにwhileは括弧は必要ありませんが、括弧なしjoin'',<>;でも機能します。
チョロバ

10

単語以外の文字を変数名として使用する

$%代わりに使用すると$a、変数名をのすぐ隣に配置したりif、次のように構成しforたりできますwhile

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

多くのものを使用できますが、ドキュメント@BreadBoxの回答を確認してください。


ステートメント修飾子を使用できない場合はマップを使用します

@JB の答えに従ってステートメント修飾子を使用できない場合、mapはバイトを節約する可能性があります。

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

またformap。内にpostfix ループを配置できるため、ネストされた反復を実行する場合に便利です。


すべての魔法の正規表現変数を使用する

Perlには「一致する前のテキスト」と「一致した後のテキスト」のマジック変数があるため、潜在的に少ない文字で2つのグループに分割できます。

($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コードでsayを4回以上呼び出す場合(これは呼び出しているルーチンの長さによって明らかに異なります)、短いサブ名に置き換えてください:

sub p{print@_}p;p;p;p

print;print;print;print

条件付きインクリメンター/デクリメンターを置き換える

次のようなコードがある場合:

$i--if$i>0

次を使用できます:

$i-=$i>0

代わりに、いくつかのバイトを保存します。


整数に変換

変数に割り当てていないため、ブレッドボックスのヒントを使用できない場合は、次の式を使用できます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

redoブロックにすることなく、ループ動作が追加されますforwhile{redo}無限ループです。


7

関数呼び出しを括弧で囲まないでください。

Perlでは、NAME LIST構文を使用して既知の(コアまたは事前宣言された)関数を呼び出すことができます。これにより、&シギル(まだ使用している場合)と括弧をドロップできます。

例えば: $v=join'',<>

完全なドキュメント


5

次のように、割り当て式の値を使用してみてください。

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

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

これ$nは、Perlでは2文字であるため機能します。に無料で変更$n()、割り当てを括弧内に移動することにより、セミコロンを1つ節約できます。


5

ネストされた3項論理内で複数の異なるステートメントを実行できます。

大きな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

単に変数を出力するだけです。最後の1つは、特にこのチャレンジにのみ適用されます(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.