Відповіді:
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 байтОстанні кілька навряд чи дійсно збережуть байти, оскільки пряма заміна дає більше байтів, ніж імпорт, але це може бути можливим у випадках, коли рекурсія над списком все-таки потрібна.
Ця порада була успішно використаний тут .
map f list -> [f x\\x<-list] (11 bytes saved)
(або чогось подібного)).
Зрештою, як може хтось гольфу мовою, якою він не може користуватися!
Інтернет
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]+[/]
Компілятор 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, звичайно).
При визначенні функції часто коротше використовувати якусь комбінацію, !@$%^&*~-+=<:|>.?/\
ніж використовувати буквено-цифрові символи, оскільки це дозволяє опускати пробіл між ідентифікаторами.
Наприклад: ?a=a^2
коротше f a=a^2
, а виклик також коротше.
Однак :
Якщо ідентифікатор функції використовується поруч з іншими символами, які можуть поєднуватися, утворюючи інший, але дійсний ідентифікатор, всі вони будуть проаналізовані як один ідентифікатор, і ви побачите помилку.
Наприклад: ?a+?b
розбирає як? a +? b
Додатково:
Можна імпортувати імпортовані ідентифікатори в Очистити, і тому єдиними односимвольними ідентифікаторами символів, які ще не використовуються, StdEnv
є @$?
. Переписування ^-+
(тощо) може бути корисним, якщо вам потрібні більше символічних ідентифікаторів, але будьте обережні, щоб не перезаписати той, який ви використовуєте.
Деякі з найсильніших конструкцій (для гольфу) на функціональних мовах є 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
і т.д.
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
, який розбиває рядок на список рядків на підрядкуІноді ви опиняєтесь, використовуючи лямбдаський вираз (переходити до 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 -> ...)
остання з яких ідентична =
варіанту, і перша з яких існує чомусь і часто виглядає так, що ви намагаєтесь отримати доступ до властивості чогось, а не визначати лямбду, так що не використовую.
\a=...
був звичайний синтаксис Clean в лямбда: P
->
і =
для лямбдів однакові, що стосується компілятора ( ->
це старий синтаксис). Тільки .
інше (але я не знаю точно як).
on(<)length
, хоча Data.Func
імпорт порушить вас, якщо вам це вже не потрібно.
#
) в лямбдах.
Список літери є скороченим способом писати що - щось на зразок , ['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!']
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.
import StdEnv
+a and b
(21 байт) менше%[a:r]|a= %r=a;%_=True
(22 байти)? Або це було бimport StdEnv
+a=True and b=True
(31 байт), в такому випадку він дійсно коротше? (Я ніколи не програмував у Clean, btw.)