Scalaでのゴルフのヒント


24

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

(これはPythonの...の恥知らずなコピーです)

回答:


5

免責事項:この回答の一部は、ここにある他の回答の一般化です。

引数タイプを指定せずにラムダを使用します

次のようなものを送信することができます:のa=>a.size代わりに(a:String)=>a.size

ascii-symbolsを識別子として使用します。

これらにはが含まれます!%&/?+*~'-^<>|。彼らは手紙を聞かないので、彼らは手紙の隣にいるとき、彼らは分離されて解析されます。

例:

a=>b       //ok
%=>%        //error, parsed as one token
% => %      //ok
val% =3     //ok
&contains+  //ok
if(x)&else* //ok

含むのではなくセットを使用する

if (Seq(1,2,3,'A')contains x)... //wrong
if (Set(1,2,3,'A')(x))...         //right

これは可能ですSet[A] extends (A => Boolean)

2つの引数が必要な場合は、カリー化された関数を使用します。

(a,b)=>... //wrong
a=>b=>...  //right

_可能な場合は-syntaxを使用します

これのルールはやや曖昧です。最短の方法を見つけるために、時々遊んでいる必要があります。

a=>a.map(b=>b.size)) //wrong
a=>a.map(_.size)     //better
_.map(_.size)        //right

部分的なアプリケーションを使用する

a=>a+1 //wrong
_+1    //better, see above
1+     //right; this treats the method + of 1 as a function

""+代わりに使用toString

a=>a.toString //wrong
a=>a+""       //right

文字列をシーケンスとして使用する

"" アクチュアタイプを気にしない場合、空のシーケンスを作成する最も簡単な方法である場合があります

BigIntを使用して、数字と文字列を変換します

10以外の基数で数値を文字列に変換する最短の方法は、BigIntのtoString(base: Int)メソッドを使用することです

Integer.toString(n,b) //wrong
BigInt(n)toString b   //right

文字列を数値に変換する場合は、使用します BigInt.apply(s: String, base: Int)

Integer.parseInt(n,b) //wrong
BigInt(n,b)           //right

これはBigIntを返すことに注意してください。これはほとんどの場合に使用できますが、たとえばシーケンスのインデックスとして使用することはできません。

Seqを使用してシーケンスを作成する

a::b::Nil   //wrong
List(...)   //also wrong
Vector(...) //even more wrong
Seq(...)    //right
Array(...)  //also wrong, except if you need a mutable sequence

文字のシーケンスに文字列を使用する:

Seq('a','z') //wrong
"az"         //right

無限シーケンスにストリームを利用する

いくつかの課題は、無限シーケンスのn番目の要素を要求します。ストリームはこれに最適な候補です。ことを忘れないでくださいStream[A] extends (Int => A)ストリームは、そのインデックスの要素のインデックスからの機能である、つまり、。

Stream.iterate(start)(x=>calculateNextElement(x))

対応する単語の代わりに記号演算子を使用する

:\そして:/代わりにfoldRightfoldLeft

a.foldLeft(z)(f) //wrong
(z/:a)(f)        //right
a.foldRight(z)(f) //wrong
(a:\z)(f)         //right

hashCode -> ##

throw new Error() -> ???

&andの|代わりに&&andを使用します||

ブール値でも同じように機能しますが、常に両方のオペランドを評価します

関数としての別名longメソッド

def r(x:Double)=math.sqrt(x) //wrong
var r=math.sqrt _            //right; r is of type (Double=>Double)

標準ライブラリの関数を知っている

これは特にコレクションのメソッドに適用されます。

非常に便利な方法は次のとおりです。

map
flatMap
filter
:/ and :\ (folds)
scanLeft and scanRight
sliding
grouped (only for iterators)
inits
headOption
drop and take
collect
find
zip
zipWithIndex3
distinct and/or toSet
startsWith

11

何かを繰り返す最短の方法はSeq.fillです。

1 to 10 map(_=>println("hi!")) // Wrong!
for(i<-1 to 10)println("hi!") // Wrong!
Seq.fill(10)(println("hi!")) // Right!

10

疑わしい識別子:?

使用できますか?識別子として:

val l=List(1,2,3)
val? =List(1,2,3)

ここでは、等号に固定できないため、何も保存されません。

val ?=List(1,2,3) // illegal

ただし、後で区切り文字が必要ないため、多くの場合1文字が保存されます。

print(?size)  // l.size needs a dot
def a(? :Int*)=(?,?tail).zipped.map(_-_)

ただし、次を使用するのは難しい場合があります。

       print(?size)
3
       print(?size-5)
<console>:12: error: Int does not take parameters
       print(?size-5)
              ^

9

コレクション

ランダムコレクションの最初の選択肢は、多くの場合Listです。多くの場合、これをSeqに置き換えることができます。これにより、1文字のインスタントが保存されます。:)

の代わりに

val l=List(1,2,3)
val s=Seq(1,2,3)

また、通常のコードでs(0)はs.headとs.tailはよりエレガントですが、ここでも1文字短くなっていs.headます。

場合によってはさらに短くなります-必要な機能に応じてタプルがあります:

val s=Seq(1,2,3)
val t=(1,2,3)

3文字をすぐに保存し、アクセスするために:

s(0)
t._1

直接インデックスアクセスの場合も同じです。ただし、詳細な概念の場合、タプルは失敗します。

scala> s.map(_*2)
res55: Seq[Int] = List(2, 4, 6)

scala> t.map(_*2)
<console>:9: error: value map is not a member of (Int, Int, Int)
       t.map(_*2)
         ^

更新

def foo(s:Seq[Int])
def foo(s:Int*)

パラメーター宣言では、Int *はSeq [Int]に4文字を保存します。同等ではありませんが、Int *で十分な場合があります。


8

通常は次のmap代わりに使用できますforeach

List("a","b","c") foreach println

に置き換えることができます

List("a","b","c") map println

唯一の違いは戻り値のタイプ(Unitvs List[Unit])で、これはを使用する場合にはとにかく興味がありませんforeach


7

より短いタイプを定義する:

次のような型の宣言が複数ある場合

def f(a:String,b:String,c:String) 

型エイリアスを定義し、代わりに使用する方が短いです:

type S=String;def f(a:S,b:S,c:S)

元の長さは3 * 6 = 18です。置換コードは8(タイプS =;)+ 6 + 3 * 1(=新しい長さ)= 17です。

(n * length <8 + length + n)の場合、それは利点です。

ファクトリを介してインスタンス化されるクラスの場合、そのオブジェクトを指すように短い変数名を設定できます。の代わりに:

val a=Array(Array(1,2),Array(3,4))

私たちは書くことができます

val A=Array;val a=A(A(1,2),A(3,4))

これは#define、たとえばC ++にも当てはまりますが、それは良いことでdefありval、より短いことを認めます。
マシュー

ふむ defはメソッドを定義するためのキーワードであり、c ++ forへの単純な変換valは 'const'であり、それは宣言ですが、型はしばしば推測されます。短縮は、最初のケースtype=ではに近いtypedef-そうではありませんか?2番目の例は私からのものではなく、私にとって新しいものです。私はそれをどこで使用するかに気をつけなければなりません。
ユーザー不明

typedef long long ll;#define ll long longはと同じなので、後者は1だけ短くなりますが、typedef動作します。val例をもう一度見ると、私は間違いなく誤解しています。Scala特有ではないようです。 x = thingWithAReallyLongComplicatedNameForNoReasonはかなり一般的な戦略です:P
マシュー

@userunknown 構文でListor Arrayなどをインスタンス化するときは、オブジェクトのメソッドをval x = List(1,2,3)呼び出すだけです。(オブジェクト作成のこの手法は、でコンストラクタを使用するのとは対照的に、「ファクトリーメソッド」として知られています。)上記では、変数nameと同じシングルトンオブジェクトを指す新しい変数を作成しています。同じことなので、を含むすべてのメソッドが利用可能です。applyListnewArrayapply
ルイージプリンジ

7

中置構文を使用して、.文字の必要性を取り除きます。隣接する項目が両方とも英数字または演算子文字(ここを参照)であり、予約文字(ブラケット、コンマなど)で区切られていない限り、スペースは必要ありません。

例えば

List(1,2,3,4).filter(_ % 2 == 0) // change to:
List(1,2,3,4)filter(_%2==0)

7

リテラルは、などの書き込みに短く、真のためにと偽のためにtruefalse2>11>2


7

初期化のために同じ関数を2回呼び出します。

val n,k=readInt

(他の場所で見たが、今は見つからない)。


6

名前が長く、複数回使用されている場合、メソッドの名前を変更します-実際の例:

 x.replaceAll(y,z)

 type S=String; def r(x:S,y:S,z:S)=x.replaceAll(y,z)

さまざまな場所で「S = String」を保存する可能性にもよりますが、少なくともreplaceAllを3回交換する場合、これは経済的です。


3

タプルを使用して複数の変数を一度に初期化します。

var(a,b,c)=("One","Two","Three") //32 characters

var a="One";var b="Two";var c="Three" //37 characters

0

関数定義に使用する代わりに使用することもでき=>ます。


1
こんにちは、PPCGへようこそ。ほとんどの場合、回答は文字ではなくバイトでカウントされるため、ヒントの範囲は限られています。これに対処し、文字数に基づくコードゴルフチャレンジの関数定義の短縮などのヒントタイトルを追加します。
ジョナサンフレッチ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.