Поради щодо гольфу в Луа


21

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


6
Поради щодо питань повинні бути вікі спільноти. Але для тих, хто подав це наближене як "насамперед на основі думки", поради щодо питань гольфу в мові - наше прийняте виняток із цього правила. Відкритий характер цих питань полягає в тому, чому вони є вікі-редакцією спільноти.
Джонатан Ван Матре

Відповіді:


9

Окрім тих, що вже були опубліковані, ось деякі трюки, які я зібрав з часом, у не конкретному порядку ...

  • Для викликів функцій, які мають лише один параметр, обмежений символом ( "для рядків, {таблиць), параметр не потрібно обертати навколо дужок.
    Наприклад, замість того, щоб робити print("hello"), ви можете просто робити:print"hello"

  • Видаліть якомога більше пробілів - це особливо легко зробити після символу, який закриває рядки (або перед одним відкриттям), виклики функцій, таблиці ...
    Замість цього print(42) a=1ви можете зробити print(42)a=1. Інший приклад: print(a and-1 or-2).

  • Використовуйте потрійного оператора, коли зможете! Замість цього if a>0 then print("hello") else print("goodbye") endвіддайте перевагу print(a>0 and "hello" or "goodbye"). Більше інформації тут .
    (Це насправді може стати ще кращим print(a>0 and"hello"or"goodbye"):)

  • Використовуйте синтаксичний цукор двокрапкової кишки, коли можете: замість цього string.rep(str,12)робити str:rep(12). Це також працює на не змінних таким чином (і тільки таким чином):("a"):rep(5)

  • Замість того, щоб робити tonumber(str)просто робитиstr+0

  • Для функцій, що не мають параметрів, замість того, щоб визначити їх звичайним способом ( function tick() blabla() end), ви можете зробити:, ticks=loadstring"blabla()"який зберігає 1 або більше байт, залежно від вмісту. Крім того, якщо ви визначите кілька функцій, локалізуйте loadstringдо 1-знакової змінної раніше, і ви збережете багато байт;). Подяка Джиму Баувенсу за цю хитрість.

  • Lua вважає порожній рядок (і 0занадто на відміну від інших мов) справжнім в умовних тестах, тому, наприклад, замість того, щоб while 1 do ... endзберегти 1 байт, записавшиwhile''do ... end


(Додано навантажувальний трюк)
Adriweb

2
0 бути правдивою цінністю просто нерозумно
SuperJedi224

інший str+0еквівалент - ~~strможе бути корисним для свого переваги
Феліпе Нарді Батіста

@FelipeNardiBatista, проте це підтримується лише в Lua 5.3+
Adriweb

5

Я вже думав про одне. Я не знаю, чи працює вона в деяких інших мовах, але Lua - це єдиний, кому я знаю, що дозволяє зберігати функції у змінних. Отже, якщо напр. string.subБагато разів використовується у вашій програмі, використовуйте напр s=string.sub.


4
Він також працює на багатьох інших мовах, таких як Python та Ruby.
nyuszika7h

4
У Javascript та Haskell можуть бути і значення функцій.
гордий haskeller

Це еквівалентно тому , s=("").subчи s=a.subдля будь-якої змінної , aщо містить значення рядка.
Єгор Скриптунов

Це називається функціями першого класу
Redwolf Programs

5

Це досить багатослівна мова для гри в гольф ... але деякі загальні поради, які приходять вам на думку:

  • Постарайтеся уникати умовних умов, оскільки if... then... else... endце велика трата.
  • Натомість намагайтеся зосередити увагу на мовних конструкціях, які є коротшими, наприклад for i=1,5 do.
  • #Оператор є досить великим для гри в гольф (і в цілому).

5

Скоротіть нескінченну петлю

Коли вам доведеться використовувати нескінченний цикл, ви можете подумати про використання while, але замість цього мітка коротша на 2 байти:

while''do end
::a::goto a

Використовуйте якомога менше місця

Існує проста річ, яку ви могли б (ab) використати, щоб видалити ще більше пробілів із коду. Характеристики Луа чіткі щодо імені, яке ви даєте змінним: Вони повинні починатися з літери. Це означає, що іноді можна пропускати пробіли між числами та функціями / змінними

x=0>1 and 0or 1print(x)

Можливість вилучення пробілу залежить від літери, яка слідує за номером, ось лист, який не дозволить вам це зробити:

a,b,c,d,e,f            -- They would be interpreted as hexadecimal
x                      -- only fail when after a 0, other number are fine
                       -- (0x indicates the following is an hexadecimal number)

Використовуючи це та звертаючи увагу на те, як ви називаєте свої змінні, ви можете зробити більшість вихідних кодів просторовими.

Збираючи приклад вже тут, і користуючись цією порадою, ось ще один байт, який ви могли б побрити :).

print(a and-1 or-2)
print(a and-1or-2)

Використовуйте правильний метод введення

Якщо ми подивимось на котлову та вартість кожного основного типу входу, ось що ми маємо:

function f(x)x end
io.read()
arg[1]

Кожен з цих методів дозволяє нам взяти 1 вхід, при цьому функція є найвищою вартістю (але дозволяє приймати таблицю як вхід)

Тепер ми можемо бачити, що використання аргументу командного рядка - це шлях, якщо ви хочете займатися гольфом, але пам’ятайте: він може бути ще коротшим

arg[1]
...

Вони ...дещо особливі в lua, це змінна, що містить розпакований вміст argабо розпаковані параметри у разі варіативної функції.

Коли вам доведеться отримати більше одного вводу та використовувати кожен з них, це може бути добре зберегти їх у змінній. Ось кілька способів збереження 2 вхідних даних у змінних

a=arg[1]b=arg[2]    -- highly un-efficient, costs 8 bytes by variable
a,b=unpack(arg)     -- costs 15, but at least doesn't depends on the number of argument
a,b=...             -- only costs 7

і ось найкоротший дзвінок, який ви могли зробити без змінних:

...     -- using a allow a gain of 1-2 bytes at each use
arg[2]  -- using b allow a gain of 4-5 bytes at each use

З моменту, коли у вас є 3 аргументи, або коли ви використовуєте 2 аргументи, причому один використовується два рази, ви вже набираєте байти завдяки a,b=...! :)

Майже ніколи не використовуйте, якщо!

Немає випадків, коли використання оператора if / elseif / if обійдеться менше, ніж потрійний. котельня для такої заяви справді важка:

-- exemple with dumb values
if 1>0then v=1 else v=0 end
v=1>0 and 1or 0

Простим прикладом ви вже економите 12 байт, коли вам доведеться робити ще кілька фішок, це стає все більш важливим, тому пам’ятайте про це!

Крім того, тернари в Луа є особливими , є певна умова, як вони працюють, для тих, хто цікавиться, я поясню це нижче:

Тернарії в луа мають форму <condition> and <case true: have to be a true value> or <case false: can be anything>

Перш за все, давайте подивимось таблицю істинності or. A orможе розглядатися як функція: воно завжди повертає значення, ось значення, яке воно повертає:

x | y ||x or y
------||-------
0 | 0 ||   y
0 | 1 ||   y
1 | 0 ||   x
1 | 1 ||   x

Саме це дозволяє нам сконструювати наш тернар.

Це andте, що дозволяє нам оцінити умову, вона завжди повернеться, yякщо x and yоцінить до істини.

Проблема з ним полягає в тому, що він не вийде, якщо ми хочемо повернути nilабо falseповернутись, коли це стан false. Наприклад, наступні завжди повертатимуть 5, незважаючи на те, що умова є істинною.

v = true and false or 5

Ось покрокове оцінювання потрійника, щоб пояснити, як він працює (це стане в нагоді, коли вам доведеться їх вкладати :))

-- let's use our dumb ternary
= true and false or 5
-- and statement will be evaluated first, leading to
= false or 5
-- and we saw how the or works
= 5

Будь ласка, одна порада на відповідь.
Атако

Зауважте, що трюк "Використовуйте якомога менше місця" працює лише у Lua 5.2 та пізніших версіях.
Adriweb

4

Я також склав кілька порад. Я впевнений, що частина моїх буде збігатися з уже заявленими, але я все-таки включу їх у русло створення більш повної відповіді.


Призначити повторні функції змінним

Lua дозволяє призначити функції змінним. Навіть одні змінні символи. Це означає, що якщо повторити функцію string.sub(x, y)більше двох разів, ви отримаєте вигоду від присвоєння їй змінної.

Без присвоєння змінній (69 символів):

print(string.sub(x, y))
print(string.sub(x, y))
print(string.sub(x, y))

Призначення змінної (51 символ):

s=string.sub
print(s(x,y))
print(s(x,y))
print(s(x,y))

Є випадки, коли ви можете зробити це ще на крок. Lua дозволяє OOP здійснювати маніпуляції з рядками, як-от так: str:sub(x, y)або str.sub(x, y)Це відкриває нові варіанти нашого коду. Ви можете призначити змінну функції за її посиланням, як показано (46 символів)

s=z.sub
print(s(x, y))
print(s(x, y))
print(s(x, y))

Використовуйте найефективніший спосіб розбору рядків

Ви можете виявити, що ви використовуєте forцикл і string.subперебираєте символи за символом у Lua. Іноді це може працювати найкраще, залежно від ваших потреб, але в інших випадках string.gmatch працюватиме з меншою кількістю символів. Ось приклад обох:

s=io.read()
for a = 1, s:len() do
    print(s:sub(a, a))
end 

for i in io.read():gmatch('.') do
    print(i)
end

А при гольфі різниця помітніша:

s=io.read()for a=1,s:len()do print(s:sub(a, a))end

for i in io.read():gmatch'.'do print(i)end

Призначення реструктуризації для оптимізації пробілів

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

  • Призначення змінних:

     x,y=io.read(),0 print(x)
     vs.
     y,x=0,io.read()print(x)
    
  • Якщо виписки:

     if x:sub(1,1)==1 then
     vs
     if 1==x:sub(1,1)then
    

Поверніть найменше можливих символів

Якщо ви повинні повернути справжнє або хибне значення, то, здається, ви обов'язково повинні використовувати принаймні 5 символів для повернутого значення. Насправді так само добре працює наступне:

return true
return false
vs
return 1>0
return 0>1

Чудові поради, я сміливо пропоную змінити вашу публікацію. Тільки nilі falseприймає значення брехні в Lua, все інше так, щоб ваші поради про заміну x==0, x==""і x==''на xхибні. Зараз я змінюю його на nil:).
Katenkyo

Ах, ти прав. Дякую, що це виправили!
Skyl3r

2

Це лише оптимізація Lua (я думаю):

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

Для переключення вмісту двох змінних не потрібна тимчасова змінна. a,b=b,aпідмінять значення a і b.

Крім того, щоб поширитись на сказане вище, будь-який буквено-цифровий символ може торкатися не буквено-цифрового символу. Так a,b=io.read():match"(.+)/(.+)"u,v=a,bце ідеальний, робочий сценарій, навіть не маючи пробілу.


2

Поєднайте локальні змінні призначення

Замість:

local a=42
local b=17
local c=99

Використовуйте паралельне призначення:

local a,b,c=42,17,19

6 байтів збережено для кожної змінної!

Декларуйте локальні змінні за допомогою параметрів невикористаної функції

Замість:

function foo(a,b) local c,d ... end
function bar(a,b) local c,d=42,17 ... end

Використовуйте

function foo(a,b,c,d) ... end
function bar(a,b,c,d) c,d=42,17 ... end

Збережено 6 байт (мінус 1-2 байти для кожної змінної, яка може бути дубльована).


1
Захищений, тому що немає абсолютно жодного випадку, коли використання localвиправданих при гольфі, тому що ви просто повинні використовувати іншу назву. Ми можемо використовувати ВСІ назви до 7 символів ТА таблиць із строковими індексами до 7 комбінацій charachter, перш ніж ми потрапимо на щось, що може отримати користь від використання місцевих жителів
Katenkyo

1

Варіатичні функції

Основна варіативна функція, яка буде вас турбувати, - це print(). Наприклад, коли ви використовуєте його уздовж, String.gsub()він буде друкувати змінений рядок І кількість gsubзапущених разів .

Щоб придушити цей другий результат, інкапсулюйте його gsubв парен, щоб змусити його повернути лише одне значення

print(String.gsub("aa",".","!"))    -- outputs "!!    2\n"
print((String.gsub("aa",".","!")))  -- outputs "!!\n"

1

Знати, як вивести

Існує два основних способи виведення в луа

io.write()    -- outputs without trailing newline
print()       -- outputs with trailing new line and shorter by 3 bytes

Коли вам доведеться об'єднати кілька разів, ви можете скоротити це, використовуючи io.write()призначену змінну однієї літери замість стандартного оператора конкатенації..

i(a)    -- i was defined as io.write
s=s..a

Ви набираєте 2 байти під час кожного дзвінка, оплачуючи при цьому деяку суму

i=io.write  -- longer by 6 bytes
s=""

Ви навіть на третьому конкатенації, і почнете набирати байт на четвертому.


3
Це так printі немає printf!
val каже Відновити Моніку

@val Wow, я навіть не знаю, як я могла зробити цю помилку. Дякую, що
вказали

1

Купа підказок у конкретному порядку:

  • stringдосить довге ім'я. Ефективно, те ('').charсаме, що string.char. Ще кращих результатів можна досягти, якщо використовувати його разом з крапкою з комою на змінних:, a=...; print(a:sub(1, 5))але деякі stringфункції не беруть рядки як вхідні дані.
  • У більшості випадків Lua має автоматичні перетворення між рядками та числами, тому часто tonumberі +0лише марнуйте байти.
  • Завжди використовуйте load'your function code here'замість function()your function code here end. Доступ до аргументів функції, використовуючи ...всередині.
  • Деякі рядкові функції в Lua можна використовувати ненавмисно! Наприклад, a:gsub('.',load'my function')здається, це найкоротший спосіб перебрати символи в рядку
  • Хоча струнний двигун потужний, остерігайтеся його потужності, використовуючи введення користувача як візерунки! Через це, можливо, вам доведеться скористатися a:find('.',1,1)(для тестування на цю проблему, спробуйте включити %в різні місця у своєму введенні та перевірити результати). Незліченна кількість ідей зламалася через те, що Луа намагався розібрати вклад як зразок.
  • nilце три байти, _це один (це просто випадкове ім'я, яке, швидше за все, не існує). Крім того, будь-яка цифра буде працювати як трибожне значення.
  • Знай свою логіку позаду x and i or o. Це не просто потрійний оператор - це повний логічний вираз. Насправді це означає наступне: "якщо xправда, спробуйте i. Якщо або x, або iлживо, поверніть o". Тож якщо iце не правда, результат є o. Також обидва andабо orчастини можна опустити ( x and i, x or o).
  • Використовуйте цілочисельне ділення на одиницю замість math.floor: 5.3//1==5.0. Зверніть увагу, що отримане число завжди відповідає типу вхідного (integer / float).

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