Conseils pour jouer au golf à Scala

24

Quels conseils avez-vous pour jouer au golf à Scala? Je recherche des idées qui peuvent être appliquées aux problèmes de golf de code en général qui sont au moins quelque peu spécifiques à Scala (par exemple, "supprimer les commentaires" n'est pas une réponse). Veuillez poster un pourboire par réponse.

(Ceci est une copie sans vergogne de ... en Python)

Utilisateur inconnu
la source

Réponses:

5

Avertissement: certaines parties de ces réponses sont des généralisations d'autres réponses trouvées ici.

Utiliser des lambdas sans spécifier leurs types d'arguments

Il est permis de soumettre quelque chose comme ceci: a=>a.sizeau lieu de (a:String)=>a.size.

Utilisez des symboles ascii comme identificateurs.

Cela comprend !%&/?+*~'-^<>|. Parce qu'ils ne sont pas des lettres, ils sont séparés séparément lorsqu'ils sont à côté des lettres.

Exemples:

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

Utiliser Set au lieu de contient

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

C'est possible parce que Set[A] extends (A => Boolean).

Utilisez une fonction au curry lorsque vous avez besoin de deux arguments.

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

Utilisez la _syntaxe -si possible

Les règles pour cela sont quelque peu obscures, il faut parfois jouer un peu pour trouver le chemin le plus court.

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

Utiliser une application partielle

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

Utiliser ""+au lieu detoString

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

Utiliser des chaînes comme séquences

"" est parfois le moyen le plus court de créer une séquence vide si vous ne vous souciez pas du type d'actula

Utilisez BigInt pour convertir des nombres vers et depuis des chaînes

La méthode la plus courte pour convertir un nombre en chaîne dans une base autre que la base 10 est la toString(base: Int)méthode BigInt

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

Si vous souhaitez convertir une chaîne en nombre, utilisez BigInt.apply(s: String, base: Int)

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

Sachez que cela renvoie un BigInt, qui est utilisable comme un nombre la plupart du temps, mais ne peut pas être utilisé comme index pour une séquence, par exemple.

Utilisez Seq pour créer des séquences

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

Utilisez des chaînes pour les séquences de caractères:

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

Utilisez Stream pour des séquences infinies

Certains défis demandent le n-ième élément d'une séquence infinie. Stream est le candidat idéal pour cela. N'oubliez pas que Stream[A] extends (Int => A), c'est-à-dire, un flux est une fonction d'un index à l'élément de cet index.

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

Utilisez des opérateurs symboliques au lieu de leurs homologues verbeux

:\et :/au lieu de foldRightetfoldLeft

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

hashCode -> ##

throw new Error() -> ???

Utilisez &et |au lieu de &&et||

Ils fonctionnent de la même manière pour les booléens, mais évalueront toujours les deux opérandes

Alias ​​méthode longue en tant que fonctions

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

Connaître les fonctions de la bibliothèque standard

Cela s'applique particulièrement aux méthodes de collecte.

Les méthodes très utiles sont:

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
la source
11

La façon la plus courte de répéter quelque chose est avec 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
la source
10

identifiant suspect:?

Vous pouvez utiliser ? comme identifiant:

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

Ici, cela ne vous sauve rien, car vous ne pouvez pas le coller au signe égal:

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

Mais plus tard, il enregistre souvent un caractère, car vous n'avez pas besoin de délimiteur:

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

Cependant, il est souvent difficile à utiliser:

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

Les collections

Le premier choix pour une collection aléatoire est souvent List . Dans de nombreux cas, vous pouvez le remplacer par Seq , qui enregistre un caractère instantané. :)

Au lieu de

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

et, tandis que s.head et s.tail sont plus élégants dans le code habituel, s(0)est à nouveau un caractère plus court que s.head.

Encore plus court dans certains cas - en fonction des fonctionnalités nécessaires est un tuple:

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

enregistrer 3 caractères immédiatement, et pour accéder à:

s(0)
t._1

il en est de même pour l'accès direct aux index. Mais pour les concepts élaborés, les tuples échouent:

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

mise à jour

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

Dans la déclaration de paramètre, Int * enregistre 4 caractères sur Seq [Int]. Ce n'est pas équivalent, mais parfois, Int * fera l'affaire.

Utilisateur inconnu
la source
8

Vous pouvez généralement utiliser mapau lieu de foreach:

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

peut être remplacé par

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

La seule différence est le type de retour ( Unitvs List[Unit]), qui ne vous intéresse pas de toute façon lors de l'utilisation foreach.

Luigi Plinge
la source
7

définir des types plus courts:

Si vous avez plusieurs déclarations d'un type, comme

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

il est plus court de définir un alias de type et de l'utiliser à la place:

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

La longueur d'origine est 3 * 6 = 18 Le code de remplacement est 8 (type S =;) + 6 + 3 * 1 (= nouvelle longueur) = 17

si (n * longueur <8 + longueur + n), alors c'est un avantage.

Pour les classes instanciées via une fabrique, nous pouvons définir un nom de variable plus court pour pointer vers cet objet. Au lieu de:

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

nous pouvons écrire

val A=Array;val a=A(A(1,2),A(3,4))
Utilisateur inconnu
la source
Cela s'applique également au C ++ avec #definepar exemple, mais j'avoue que c'est bien cela defet qu'ils valsont plus courts.
Matthieu lu
Hm. defest le mot clé pour définir une méthode, et une simple traduction en c ++ pour val«const», et c'est une déclaration, mais le type est souvent déduit. Le raccourcissement est dans le premier cas le type=plus proche typedef- n'est-ce pas? Le deuxième exemple ne vient pas de moi et c'est nouveau pour moi. Je dois faire attention, où l'utiliser.
utilisateur inconnu
typedef long long ll;est le même que #define ll long long, donc celui-ci est plus court de 1. Mais oui, typedefça marche. En regardant à valnouveau l' exemple, je l'ai certainement mal lu. Cela semble encore moins spécifique à Scala. x = thingWithAReallyLongComplicatedNameForNoReasonest une stratégie assez générale: P
Matthew Lire
@userunknown Lorsque vous instanciez un Listou Arrayetc avec une syntaxe, val x = List(1,2,3)vous appelez simplement la applyméthode sur l' Listobjet. (Cette technique de création d'objet est connue sous le nom de "méthode d'usine", contrairement à l'utilisation d'un constructeur avec new.) Donc, ci-dessus, nous créons simplement une nouvelle variable qui pointe vers le même objet singleton que le nom de la variable Array. Comme c'est la même chose, toutes les méthodes, y compris apply, sont disponibles.
Luigi Plinge,
7

Utilisez la syntaxe infixe pour supprimer le besoin de .caractères. Vous n'avez pas besoin d'espaces sauf si les éléments adjacents sont à la fois en alphanumérique ou en caractères d'opérateur (voir ici ), et non séparés par des caractères réservés (crochets, virgules, etc.).

Par exemple

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

Les littéraux trueet falsesont plus courts à écrire que 2>1pour le vrai et le 1>2faux

triggerNZ
la source
7

Appelez deux fois la même fonction pour l'initialisation:

val n,k=readInt

(Vu ailleurs, mais ne le trouve pas maintenant).

Utilisateur inconnu
la source
6

Renommez les méthodes, si leur nom est long et si elles sont utilisées plusieurs fois - exemple réel:

 x.replaceAll(y,z)

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

En fonction de la possibilité d'enregistrer 'S = String' à différents endroits également, cela ne sera économique que si vous remplacez au moins replaceAll 3 fois.

Utilisateur inconnu
la source
3

Initialisez plusieurs variables à la fois à l'aide d'un tuple:

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

contre.

var a="One";var b="Two";var c="Three" //37 characters
randak
la source
0

Vous pouvez également utiliser au lieu d'utiliser =>pour les définitions de fonction.

Varon
la source
1
Bonjour et bienvenue chez PPCG. Comme la plupart du temps, les réponses sont comptées en octets plutôt qu'en caractères, votre astuce n'a qu'une portée limitée. Je voudrais aborder cela et ajouter également un titre de conseil comme raccourcir les définitions de fonction dans les défis de golf de code basés sur le nombre de caractères .
Jonathan Frech