Какие общие советы у вас есть для игры в гольф в Clean? Пожалуйста, публикуйте только идеи, которые могут быть применены к кодовым проблемам гольфа в целом, и, по крайней мере, несколько специфичны для Clean.
Если вы никогда не слышали о Чистоте, вы можете узнать больше здесь .
Или вы можете присоединиться к чату .
Чтобы получить доступ к встроенным функциям, даже , казалось бы , основные из них , как (==)и 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 байт), и в этом случае он действительно определенно будет короче? (
Между
@KevinCruijssen Мы только что обсуждали это в чате . Это правда, что они вряд ли сохранят байты, за исключением, может быть, когда программе все равно нужно пересмотреть список.
Лайкони
4
Ах хорошо. Возможно, было бы также полезно указать, сколько байтов сохранено с альтернативой (то есть map f list -> [f x\\x<-list] (11 bytes saved)(или что-то подобное)).
Кевин Круйссен
@KevinCruijssen Готово.
Лайкони
5
Знать, как выучить язык
В конце концов, как любой может играть в гольф на языке, который они не могут использовать!
В сети
Чистый язык не является общеизвестным или хорошо документированным языком, и название определенно не облегчает поиск столь необходимых ресурсов для устранения этих проблем ... или так?
Первоначально Clean назывался Concurrent Clean , который до сих пор используется в предисловии почти к каждому документу, связанному с Clean, поэтому, если вы ищете Clean, вместо этого ищите Concurrent Clean.
Одним из наиболее примечательных сходств Clean с Haskell (из которых их много) является существование Cloogle , функции поисковой системы, охватывающей библиотеки, с которыми поставляется Clean.
в местном масштабе
Библиотеки, с которыми поставляется Clean, представлены в виде прилично прокомментированных, несколько самодокументированных исходных файлов Clean, которые можно просматривать с помощью IDE.
(Это также идет с полными примерами программ, под $INSTALL/Examples.)
Говоря об этом, версия Clean для Windows поставляется с IDE - хотя она довольно ограничена современными стандартами, она лучше, чем использование текстового редактора и командной строки.
Две наиболее полезные функции (в контексте обучения):
Вы можете дважды щелкнуть по ошибке, чтобы увидеть, какая строка
Вы можете выделить имя модуля и нажать, [Ctrl]+[D]чтобы открыть файл определения (или использовать [Ctrl]+[I]для файла реализации), и переключаться между файлом определения и реализацией с помощью[Ctrl]+[/]
Компилятору Clean не важно, в какой кодировке вы сохранили исходный файл, просто в значениях байтов в файле. Это имеет некоторые аккуратные последствия.
В теле исходного кода разрешены только байты с кодовыми точками, соответствующими печатным символам ASCII, в дополнение к тем, для которых \t\r\n.
литералы:
В Stringи [Char]Литералы ( "stuff"и ['stuff']соответственно) разрешены любые байты, кроме 0 , с оговоркой, что "и 'должны быть экранированы (для Stringи [Char]соответственно), и что новые строки и возвраты Carraige должны быть заменены на \nи \r(также соответственно).
В Charлитералах допускается любой байт, кроме 0 , что означает, что:
'\n'
'
'
То же самое, но второй на один байт короче.
Экранирование:
Кроме стандартных экранированных букв \t\r\n(и т. Д.), Все нечисловые экранирующие последовательности в Clean предназначены либо для косой черты, либо для кавычки, используемой для разграничения литерала, внутри которого находится экранированная буква .
Для числовых escape-последовательностей число обрабатывается как восьмеричное значение, оканчивающееся после трех цифр. Это означает, что если вы хотите, чтобы за нулем следовал символ 1в String, вам нужно использовать "\0001"(или "\0\61"), а не"\01" . Однако, если вы следите за выходом с чем-либо, кроме чисел, вы можете опустить ведущие нули.
Последствия:
Эта причуда с тем, как Clean обрабатывает свои исходные файлы, позволяет Stringи ['Char']эффективно превращается в последовательности из однозначных чисел с 256 базовыми числами, которые имеют множество применений для игры в код, например, для хранения индексов (конечно, до 255).
При определении функции часто короче использовать некоторую комбинацию, !@$%^&*~-+=<:|>.?/\чем алфавитно-цифровые символы, потому что она позволяет пропустить пробел между идентификаторами.
Например: ?a=a^2короче f a=a^2, и вызов его также короче.
Однако :
Если идентификатор функции используется рядом с другими символами, которые могут объединяться для формирования другого, но действительного идентификатора, все они будут проанализированы как один идентификатор, и вы увидите ошибку.
Например: ?a+?bразбирает как? a +? b
Дополнительно:
В Clean можно перезаписать импортированные идентификаторы, поэтому единственными символьными идентификаторами символов, которые еще не используются, StdEnvявляются @$?. Перезапись ^-+(и т. Д.) Может быть полезна, если вам нужно больше символических идентификаторов, но будьте осторожны, чтобы не перезаписать тот, который вы используете.
Некоторые из самых сильных конструкций (для игры в гольф) на функциональных языках let ... in ....
Чисто конечно, есть это, а что-то лучше - то #.
Что такое узел?
Чистые #и вездесущие |(pattern guard) оба известны как «выражения узла».
В частности, они позволяют программировать 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)
Иногда вы обнаруживаете, что используете лямбда-выражение (для передачи 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 -> ...)последняя из которых идентична =варианту, а первая существует по какой-то причине и часто выглядит так, будто вы пытаетесь получить доступ к свойству чего-то, а не определяете лямбду, так что Не используй это.
После просмотра некоторых из ваших golfed программ, я получил впечатление , \a=...был обычный синтаксис Clean в лямбда: P
Ørjan Йохансен
Вы также можете добавить охрану в лямбду, как здесь . Это недокументировано (даже противоречит языку отчета), но работает. Кроме того, ->и =для лямбды идентичны, поскольку компилятор обеспокоен ( ->это старый синтаксис). Только .отличается (но я не знаю точно, как).
И в этом конкретном примере вы могли бы рассмотреть возможность использования on(<)length, хотя Data.Funcимпорт сломает вас, если вам это уже не нужно.
@ Килан Круто. Я обновлю это позже сегодня. Я думаю, что вы также можете использовать let-before ( #) в лямбдах.
Οurous
Да, вы можете :-)
0
Использовать литералы списка символов
Литерал списка символов - это сокращенный способ написать что-то вроде ['h','e','l','l','o']as ['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!']
Clean имеет множество действительно полезных функций в стандартных библиотеках, некоторые из которых невероятно многословны для использования без доступа *Worldи использования.*World в code-golf - вообще плохая идея.
Чтобы обойти эту проблему, часто ccallможно codeвместо этого использовать внутри блоков.
Выше 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"
}
Другое использование
В дополнение к этим двум, которые, вероятно, являются основным использованием для этого в code-golf, вы можете вызывать любую именованную функцию (включая, но не ограничиваясь каждым системным вызовом), вставлять произвольную сборку instruction <byte>и вставлять код для машины ABC.
import StdEnv
+a and b
(21 байт) не меньше%[a:r]|a= %r=a;%_=True
(22 байт)? Или это будетimport StdEnv
+a=True and b=True
(31 байт), и в этом случае он действительно определенно будет короче? (map f list -> [f x\\x<-list] (11 bytes saved)
(или что-то подобное)).Знать, как выучить язык
В конце концов, как любой может играть в гольф на языке, который они не могут использовать!
В сети
Чистый язык не является общеизвестным или хорошо документированным языком, и название определенно не облегчает поиск столь необходимых ресурсов для устранения этих проблем ... или так?
Первоначально Clean назывался Concurrent Clean , который до сих пор используется в предисловии почти к каждому документу, связанному с Clean, поэтому, если вы ищете Clean, вместо этого ищите Concurrent Clean.
Одним из наиболее примечательных сходств Clean с Haskell (из которых их много) является существование Cloogle , функции поисковой системы, охватывающей библиотеки, с которыми поставляется Clean.
в местном масштабе
Библиотеки, с которыми поставляется Clean, представлены в виде прилично прокомментированных, несколько самодокументированных исходных файлов Clean, которые можно просматривать с помощью IDE.
(Это также идет с полными примерами программ, под
$INSTALL/Examples
.)Говоря об этом, версия Clean для Windows поставляется с IDE - хотя она довольно ограничена современными стандартами, она лучше, чем использование текстового редактора и командной строки.
Две наиболее полезные функции (в контексте обучения):
[Ctrl]+[D]
чтобы открыть файл определения (или использовать[Ctrl]+[I]
для файла реализации), и переключаться между файлом определения и реализацией с помощью[Ctrl]+[/]
источник
Забудьте о кодировке символов
Компилятору Clean не важно, в какой кодировке вы сохранили исходный файл, просто в значениях байтов в файле. Это имеет некоторые аккуратные последствия.
В теле исходного кода разрешены только байты с кодовыми точками, соответствующими печатным символам ASCII, в дополнение к тем, для которых
\t\r\n
.литералы:
В
String
и[Char]
Литералы ("stuff"
и['stuff']
соответственно) разрешены любые байты, кроме 0 , с оговоркой, что"
и'
должны быть экранированы (дляString
и[Char]
соответственно), и что новые строки и возвраты Carraige должны быть заменены на\n
и\r
(также соответственно).В
Char
литералах допускается любой байт, кроме 0 , что означает, что:То же самое, но второй на один байт короче.
Экранирование:
Кроме стандартных экранированных букв
\t\r\n
(и т. Д.), Все нечисловые экранирующие последовательности в Clean предназначены либо для косой черты, либо для кавычки, используемой для разграничения литерала, внутри которого находится экранированная буква .Для числовых escape-последовательностей число обрабатывается как восьмеричное значение, оканчивающееся после трех цифр. Это означает, что если вы хотите, чтобы за нулем следовал символ
1
вString
, вам нужно использовать"\0001"
(или"\0\61"
), а не"\01"
. Однако, если вы следите за выходом с чем-либо, кроме чисел, вы можете опустить ведущие нули.Последствия:
Эта причуда с тем, как Clean обрабатывает свои исходные файлы, позволяет
String
и['Char']
эффективно превращается в последовательности из однозначных чисел с 256 базовыми числами, которые имеют множество применений для игры в код, например, для хранения индексов (конечно, до 255).источник
Имя функции с символами
При определении функции часто короче использовать некоторую комбинацию,
!@$%^&*~-+=<:|>.?/\
чем алфавитно-цифровые символы, потому что она позволяет пропустить пробел между идентификаторами.Например:
?a=a^2
корочеf a=a^2
, и вызов его также короче.Однако :
Если идентификатор функции используется рядом с другими символами, которые могут объединяться для формирования другого, но действительного идентификатора, все они будут проанализированы как один идентификатор, и вы увидите ошибку.
Например:
?a+?b
разбирает как? a +? b
Дополнительно:
В Clean можно перезаписать импортированные идентификаторы, поэтому единственными символьными идентификаторами символов, которые еще не используются,
StdEnv
являются@$?
. Перезапись^-+
(и т. Д.) Может быть полезна, если вам нужно больше символических идентификаторов, но будьте осторожны, чтобы не перезаписать тот, который вы используете.источник
Знай своих
KузловНекоторые из самых сильных конструкций (для игры в гольф) на функциональных языках
let ... in ...
.Чисто конечно, есть это, а что-то лучше - то
#
.Что такое узел?
Чистые
#
и вездесущие|
(pattern guard) оба известны как «выражения узла».В частности, они позволяют программировать imperatively- МОГ в Clean (что очень хорошо здесь!).
#
(Пусть-раньше):Они оба вычисляют значение целого числа, заданного в виде строки, умноженного на сумму его символов
Обратите внимание, как версия с
#
короче, и как мы можем переопределитьs
. Это полезно, если нам не нужно значение, которое имеет переменная, когда мы ее получаем, поэтому мы можем просто повторно использовать имя. (let
может возникнуть проблемы, когда вы это сделаете)Но использовать
let
легче, когда вам нужно что-то вродеflip f = let g x y = f y x in g
|
(Шаблон охранник):Защита шаблона Clean может использоваться как во многих других функциональных языках - однако она также может использоваться как императив
if ... else ...
. И более короткая версия троичного выражения.Например, все они возвращают знак целого числа:
Конечно, последний, который традиционно использует охрану, является самым коротким, но первый показывает, что вы можете вкладывать их (но только две безусловные предложения возврата могут появляться в одной строке в правиле макета), а второй показывает, что Первый делает логически.
Заметка:
Вы можете использовать эти выражения в основном где угодно. В лямбды,
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
с C-подобным поведением возврата-1
вместоNothing
сбояconcat
, который объединяет список строкjoin
, который соединяет список строк, используя строку разделителяsplit
, который разбивает строку в список строк на подстрокеисточник
Используйте более короткие лямбды
Иногда вы обнаруживаете, что используете лямбда-выражение (для передачи
map
илиsortBy
, и т. Д.). Когда вы делаете это (пишете лямбды), есть множество способов сделать это.Правильный путь:
Это
sortBy
с идиоматическими лямбда-сортировочными списками от самого длинного до самого короткогоДругой правильный путь:
Если вы используете
Data.Func
, вы также можете сделатьКороткий путь:
Это то же самое, но с синтаксисом игрока в гольф
Другой способ:
Использование композиции на этот раз не короче, но иногда может быть короче
Другой способ:
Хотя это немного надумано, вы можете использовать охрану в лямбдах
А также дай-до выражения узла
Заметка:
Есть еще две формы лямбды,
(\a b . ...)
и(\a b -> ...)
последняя из которых идентична=
варианту, а первая существует по какой-то причине и часто выглядит так, будто вы пытаетесь получить доступ к свойству чего-то, а не определяете лямбду, так что Не используй это.источник
\a=...
был обычный синтаксис Clean в лямбда: P->
и=
для лямбды идентичны, поскольку компилятор обеспокоен (->
это старый синтаксис). Только.
отличается (но я не знаю точно, как).on(<)length
, хотяData.Func
импорт сломает вас, если вам это уже не нужно.#
) в лямбдах.Использовать литералы списка символов
Литерал списка символов - это сокращенный способ написать что-то вроде
['h','e','l','l','o']
as['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
в code-golf - вообще плохая идея.Чтобы обойти эту проблему, часто
ccall
можноcode
вместо этого использовать внутри блоков.Несколько примеров:
Системное время
Выше 58 байтов, но вы можете сэкономить 17 байтов (до 40 + 1) с:
Случайные числа
Этот не сохраняет байты сам по себе, но избегает необходимости передавать список из
genRandInt
Другое использование
В дополнение к этим двум, которые, вероятно, являются основным использованием для этого в code-golf, вы можете вызывать любую именованную функцию (включая, но не ограничиваясь каждым системным вызовом), вставлять произвольную сборку
instruction <byte>
и вставлять код для машины ABC.источник