ジュリアでのゴルフのヒント


20

ジュリアでゴルフをするための一般的なヒントは何ですか?私は、ゴルフの一般的な問題をコード化するのに適用できる、少なくともジュリア特有のアイデアを探しています(たとえば、「コメントの削除」は答えではありません)。

回答:


19

注:ジュリアの構造はまだ安定していないため、以下に古いヒントが含まれている場合があります。

いくつかのキャラクターを保存するためのいくつかのトリック

  1. 頻繁に使用されるバイナリ関数で演算子をオーバーロードします。たとえば、多くの整数除算を行う必要があり、逆除算が必要ない場合は、を使用し\ =div、のa\b代わりに入力できますdiv(a,b)。スペースに注意してください-これは、「\ =」演算子として解析されないようにするために必要です。また、REPLプロンプトレベルで過負荷になった場合は、(\)=Base.(\)または\ =Base. \を使用してリセットしてください。注:一部の関数には、Alex Aが指摘しているように、÷for などの既存のUTF-8演算子が事前に定義divされています。
  2. 条件付き出力には文字列で^を使用します。つまり、ではなくa>0?"Hi":""、使用"Hi"^(a>0)1つのバイトを保存する、またはブール値A、使用のための"Hi"^a3つのバイトを保存します。
  3. (時々)小さな固定サイズのベクトルを個別の変数として保存します。たとえば、ではなくa=split("Hi there"," ")、あなたが避けることができるかもしれませa[1]およびa[2]使用しa,b=split("Hi there"," ")て参照することができ、aおよびb割り当てのちょうど2つの余分な文字のコストで、それぞれの使用のための3バイトを保存し、。明らかに、ベクトル演算を使用できる場合は、これを行わないでください。
  4. 配列の最初の要素にアクセスするには[] -配列の場合、式A[]はと同等A[1]です。これは、最初の文字を取得したい場合は文字列、またはタプルでは機能しないことに注意してください。
  5. 配列、タプル、または文字==[]列にisemptyを使用しないでください -代わりに、配列および==()タプルに使用してください。同様に、ネガティブの場合、とを使用!=[]!=()ます。文字列の場合==""、空の場合に使用しますが、空で>""はない場合に使用します。これは、 ""が辞書的に他の文字列の前にあるためです。
  6. "if"の代わりに正しい短絡論理演算子を使用します。ジュリア固有ではないかもしれませんが、覚えておく価値があります。x<=1&&"Hi"として記述できx>1||"Hi"、文字を保存します(ブール値の戻り値が重要でない限り)。
  7. 使用は、文字列内の文字の存在を確認するために含まれないでください -あなたは基本的なASCII、使用に制限している場合in('^',s)ではなくcontains(s,"^")。他の文字を使用できる場合、でもう少し節約できますが、UTF-8では3バイトである'^'∈sことに注意してください
  8. 配列の最小/最大値をお探しですか?最小または最大使用しないでくださいではなく、使用して- minimum(x)あるいはmaximum(x)、使用min(x...)またはmax(x...)あなたが知っていれば、あなたのコードをオフ1つの文字を剃るために、x少なくとも2つの要素を持つことになります。または、のすべての要素がx負でないことがわかっている場合は、minabs(x)またはmaxabs(x)
  9. 可能な場合、チャレンジで許可されている場合は、\ nの代わりに実際の改行を使用します。これによりコードが読みにくくなり、実際に理解できるように「改変されていない」バージョンを提供する必要があることに注意してくださいそれ。
  10. 正規表現文字列の後にオプションを配置します -たとえば、複数行モードで正規表現文字列を使用する場合は、type r"(?m)match^ this"、type r"match^ this"m、3文字を保存しないでください。
  11. flipudを使用した1次元配列の反転 - 同じ操作を実行するreverse(x)よりも1バイト長くflipud(x)、後者の方が優れています。
  12. 可能であれば、push!、unshift!、append!、prepend!の代わりに配列連結を使用してください。-通常の配列の場合、これは簡単に行えます。配列要素を持つAny型の配列の場合、追加された配列を囲む中かっこが必要です(つまり、{[1,2]}ではありません{1,2})-Julia 0.4の場合はが必要Any[[1,2]]です。
  13. 配列のインデックス付けを使用して、配列または文字列のサイズを取得します - end配列のインデックス付け内で使用すると、自動的に配列/文字列の長さに変換されます。ではなくk=length(A)、を使用A[k=end]して3文字を保存します。kをすぐに使用する場合、これは有益ではないことに注意してください。これは多次元の場合にも機能します- A[k=end,l=end]各次元のサイズを取得しますA-ただし、(k,l)=size(A)この場合は1バイト短くなりますので、同時に最後の要素にすぐにアクセスしたい場合にのみ使用してください。
  14. 配列のインデックス付けを使用してインデックスイテレータを取得する -13と同様に、を使用して配列の長さに一致するイテレータを取得することもできます。A[k=1:end]この場合k、イテレータマッチングを保持します1:length(A)。これはA、同時に配列も使用する場合に便利です。
  15. 文字列をchar配列に変換するためにcollectを使用しないでください -の代わりにcollect(A)[A...]同じことを行い、4バイトを節約します。
  16. 数値を文字列に変換する必要がありますか?式または複数文字変数、および単一文字変数には、"$(s[i])"またはdec(s[i])を使用し"$i"ます。
  17. 条件付き割り当ての?:代わりに、&&または||条件付き割り当てに使用します。つまり、特定の条件でのみ割り当てを実行する場合、、またはではなくを書き込むことcond?A=B:1で1バイトを節約できます。ここで、はダミー値であることに注意してください。cond&&(A=B)cond?1:A=Bcond||(A=B)1
  18. unionまたはの代わりにunique - union(s)使用すると、と同じことが行わunique(s)れ、プロセスに1バイトが保存されます。ASCII以外の文字を使用できる場合∪(s)、同じことを行い、コストはの5バイトではなく3バイトになりますunion

2
ああ、私はPythonでの最初のトリックをどのように愛するか。
seequ 14年

split("Hi there")pattern引数はデフォルトでスペースになっているため、単にスペースを使用して分割できます。
アレックスA.

@AlexA。-私は知っていますが、それは先端のポイントではなく、先端はどちらの方法でも等しく適用されます。
グレンO

ポイント12は0.5で変更されました。
リンドンホワイト

@Oxinabox-驚くことではありませんが、そのうちのいくつかは時代遅れになっていると確信しています。私はもともと、0.3のヒントのほとんどを書いたと思います。
グレンO

15

演算子を再定義して関数を定義する

演算子を再定義すると、括弧とコンマで大量のバイトを節約できます。

再帰的な単項演算子

単項の例については、フィボナッチ数列の次の再帰的な実装を比較してください。

F(n)=n>1?F(n-1)+F(n-2):n # 24 bytes
!n=n>1?!~-n+!(n-2):n     # 20 bytes
!n=n>1?!~-n+!~-~-n:n     # 20 bytes

オンラインでお試しください!

再定義された演算子は、最初の優先順位を保持します。

私たちは単純に入れ替えることができなかったことをノート!に賛成して~いるので、~すでに整数のために定義されている間、!唯一のブール値のために定義されています。

二項演算子

再帰がなくても、演算子の再定義は二項関数の定義よりも短くなります。単純な可分性テストの次の定義を比較してください。

f(x,y)=x==0?y==0:y%x==0 # 23 bytes
(x,y)->x==0?y==0:y%x==0 # 23 bytes
x->y->x==0?y==0:y%x==0  # 22 bytes
x\y=x==0?y==0:y%x==0    # 20 bytes

オンラインでお試しください!

再帰的な二項演算子

以下は、二項演算子を再定義してアッカーマン関数を計算する方法を示しています。

A(m,n)=m>0?A(m-1,n<1||A(m,n-1)):n+1    # 35 bytes
^ =(m,n)->m>0?(m-1)^(n<1||m^~-n):n+1   # 36 bytes
| =(m,n)->m>0?m-1|(n<1||m|~-n):n+1     # 34 bytes
m\n=m>0?~-m\(n<1||m\~-n):n+1           # 28 bytes

オンラインでお試しください!

^優先度が高すぎるため、通常の識別子を使用するよりも長いことに注意してください。

前に述べたように

m|n=m>0?m-1|(n<1||m|~-n):n+1           # 28 bytes

|この場合は既に定義されているため、整数引数に対しては機能しません。整数の定義は次のように変更できます

m::Int|n::Int=m>0?m-1|(n<1||m|~-n):n+1 # 38 bytes

しかし、それは法外に長いです。ただし、floatを左の引数として、整数を右の引数として渡すと機能します。


11
  1. factor(n)に簡単に惑わされないでください。魅力的なベースライブラリ関数factor(n)には致命的な欠陥があります。整数の因数分解を順序付けられていないDict型で返します。したがって、それはコストのかかる必要ですcollect(keys())collect(values())、潜在的にも、catそしてsortあなたはそれのうち望んでいたデータを取得します。多くの場合、試験部門ごとにファクタリングする方が安価です。悲しいが本当。

  2. 使用マップ mapは、ループの優れた代替手段です。違いに注意してくださいmapmap!し、ときにすることができ、後者のインプレース機能を活用します。

  3. mapreduceを使用する mapreduce、mapの機能がさらに拡張され、バイトを大幅に節約できます。

  4. 匿名関数は素晴らしいです!..特に前述のmap関数に渡される場合。

  5. 累積配列関数 cumprodcumsum、風味cummin及び他の同様の名前の関数はn次元配列の指定された次元に沿った累積動作を可能にします。(または、配列が1-dの場合は* un *指定)

  6. Anyの短い表記法多次元配列(またはDict)の特定の次元をすべて選択する場合、たとえばA[Any,2]、次を使用してバイトを節約できます。A[:,2]

  7. 関数の代わりに単一行の表記を使用function f(x) begin ... endするf(x)=(...)

  8. 三項演算子を使用する単一式のIf-Then-Else構文の場合は、スペースを節約できます。警告:他の一部の言語では可能ですが、ジュリアではコロンの後の部分を省略できません。また、Juliaの演算子は式レベルであるため、コードブロック全体の条件付き実行には使用できません。
    if x<10 then true else false end
    x<10?true:false


3
ジュリア特有の「三項演算子を使用する」のはどうですか?それはそれを持っているすべての言語に関連しています。
ピーターテイラー14年

5
それが関連している。多くの言語には、マップ、匿名または純粋な関数、何らかの/すべての略記法、累積関数などもあります。すべてのヒントスレッドをその言語に完全に固有の機能のみに減らす場合、ヒントの内容はほとんどありません。 。
ジョナサンヴァンマトレ14年

3
まあ、スターター用のすべての機能的なものだけで、すべてが値を返すので、三項演算は冗長です。特定の例が必要な場合:Go、Haskell、Scala、Lua、VB、Prolog、PL / SQL ... Pythonでさえ、多くのバージョンには対応していません。ヒントスレッドが三項演算子に言及している十数近くの言語の中で、あなたが私の地方に来ることだけを選んだ理由はありますか?
ジョナサンヴァンマトレ14年

3
ええ、少なくともあなたは平等な機会です。ヘ( ̄ー ̄ヘ)
ジョナサンヴァンマトレ

1
ヒント3を調整することをお勧めしますか?mapreduceは、mapfoldlまたはmapfoldrのいずれよりも長く、実装に応じて動作が異なる場合があります。mapfoldlとmapfoldrは、(それぞれ)一貫して左結合および右結合であるため、より適切な選択です。これは、より一般的に縮小にも適用されます(foldlまたはfoldrを使用)。
グレンO

9

関数を反復する

これは他の言語でも可能ですが、通常は簡単な方法よりも長くなります。ただし、ジュリアの単項演算子と二項演算子を再定義する機能により、非常にゴルファーになります。

たとえば、1〜10の自然数の加算、減算、乗算、および除算テーブルを生成するには、次のようにします。

[x|y for x=1:10,y=1:10,| =(+,-,*,÷)]

その再定義バイナリ演算子|として+-*および÷、その後、計算x|y操作ごととxし、y所望の範囲内。

これは単項演算子でも機能します。たとえば、複素数1 + 2i3-4i-5 + 6i、および-7-8i、それらのネガ、それらの複素共役、およびそれらの逆数を計算するには、

[~x for~=(+,-,conj,inv),x=(1+2im,3-4im,-5+6im,-7-8im)]

その再定義単項演算子~として+-conjおよびinv、その後、計算~xすべての所望の複素数のため。

実際のコンテストの例


6
  1. キーワードは、スペースやセミコロンを必要とせずに、定数のすぐ後に続くことがあります。例えば:

    n->(for i=1:n n-=1end;n)

    1との間にスペースがないことに注意してくださいend。これはend、親括弧の後に起こる場合にも当てはまります)end

  2. 演算子をオーバーロードするの÷ではなく、使用して整数除算を実行しdiv()ます。÷UTF-8では2バイトの価値があることに注意してください。

  3. 可能な場合ではなく、vec()or A[:](一部の配列の場合A)を使用しreshape()ます。

  4. チャレンジルールで許可されている場合、完全なプログラムではなく機能を作成します。stdinから読み取って変数を定義するのではなく、入力を受け入れる関数を定義する方が簡単です。例えば:

    n->(n^2-1)
    n=read(STDIN,Int);n^2-1
  5. 変数は、関数の引数内でインクリメントできます。たとえば、次のとおりである私の答え次の1-スパースバイナリ番号検索挑戦:

    n->(while contains(bin(n+=1),"11")end;n)

    これはn、ループ内でインクリメントするよりも短いです。


6
  1. 種類はしないでくださいreturn f(x)=x+4と同一であるが、より短いですf(x)=return x+4。ジュリアは常に最後のステートメントの結果を返します。
  2. inの代わりに=を使用します。 [x for x in 1:4]は3文字以上ですが、[x for x=1:4]

5

ブロードキャスト関数呼び出しを使用します。

Julia 0.5で導入されました。それはマップに似ていますが、使用する文字数が少なく、その引数に対するブロードキャスト動作を行います。つまり、処理するラムダを少なくすることができます。

のではなく:

  • map(f,x) -8文字。
  • f.(x) -5文字

より良い:

  • map(a->g(a,y),x) -16文字
  • g.(x,[y]) -9文字
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.