Поради щодо гольфу в Скалі


24

Які загальні поради щодо гольфу в Скалі? Я шукаю ідеї, які можна застосувати до коду проблем із гольфом взагалі, які принаймні дещо характерні для Scala (наприклад, "видалити коментарі" - це не відповідь). Будь ласка, опублікуйте одну пораду за кожну відповідь.

(Це безсоромна копія ... на Python)

Відповіді:


5

відмова від відповідальності: частини цих відповідей - це узагальнення інших відповідей, знайдених тут.

Використовуйте лямбда, не вказуючи їхні типи аргументів

Дозволяється подавати щось подібне: a=>a.sizeзамість (a:String)=>a.size.

Використовуйте символи ascii як ідентифікатори.

До них належать !%&/?+*~'-^<>|. Оскільки вони не букви, вони розбиваються окремо, коли поруч із літерами.

Приклади:

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

Використовуйте Set, а не містить

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

Це можливо тому, що Set[A] extends (A => Boolean).

Використовуйте функцію curried, коли вам потрібні два аргументи.

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

Використовуйте _-синтаксис, коли це можливо

Правила цього дещо незрозумілі, вам доведеться трохи пограти навколо, щоб знайти найкоротший шлях.

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, - це за допомогою toString(base: Int)методу BigInt

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

Використовуйте символічні оператори замість їх багатослівних аналогів

:\і :/замість foldRightіfoldLeft

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

hashCode -> ##

throw new Error() -> ???

Використовуйте &і |замість &&і||

Вони працюють однаково для булевих, але завжди будуть оцінювати обидва операнди

Псевдонім довгий метод як функції

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

Але пізніше це часто зберігає один символ, оскільки вам не потрібен роздільник:

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

Колекції

Першим вибором для випадкової колекції часто є Список . У багатьох випадках ви можете замінити його Seq , що зберігає один символ миттєвості. :)

Замість

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

і, хоча s.head і s.tail є більш елегантним у звичайному коді, s(0)він знову на один символ коротший 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 * зберігає 4 символи над Seq [Int]. Це не рівнозначно, але іноді 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 * довжина <8 + довжина + n), то це є перевагою.

Для класів, які інстанціюються через фабрику, ми можемо встановити коротше ім’я змінної для вказівки на цей об’єкт. Замість:

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

ми можемо писати

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

Це стосується і C ++, як, #defineнаприклад, але я визнаю, що це приємно, defі valвони коротші.
Матвій

Гм. defє ключовим словом для визначення методу, а простим перекладом на c ++ для valє "const", і це декларація, але тип часто визначається. Скорочення - це в першому випадку те, type=що ближче typedef- чи не так? Другий приклад не від мене, і він для мене новий. Я повинен стежити, де його використовувати.
користувач невідомий

typedef long long ll;такий же, як #define ll long long, тому останній коротший на 1. Але так, typedefпрацює. Переглядаючи valприклад ще раз, я точно його неправильно прочитав. Це здається навіть менш характерним для Scala. x = thingWithAReallyLongComplicatedNameForNoReasonє досить загальною стратегією: P
Матвій

@userunknown Коли ви створюєте копію синтаксису a Listабо Arrayetc, val x = List(1,2,3)ви просто викликаєте applyметод на Listоб'єкті. (Ця методика створення об'єкта відома як "заводський метод", на відміну від використання конструктора з new.) Отже, вище ми просто робимо нову змінну, яка вказує на той самий однотонний об'єкт, що і ім'я змінної Array. Оскільки це одне й те саме, всі методи, в тому числі apply, доступні.
Луїджі Плінге

7

Використовуйте синтаксис infix, щоб усунути потребу в .символах. Пробіли вам не потрібні, якщо суміжні елементи є або буквено-цифровими, або обома символами оператора (див. Тут ), і не розділені зарезервованими символами (дужки, коми тощо).

Напр

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

7

Ці trueта falseлитералов коротше , щоб писати , як 2>1для істинного і 1>2за неправдиві



6

Перейменуйте методи, якщо їх ім’я довге і якщо вони використовуються кілька разів - приклад із реального світу:

 x.replaceAll(y,z)

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

Залежно від можливості збереження "S = String" в різних місцях, це буде економічно лише в тому випадку, якщо ви заміните принаймні 3 рази.


3

Ініціалізуйте відразу кілька змінних, використовуючи кортеж:

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

vs.

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

0

Ви також можете використовувати замість використання =>для визначення функцій.


1
Привіт і ласкаво просимо до PPCG. Оскільки більшу частину часу відповіді зараховуються в байтах, а не символах, у вашої підказки є лише обмежена область застосування. Я би вирішив це, а також додати назву підказки, як, наприклад, визначення функцій скорочення в підрахунку символів для гольфу на основі кількості символів .
Джонатан Фрех
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.