Golftipps in der Scala

24

Welche allgemeinen Tipps haben Sie zum Golfen in Scala? Ich bin auf der Suche nach Ideen, die auf Code-Golf-Probleme im Allgemeinen angewendet werden können, die zumindest etwas spezifisch für Scala sind (z. B. "Kommentare entfernen" ist keine Antwort). Bitte posten Sie einen Tipp pro Antwort.

(Dies ist eine schamlose Kopie von ... in Python)

Benutzer unbekannt
quelle

Antworten:

5

Haftungsausschluss: Teile dieser Antworten sind Verallgemeinerungen anderer Antworten, die hier zu finden sind.

Verwenden Sie Lambdas, ohne deren Argumenttypen anzugeben

Es ist erlaubt so etwas einzureichen: a=>a.sizestatt (a:String)=>a.size.

Verwenden Sie ASCII-Symbole als Bezeichner.

Dazu gehören !%&/?+*~'-^<>|. Da sie keine Buchstaben sind, werden sie getrennt analysiert, wenn sie sich neben Buchstaben befinden.

Beispiele:

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

Set anstelle von contain verwenden

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

Das ist möglich, weil Set[A] extends (A => Boolean).

Verwenden Sie eine Curry-Funktion, wenn Sie zwei Argumente benötigen.

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

Verwenden _Sie nach Möglichkeit die Syntax -syntax

Die Regeln dafür sind etwas unklar, man muss manchmal ein bisschen herumspielen, um den kürzesten Weg zu finden.

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

Teilweise anwenden

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

Verwenden Sie ""+anstelle vontoString

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

Verwenden Sie Zeichenfolgen als Sequenzen

"" Dies ist manchmal der kürzeste Weg, eine leere Sequenz zu erstellen, wenn Sie sich nicht für den Actula-Typ interessieren

Verwenden Sie BigInt, um Zahlen in und aus Zeichenfolgen zu konvertieren

Der kürzeste Weg, eine Zahl in eine Zeichenfolge in einer anderen Basis als Basis 10 umzuwandeln, ist die toString(base: Int)Methode von BigInt

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

Wenn Sie eine Zeichenfolge in eine Zahl konvertieren möchten, verwenden Sie BigInt.apply(s: String, base: Int)

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

Beachten Sie, dass dies ein BigInt zurückgibt, das die meiste Zeit wie eine Zahl verwendet werden kann, aber beispielsweise nicht als Index für eine Sequenz verwendet werden kann.

Verwenden Sie Seq, um Sequenzen zu erstellen

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

Verwenden Sie Zeichenfolgen für Zeichenfolgen:

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

Nutzen Sie Stream für unendliche Sequenzen

Einige Herausforderungen verlangen das n-te Element einer unendlichen Folge. Stream ist dafür der perfekte Kandidat. Denken Sie daran Stream[A] extends (Int => A), dass ein Stream eine Funktion von einem Index zu dem Element an diesem Index ist.

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

Verwenden Sie symbolische Operatoren anstelle ihrer wortreichen Gegenstücke

:\und :/anstelle von foldRightundfoldLeft

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

hashCode -> ##

throw new Error() -> ???

Verwenden Sie &und |anstelle von &&und||

Sie funktionieren für Boolesche Werte gleich, werten jedoch immer beide Operanden aus

Alias ​​lange Methode als Funktionen

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

Kennen Sie die Funktionen in der Standardbibliothek

Dies gilt insbesondere für die Methoden der Sammlung.

Sehr nützliche Methoden sind:

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
corvus_192
quelle
11

Der kürzeste Weg, etwas zu wiederholen, ist mit Seq.fill.

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

verdächtiger Identifikator:?

Sie können verwenden? als Kennung:

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

Hier erspart es dir nichts, weil du es nicht auf das Gleichheitszeichen beschränken kannst:

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

Später wird jedoch häufig ein Zeichen gespeichert, da Sie kein Trennzeichen benötigen:

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

Es ist jedoch oft schwierig:

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

Sammlungen

Die erste Wahl für eine zufällige Sammlung ist oft Liste . In vielen Fällen können Sie es durch Seq ersetzen , wodurch ein Zeichen augenblicklich gespeichert wird. :)

Anstatt

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

und während am kopf und am schwanz im üblichen code eleganter s(0)ist , ist nochmal ein zeichen kürzer als s.head.

In manchen Fällen sogar noch kürzer - je nach benötigter Funktionalität gibt es ein Tupel:

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

Sofortiges Speichern von 3 Zeichen und Zugriff auf:

s(0)
t._1

Dies gilt auch für den direkten Indexzugriff. Aber bei ausgearbeiteten Konzepten scheitern Tupel:

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)
         ^

aktualisieren

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

In der Parameterdeklaration speichert Int * 4 Zeichen über Seq [Int]. Es ist nicht gleichbedeutend, aber manchmal reicht Int * aus.

Benutzer unbekannt
quelle
8

Sie können normalerweise mapanstelle von foreach:

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

kann durch ersetzt werden

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

Der einzige Unterschied ist der Rückgabetyp ( Unitvs List[Unit]), den Sie bei der Verwendung sowieso nicht interessieren foreach.

Luigi Plinge
quelle
7

kürzere Typen definieren:

Wenn Sie mehrere Deklarationen eines Typs haben, wie

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

Es ist kürzer, einen Typalias zu definieren und ihn stattdessen zu verwenden:

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

Die ursprüngliche Länge beträgt 3 * 6 = 18. Der Ersatzcode lautet 8 (Typ S =;) + 6 + 3 * 1 (= neue Länge) = 17

wenn (n * länge <8 + länge + n), dann ist es ein vorteil.

Für Klassen, die über eine Factory instanziiert werden, können wir einen kürzeren Variablennamen festlegen, um auf dieses Objekt zu verweisen. Anstatt:

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

wir können schreiben

val A=Array;val a=A(A(1,2),A(3,4))
Benutzer unbekannt
quelle
Dies gilt zum Beispiel auch #definefür C ++ , aber ich gebe zu, dass es schön ist defund valkürzer ist.
Matthew Read
Hm. defist das Schlüsselwort zum Definieren einer Methode, und eine einfache Übersetzung in c ++ für vallautet 'const', und es ist eine Deklaration, aber der Typ wird oft abgeleitet. Die Verkürzung ist im ersten Fall die, type=die näher ist typedef- nicht wahr? Das zweite Beispiel ist nicht von mir und es ist neu für mich. Ich muss aufpassen, wo ich es verwenden soll.
Benutzer unbekannt
typedef long long ll;ist das gleiche wie #define ll long long, also ist letzteres um 1 kürzer. Aber ja, typedeffunktioniert. Wenn valich das Beispiel noch einmal anschaue, habe ich es definitiv falsch verstanden. Es scheint noch weniger Scala-spezifisch. x = thingWithAReallyLongComplicatedNameForNoReasonist eine ziemlich allgemeine Strategie: P
Matthew Read
@userunknown Wenn Sie ein Listoder Arrayetc mit Syntax instanziieren, val x = List(1,2,3)rufen Sie nur die applyMethode für das ListObjekt auf. (Diese Technik zur Objekterstellung wird im Gegensatz zur Verwendung eines Konstruktors mit als "Factory-Methode" bezeichnet new.) Daher erstellen wir oben nur eine neue Variable, die auf dasselbe Singleton-Objekt verweist wie der Variablenname Array. Da es dasselbe ist, stehen alle Methoden, einschließlich apply, zur Verfügung.
Luigi Plinge
7

Verwenden Sie die Infix-Syntax, um .Zeichen zu vermeiden . Sie brauchen keine Leerzeichen, es sei denn, benachbarte Elemente sind beide alphanumerisch oder beide in Operatorzeichen (siehe hier ) und nicht durch reservierte Zeichen (Klammern, Kommas usw.) getrennt.

Z.B

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

Die trueund falseLiterale sind kürzer zu schreiben als 2>1für wahr und 1>2für falsch

triggerNZ
quelle
7

Rufen Sie zur Initialisierung zweimal dieselbe Funktion auf:

val n,k=readInt

(Woanders gesehen, kann es aber jetzt nicht finden).

Benutzer unbekannt
quelle
6

Methoden umbenennen, wenn ihr Name lang ist und wenn sie mehrfach verwendet werden - Beispiel aus der Praxis:

 x.replaceAll(y,z)

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

Abhängig von der Möglichkeit, 'S = String' auch an verschiedenen Stellen zu speichern, ist dies nur dann wirtschaftlich, wenn Sie mindestens 3-mal 'replaceAll' verwenden.

Benutzer unbekannt
quelle
3

Initialisieren Sie mehrere Variablen gleichzeitig mit einem Tupel:

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

gegen

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

Sie können auch anstelle von =>für Funktionsdefinitionen verwenden.

Varon
quelle
1
Hallo und willkommen bei PPCG. Da Antworten in der Regel eher in Bytes als in Zeichen gezählt werden, hat Ihr Tipp nur einen begrenzten Umfang. Ich würde dies ansprechen und auch einen Tipp wie die Definitionen von Shortening-Funktionen in auf der Anzahl der Zeichen basierenden Code-Golf-Herausforderungen hinzufügen .
Jonathan Frech