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


24

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

Будь ласка, опублікуйте одну пораду за кожну відповідь.


11
Ви можете розраховувати на нас - якщо це задумане
Conor O'Brien

9
Будемо сподіватися, що кілька хороших порад швидко прийдуть.
DJMcMayhem

4
Швидкий гольф? Я думав, що гольф повинен був бути спокійною, спокійною грою ...
Джодмо

Відповіді:


6

Діапазони

Одне, що дуже корисно - це створення діапазонів за допомогою операторів ...або..<

Наприклад

array[0..<n] //first n elements of array
array[k..<n] //kth through nth elements of array
array[k...n] //kth through n-1 elements of array

Отже, щоб отримати значення 2–4-го масиву

let myArray = ["ab", "cd", "ef", "gh", "ij", "kl", "mn", "op"]
print(myArray[1..<4]) //["cd", "ef", "gh"]

Практичне використання

let a = [3, 1, 4, 1, 5, 9]

Використання

for v in a[0...3]{print(v)}

На 8 байт коротше, ніж

for n in 0...3{let v=a[n];print(v)}

4
for n in 0...3{print(a[n])}Не вірно?
Джуліан Вовк

4

Закриття:

Використання змінних, що утримують функцію, а також використання самої функції може допомогти:

65 байт:

var r:(String,Int)->String={return String(repeating:$0,count:$1)}

66 байт:

func r(s:String,i:Int)->String{return String(repeating:s,count:i)}

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

Функції скорочення:

Перегляд попереднього прикладу мені щось нагадує. Іноді, якщо ви будете використовувати функцію достатньо разів, варто перейменувати її:

Це:

String(repeating:$0,count:$1)

До цього:

var r:(String,Int)->String={return String(repeating:$0,count:$1)}

Або, власне, це краще:

var r=String.init(repeating:count:)

Таким чином ви просто дзвоните r("Hello World",8)замістьString(repeating:"Hello World",count:8)

Вихід із декларацій типу:

Я одного разу створив закриття, не встановлюючи тип аргументу, таким чином створивши коротшу відповідь:

var f={(i)->Int in i-1+i%2*2}

Компілятор зробив висновок, що iє в Int.

Швидкий шлях створення масивів:

Якщо вам потрібен масив Ints, використовуйте a, Rangeщоб створити його:

Array(0...5)

Це робить те саме, що:

[0,1,2,3,4,5]

Масиви замість Ifабо Switch:

Замість цього:

if n==0{return "a"}else if n==1{return "b"}else{return "c"}

Можливо, ви можете це зробити:

return ["a","b","c"][n]

Скоротити типи:

Якщо ви багато використовуєте конверсію типів, ви можете створити псевдонім типу:

typealias f=Float

Карта:

Пам'ятайте, що часто не потрібно використовувати returnключове слово у mapфункції.

Запуск Swift Online:

Хоча Try It Online не підтримує Swift It зараз !


2
Дякую за пост. Мені дуже допомагали. tio.run/nexus зараз працює з швидким :)
palme

3

try

У Swift 2.x і вище функції, які традиційно обробляють помилки, передаючи вказівник на NSErrorоб’єкт як параметр функції, тепер throwїх помилка.

Це означає, що це:

var regex = NSRegularExpression(pattern: "\"((http)s?://.*?)\"", options: nil, error: nil)

тепер виглядає так:

do {
    let regex = try NSRegularExpression(pattern: "\"((http)s?://.*?)\"", options: [])
} catch {
    print(error)
}

Це можна скоротити за допомогою try?або try!. try?буде оцінювати вираз, nilякщо помилка викинута. try!призведе до виходу з ладу вашої програми, якщо буде видалена помилка, і її слід використовувати лише у випадках, коли помилка ніколи не буде видалена.

let regex = try! NSRegularExpression(pattern: "\"((http)s?://.*?)\"", options: [])

try?і try!збережіть принаймні 13 байт з do-try-catchциклу. Зауважте, що ви також зберігаєте принаймні ще один байт, передаючи в порожній масив ( []) параметри, а не nilтакож.


3

Скорочення масивів

Ітерація за for-in loopsдопомогою масиву для отримання єдиного значення, такого як сума елементів всередині або добуток його елементів, може бути занадто довгою, наскільки це насправді просто. Можна просто скористатися reduce()методом. Деякі приклади:

var array = [1,2,3,4,5,6,7]

Додавання елементів у масив із for-inпетлями:

var sum = 0

for item in array{
    sum += item
}
print(sum)

можна спростити:

print(array.reduce(0, +))

І отримання добутку елементів всередині масиву за допомогою циклів for-in:

var multiplier = 1
for item in array{
    multiplier *= item
}
print(multiplier)

можна також зменшити до:

print(array.reduce(1, *))

Ви не втрачаєте більше байтів, оголошуючи **функцію, ніж просто powвручну дзвонили ?
JAL

@JAL так, але якщо ви викликаєте Pow 10 разів, це економить невелику кількість байтів. Насправді не найкраща техніка для гольфу, але це лише ще одна потенційна допомога. Це було призначене не для конкретного пароха, а для інших операцій, таких як пошук простих чисел, якщо ви це зробите 3 рази, це економить величезну кількість байтів
Містер Xcoder

2

Потрійний оператор Свіфта дуже короткий: condition ? action : otherдія

Якщо умова справжня, зробіть одне, якщо ні, зробіть щось інше.

textColor = bgIsBlack ? .white : .black

Це робить textColor білим, якщо фон чорним, або чорним, якщо фон є будь-яким іншим кольором.

Оператор згортання нуля ще коротше: a ?? b

Скажімо, ви перевіряєте JSON для певного ключа, щоб ви могли представити значення ключа як текст заголовка. Якщо ключ відсутній (тобто значення дорівнює нулю), ми хочемо вказати заголовок тексту за замовчуванням.

title = keysValue ?? "Not Available"

2

Перерахування

Ви можете ланцюг forEachз enumerated()по типу колекції , щоб отримати посилання на об'єкт (або тип значення) в колекції, а також його індекс:

[1,2,3,4,5].enumerated().forEach{print($0,$1)}

або

for (c,i) in [1,2,3,4,5].enumerated(){print(c,i)}

або (ще коротший CountableClosedRangeсинтаксис)

(1...5).enumerated().forEach{print($0,$1)}

Друкує:

0 1
1 2
2 3
3 4
4 5

2

Підрядка

Іноді ви можете зберегти байти, повернувшись до типів фундаменту, а не використовуючи чисті типи Swift. Порівняйте доступ до підрядка типу NSStringvs та Swift String:

let x:NSString = "hello world"
x.substringToIndex(5) // "hello"

let y = "hello world"
y.substringToIndex(y.startIndex.advancedBy(5)) // "hello"

Навіть при втраті 9 символів, оголосивши xяк NSString, ви заощаджуєте ще 25, використовуючи тип Foundation, оскільки substringToIndexприймає Intяк параметр для NSString, a Indexstruct ( String.CharacterView.Index) для SwiftString типів .

Слід зазначити, що доступність типів Foundation може відрізнятися на різних платформах (OS X, Linux тощо). Більшість класів Foundation є NSUnimplementedу відкритій версії Swift.


2

.map ()

Поєднання .map()із синтаксисом закриття в кінці може затягувати цикл for-циклів. Ми можемо помістити речі, які хочемо повторити, у масив, а потім використати .map()для виконання певних дій над кожним елементом.

Наприклад, ми можемо використати, .map()щоб записати той старий каштан, «Фізбуз», в один рядок.

var d: [Int] = Array(1...100)

d.map{$0%15 == 0 ? print("Fizzbuzz") : $0%3 == 0 ? print("Fizz") : $0%5 == 0 ? print("Buzz") : print($0)}

Поза гольфом .map()може допомогти скоротити повторення. Наприклад, припустимо, у вас є перегляд, який потрібно позиціонувати програмно. Ви можете помістити якір для подання в анонімний масив і перейти .map()на нього, щоб встановити кожне обмеження .isActiveна істинне, наприклад:

_ = [
        view.topAnchor.constraint(equalTo: view.topAnchor, constant: 40),
        view.widthAnchor.constraint(equalTo: view.widthAnchor),
        view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ].map { $0.isActive = true }

1
Хіба не краще використовувати forEachу своєму другому прикладі? mapнасправді слід використовувати для перетворення вмісту масиву, а не як ярлик ітерації. У цьому випадку ви відкидаєте результат.
JAL

1

Гольфінг змінних призначень в структурах управління потоком з використанням кортежів

Подумайте, що ви хочете використовувати whileцикл, і ви хочете використовувати те саме, що і в умові, і в блоці, який слід виконати. Тоді вбудоване призначення в кортежі, швидше за все, допоможе. Чим довше ваш атрибут, тим краще! Розглянемо це (на 3 байти коротше):

func f(s:[Int]){var k=s,i=0;while(i=k.count,i>0).1{print(i,i+k[i-1]);k.removeLast();}}

над цим:

func g(s:[Int]){var k=s,i=0;while k.count>0{i=k.count;print(i,i+k[i-1]);k.removeLast();}}

Зауважте (i=k.count,i>0).1деталь, яка досить цікава.


Натхненний одним з Herman Lauenstein в відповідях .


1

Повторні рядки

На жаль, Swift не підтримує множення рядків за допомогою *Python. Хороший метод, який ви можете використовувати замість цього, є String(repeating:count:), але, на жаль, це насправді не гольфі. Порівняйте ці два підходи:

var a=String(repeating:"abc",count:3)

і

var a="";for _ in 0..<3{a+="abc"}

Другий - на пару байтів коротший, але його не можна використовувати для закриття ... Ще краще, і він також працює у закриттях:

(0..<3).map{_ in"abc"}.joined()

А що робити, якщо я це роблю кілька разів? Ну, можна використовувати String.init(). Тепер це може зекономити багато байтів. Наприклад (68 байт):

let k=String.init(repeating:count:)
print(k("abcd",9)+k("XYZxyz",9))

замість (74 байти):

print(String(repeating:"abcd",count:9)+String(repeating:"XYZxyz",count:9))

або (70 байт):

var f={String(repeating:$0,count:$1)}
print(f("abcd",9)+f("XYZxyz",9))

Але переконайтеся, що ваша струна досить довга. Якщо ви використовуєте String(repeating:"abc",3), набагато краще "abcabcabc"замість цього використовувати .


+1, оскільки я не мав уявлення, що змінні можуть бути подібними до ініціалізаторів.
Даніель

1

Імпорт

Ви можете замінити import Foundationз import UIKitна 5 байт коротше, так як UIKit робить імпорт Foundation вже.


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