Поради щодо гольфу в Чистому


17

Які загальні поради щодо гольфу в Чисті? Будь ласка, публікуйте лише ідеї, які можна застосувати до коду проблем із гольфом загалом і принаймні дещо конкретні для Clean.

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

Відповіді:


10

Уникайте, import StdEnvколи це можливо

Щоб отримати доступ до вбудованим функціям, навіть , здавалося б , основні з них , як (==)і map, потрібно оператор імпорту, як правило , import StdEnvтому , що вона імпортує найбільш поширені модулі , як StdInt, StdBoolі так далі (див тут для отримання додаткової інформації про StdEnv).

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

Наприклад, замість

import StdEnv 
map f list

можна писати

[f x\\x<-list]

Список альтернатив:

Деякі функції або виклики функцій, які потребують import StdEnv, альтернатива, яка не потребує імпорту, і приблизна оцінка збережених байтів.

  • hd-> (\[h:_]=h), ~ 6 байт
  • tl-> (\[_:t]=t), ~ 6 байт
  • map f list-> [f x\\x<-list], ~ 10 байт
  • filter p list-> [x\\x<-list|p x], ~ 11 байт
  • (&&)-> %a b|a=b=a;%, ~ 6 байт
  • (||)-> %a b|a=a=b;%, ~ 6 байт
  • not-> %a|a=False=True;%, ~ 1 байт
  • and-> %[a:r]|a= %r=a;%_=True, ~ 0 байт
  • or-> %[a:r]|a=a= %r;%_=False, ~ 0 байт

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

Ця порада була успішно використаний тут .


Чи не import StdEnv+ a and b(21 байт) менше %[a:r]|a= %r=a;%_=True(22 байти)? Або це було б import StdEnv+ a=True and b=True(31 байт), в такому випадку він дійсно коротше? (Я ніколи не програмував у Clean, btw.)
Kevin Cruijssen

@KevinCruijssen Ми просто обговорювали це у чаті . Це правда, що вони навряд чи збережуть байти, за винятком випадків, коли програмі все одно потрібно повторити список.
Лайконі

4
Добренько. Можливо, також може бути корисно вказати, скільки байтів збережено за допомогою альтернативи (тобто map f list -> [f x\\x<-list] (11 bytes saved)(або чогось подібного)).
Кевін Кройсейсен

@KevinCruijssen Готово.
Лайконі

5

Знати, як вивчити мову

Зрештою, як може хтось гольфу мовою, якою він не може користуватися!

Інтернет

Clean не є відомою або добре задокументованою мовою, і це ім'я, безумовно, не дозволяє легко знайти дуже потрібні ресурси для усунення цих проблем ... чи це?

Спочатку Clean називався Concurrent Clean , який досі використовується в передмові майже кожного документа, пов’язаного з Clean - тому, якщо ви шукаєте Clean, шукайте замість Concurrent Clean.

Однією з найбільш чудових схожностей Clean з Haskell (яких існує багато) є існування Cloogle , яка є пошуковою функцією, що охоплює бібліотеки, з якими Clean Ship .

Місцево

Бібліотеки, з якими «Очищується», мають форму пристойно коментованих, дещо самодокументованих файлів «Чистий вихід», які можна переглядати за допомогою IDE.
(Він також поставляється з повним прикладом програм, під $INSTALL/Examples.)

Якщо говорити про це, то версія Windows Clean Clean оснащена IDE - хоча вона досить обмежена сучасними стандартами, це світ краще, ніж використання текстового редактора та командного рядка.
Дві найкорисніші функції (в контексті навчання):

  • Ви можете двічі клацнути помилку, щоб побачити, на якій лінії знаходиться
  • Ви можете виділити ім'я модуля і натиснути, [Ctrl]+[D]щоб відкрити файл визначення (або використовувати [Ctrl]+[I]для файлу реалізації), а також перемикатися між файлом визначення та реалізацією за допомогою[Ctrl]+[/]

4

Забудьте про кодування символів

Компілятор Clean не переймається тим, яке кодування, на вашу думку, зберегло вихідний файл, як і значення байтів у файлі. Це має деякі акуратні наслідки.

У тілі вихідного коду дозволено лише байти з кодовими точками, що відповідають друкованим символам ASCII, крім тих, для яких \t\r\n.

Література:

В Stringі [Char]літерали ( "stuff"і ['stuff']відповідно), будь-які байти , крім 0 допускається, із застереженням , що "і 'повинні бути екрановані (для Stringі , [Char]відповідно), і що нові рядки і провізна повертається повинні бути замінені \nі \r(також відповідно).

У Charлітералах дозволений будь-який байт, крім 0 , тобто:

'\n'

'
'

Ті самі, але другий на один байт коротший.

Втеча:

За винятком стандартних пробілів літер \t\r\n(тощо), всі нечислові послідовності відхилення в «Чистому» - це або для косої риски, або для цитати, що використовується для обмеження прямої літери .

Для числових послідовностей виходу число трактується як вісімкове значення, що закінчується після трьох цифр. Це означає, що якщо ви хочете, щоб нуль супроводжувався символом 1у a String, вам потрібно використовувати "\0001"(або "\0\61"), а не "\01" . Однак якщо слідкувати за втечею будь-чим, крім цифр, ви можете опустити провідні нулі.

Наслідки:

Ця химерність того, як Clean обробляє вихідні файли, дозволяє Stringі ['Char']ефективно стати послідовностями базових 256 одноцифрових чисел - яка має безліч застосувань для коду-гольфу, таких як зберігання індексів (до 255, звичайно).


3

Назвіть функції з символами

При визначенні функції часто коротше використовувати якусь комбінацію, !@$%^&*~-+=<:|>.?/\ніж використовувати буквено-цифрові символи, оскільки це дозволяє опускати пробіл між ідентифікаторами.

Наприклад: ?a=a^2коротше f a=a^2, а виклик також коротше.

Однак :

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

Наприклад: ?a+?bрозбирає як? a +? b

Додатково:

Можна імпортувати імпортовані ідентифікатори в Очистити, і тому єдиними односимвольними ідентифікаторами символів, які ще не використовуються, StdEnvє @$?. Переписування ^-+(тощо) може бути корисним, якщо вам потрібні більше символічних ідентифікаторів, але будьте обережні, щоб не перезаписати той, який ви використовуєте.


3

Знайте свої K вузли

Деякі з найсильніших конструкцій (для гольфу) на функціональних мовах є let ... in ....
Чисто, звичайно, є це, і щось краще - своє #.

Що таке вузол?

Clean's #і всюдисущий |(захист шаблону) обидва відомі як "вирази вузлів".
В Зокрема, вони дозволяють програмувати imperatively- МІГ в Clean (що дуже добре тут!).

#(Хай-раніше):

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

f s=let i=toInt s;n=sum[toInt c\\c<-:s]in n*i

f s#i=toInt s
#s=sum[toInt c\\c<-:s]
=s*i

Зверніть увагу на те, наскільки версія з програмою #коротша, і як ми можемо переосмислити її s. Це корисно, якщо нам не потрібне значення, яке має змінна, коли ми її отримуємо, тому ми можемо просто повторно використовувати ім’я. ( letможе виникнути проблеми, коли ви це зробите)

Але користуватися letпростіше, коли потрібно щось подібнеflip f = let g x y = f y x in g

|(Шаблон охоронець):

Захист шаблону Clean може бути використаний як у багатьох інших функціональних мовах, однак він також може бути використаний як імператив if ... else .... І більш коротка версія потрійного виразу.

Наприклад, всі вони повертають знак цілого числа:

s n|n<>0|n>0=1= -1
=0

s n=if(n<>0)if(n>0)1(-1)0

s n|n>0=1|n<0= -1=0

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

Нотатка:

Ви можете використовувати ці вирази в основному де завгодно. В лямбда, case ... of, let ... inі т.д.


1

Якщо ви використовуєте a, Stringви повинні використовуватиText

Перетворення на струни та маніпулювання струнами ( {#Char}/ Stringвид, а не [Char]вид) досить тривалий і поганий для гольфу. У Textмодулі виправляє це.

Конверсія:

Textвизначає оператор <+для будь-яких двох типів, які були toStringвизначені.
Цей оператор, який використовується як a<+bі те саме, toString a+++toString b- зберігає принаймні 19 байт . Навіть якщо ви включите додатковий імпорт, ,Textі використовуєте його лише один раз, він все одно економить 14 байт!

Маніпуляція:

Textвизначає кілька скоб для маніпуляції з рядками, яких немає у StdEnv:

  • Оператор +для рядків, який значно коротший, ніж +++(від StdEnv)
  • indexOf, з С-подібною поведінкою повернення -1замість Nothingвідмови
  • concat, що об'єднує список рядків
  • join, яка приєднується до списку рядків, використовуючи розділовий рядок
  • split, який розбиває рядок на список рядків на підрядку

1

Використовуйте коротші лямбдахи

Іноді ви опиняєтесь, використовуючи лямбдаський вираз (переходити до mapабо sortByтощо). Коли ви це робите (пишете лямбда), є маса способів зробити це.

Правильний шлях:

Це sortByз ідіоматичним лямбда-сортуванням списків від найдовших до найкоротших

sortBy (\a b = length a > length b)

Інший правильний шлях:

Якщо ви використовуєте Data.Func, ви також можете зробити

sortBy (on (>) length)

Короткий шлях:

Це те саме, але з синтаксисом гольфіста

sortBy(\a b=length a>length b)

Інший спосіб:

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

sortBy(\a=(>)(length a)o length)

Інший інший спосіб:

Хоча це трохи надумано тут, ви можете використовувати охоронців у лямбдах

sortBy(\a b|length a>length b=True=False)

А також висловлювання перед вузлом

sortBy(\a b#l=length
=l a>l b)

Нотатка:

Є ще дві форми лямбда, (\a b . ...)і (\a b -> ...)остання з яких ідентична =варіанту, і перша з яких існує чомусь і часто виглядає так, що ви намагаєтесь отримати доступ до властивості чогось, а не визначати лямбду, так що не використовую.


1
Після перегляду деяких з ваших golfed програм, я отримав враження , \a=... був звичайний синтаксис Clean в лямбда: P
Ørjan Йохансен

Ви також можете додати охоронців у лямбда, як тут використовується . Це недокументовано (навіть суперечить мовній доповіді), але працює. Крім того, ->і =для лямбдів однакові, що стосується компілятора ( ->це старий синтаксис). Тільки .інше (але я не знаю точно як).

І в цьому конкретному прикладі ви можете розглянути можливість використання on(<)length, хоча Data.Funcімпорт порушить вас, якщо вам це вже не потрібно.

@Keelan Cool. Я оновлю це пізніше сьогодні. Я думаю, ви також можете використовувати let-before ( #) в лямбдах.
Οurous

Так, ви можете :-)

0

Використовуйте літерали зі списку символів

Список літери є скороченим способом писати що - щось на зразок , ['h','e','l','l','o']як ['hello'].

Це не межа позначень, наприклад:

  • repeat'c'стає ['c','c'..]стає['cc'..]
  • ['z','y'..'a'] стає ['zy'..'a']
  • ['beginning']++[a,b,c]++['end'] стає ['beginning',a,b,c,'end']
  • ['prefix']++suffix стає ['prefix':suffix]

Вони також працюють у відповідності:

  • ['I don't care about the next character',_,'but I do care about these ones!']

0

Іноді codeкоротше

У стандартних бібліотеках Clean є дуже багато корисних функцій, деякі з яких неймовірно багатослівні для використання без доступу *World, а використання *Worldв коді-гольфу взагалі погана ідея.

Щоб подолати цю проблему, часто ccallможна використовувати codeзамість них всередині блоків.

Деякі приклади:

Системний час

import System.Time,System._Unsafe
t=toInt(accUnsafe(time))

Наведене вище - 58 байт, але ви можете зберегти 17 байт (до 40 + 1) за допомогою:

t::!Int->Int
t _=code{ccall time "I:I"
}

Випадкові числа

Цей не зберігає байти самостійно, але уникає необхідності проходити навколо списку genRandInt

s::!Int->Int
s _=code{ccall time "I:I"ccall srand "I:I"
}
r::!Int->Int
r _=code{ccall rand "I:I"
}

Інші види використання

На додаток до цих двох, які, мабуть, є основними для цього в коді-гольф, ви можете викликати будь-яку названу функцію (включаючи, але не обмежуючись кожним систематичним викликом), вбудовувати довільну збірку instruction <byte>та вставляти код для машини ABC.

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