Que dicas gerais você tem para jogar golfe no Clean? Poste apenas idéias que possam ser aplicadas aos problemas de golfe em geral e que sejam pelo menos um pouco específicas para o Clean.
Se você nunca ouviu falar de Clean, pode descobrir mais aqui .
Ou você pode entrar na sala de bate - papo .
Para acessar funções internas, mesmo as aparentemente básicas, como (==)ou map, é necessária uma instrução de importação, geralmente import StdEnvporque importa os módulos mais comuns StdInt, StdBoolcomo assim por diante (veja aqui para obter mais informações StdEnv).
No entanto, pode ser possível evitar essa importação para alguns desafios e apenas usar os recursos principais da linguagem, como compreensão de lista e correspondência de padrões.
Por exemplo, em vez de
import StdEnv
map f list
alguém pode escrever
[f x\\x<-list]
Lista de alternativas:
Algumas funções ou invocações de funções que precisam import StdEnv, uma alternativa que não precisa da importação e uma estimativa aproximada de bytes salvos.
hd-> (\[h:_]=h), ~ 6 bytes
tl-> (\[_:t]=t), ~ 6 bytes
map f list-> [f x\\x<-list], ~ 10 bytes
filter p list-> [x\\x<-list|p x], ~ 11 bytes
(&&)-> %a b|a=b=a;%, ~ 6 bytes
(||)-> %a b|a=a=b;%, ~ 6 bytes
not-> %a|a=False=True;%, ~ 1 byte
and-> %[a:r]|a= %r=a;%_=True, ~ 0 bytes
or-> %[a:r]|a=a= %r;%_=False, ~ 0 bytes
É improvável que os últimos salvem bytes, porque uma substituição direta gera mais bytes que a importação, mas pode ser possível nos casos em que a recursão na lista seja necessária de qualquer maneira.
No entanto, import StdEnv+ a and b(21 bytes) não é menor que %[a:r]|a= %r=a;%_=True(22 bytes)? Ou seria import StdEnv+ a=True and b=True(31 bytes); nesse caso, é definitivamente mais curto? (Eu nunca programado em Limpo, btw.)
Kevin Cruijssen
@KevinCruijssen Estávamos discutindo isso no chat . É verdade que é improvável que esses bytes sejam salvos, exceto, talvez, quando o programa precisar recursar sobre uma lista de qualquer maneira.
Laikoni
4
Ah ok. Talvez também seja útil indicar quantos bytes são salvos com a alternativa (ou seja, map f list -> [f x\\x<-list] (11 bytes saved)(ou algo semelhante)).
Kevin Cruijssen
@KevinCruijssen Done.
Laikoni
5
Saiba como aprender a língua
Afinal, como alguém pode jogar golfe em um idioma que não pode usar!
Conectados
Clean não é uma linguagem conhecida ou bem documentada, e o nome certamente não facilita a localização dos recursos necessários para solucionar esses problemas ... ou não?
O Clean era originalmente chamado de Limpeza Simultânea , que ainda é usado no prefácio de quase todos os documentos relacionados à Limpeza - portanto, se você estiver procurando por Limpeza, procure por Limpeza Simultânea.
Uma das semelhanças mais notáveis de Clean com Haskell (das quais existem muitas) é a existência do Cloogle , que é um mecanismo de busca de funções que cobre as bibliotecas com as quais o Clean é fornecido.
Localmente
As bibliotecas com as quais o Clean é fornecido estão na forma de arquivos de origem limpa com comentários decentes e um pouco auto-documentáveis, que podem ser navegados usando o IDE.
(Ele também vem com programas de exemplo completos, em $INSTALL/Examples.)
Falando nisso, a versão para Windows do Clean vem com um IDE - embora seja bastante limitado pelos padrões modernos, é um mundo melhor do que usar um editor de texto e a linha de comando.
Os dois recursos mais úteis (no contexto do aprendizado) são:
Você pode clicar duas vezes em um erro para ver em qual linha está
Você pode destacar um nome de módulo e pressionar [Ctrl]+[D]para abrir o arquivo de definição (ou usá-lo [Ctrl]+[I]para o arquivo de implementação) e alternar entre o arquivo de definição e implementação com[Ctrl]+[/]
O compilador do Clean não se importa com a codificação que você acha que salvou o arquivo de origem, apenas com os valores de bytes no arquivo. Isso tem algumas conseqüências legais.
No corpo do código-fonte, somente bytes com pontos de código correspondentes aos caracteres ASCII imprimíveis são permitidos, além dos caracteres \t\r\n.
Literais:
Em Stringe [Char]literais ( "stuff"e ['stuff'], respectivamente), nenhum bytes excepto 0 são permitidos, com a ressalva de que "e 'deve ser precedido (por Stringe [Char], respectivamente), e que as novas linhas e retorna Carraige deve ser substituído com \ne \r(também respectivamente).
Em Charliterais, qualquer byte, exceto 0, é permitido, o que significa que:
'\n'
'
'
São os mesmos, mas o segundo é um byte mais curto.
Escapando:
Além da letra padrão de escape \t\r\n(etc.), todas as seqüências de escape não numéricas no Clean são para a barra ou para a cotação usada para delimitar o literal em que o escape está dentro.
Para seqüências de escape numéricas, o número é tratado como um valor octal finalizado após três dígitos. Isso significa que, se você deseja um nulo seguido pelo caractere 1a String, precisa usar "\0001"(ou "\0\61") e não"\01" . No entanto, se você seguir a fuga com qualquer coisa, exceto números, poderá omitir os zeros à esquerda.
Consequências:
Essa peculiaridade de como o Clean lida com seus arquivos de origem permite Stringe ['Char']efetivamente se torna sequências de números de um dígito com base 256 - que tem uma infinidade de usos para golfe de código, como armazenar índices (até 255, é claro).
Ao definir uma função, geralmente é mais curto usar alguma combinação do !@$%^&*~-+=<:|>.?/\que caracteres alfanuméricos, porque permite omitir o espaço em branco entre os identificadores.
Por exemplo: ?a=a^2é menor que f a=a^2, e invocá-lo também é menor.
No entanto :
Se o identificador da função for usado adjacente a outros símbolos, que podem ser combinados para formar um identificador diferente, mas válido , todos serão analisados como um identificador e você verá um erro.
Por exemplo: ?a+?banalisa como? a +? b
Além disso:
É possível substituir identificadores importados em Limpo, e assim os únicos identificadores símbolo de um carácter que já não são utilizados na StdEnvestão @$?. A substituição ^-+(etc.) pode ser útil se você precisar de mais identificadores simbólicos, mas tenha cuidado para não substituir um que esteja usando.
Algumas das construções mais fortes (para o golfe) em linguagens funcionais são let ... in ....
Limpo, claro, tem isso, e algo melhor - o #.
O que é um nó?
Clean's #e onipresente |(padrão de guarda) são conhecidos como 'expressões de nó'.
Notavelmente, eles permitem programar imperatively- ish em Limpo (que é realmente bom aqui!).
O #(let-before):
Ambos calculam o valor de um número inteiro dado como uma string, multiplicado pela soma de seus caracteres
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
Observe como a versão com #é mais curta e como podemos redefinir s. Isso é útil se não precisarmos do valor que uma variável possui quando a recebemos, para que possamos apenas reutilizar o nome. ( letpode ter problemas quando você faz isso)
Mas usar leté mais fácil quando você precisa de algo comoflip f = let g x y = f y x in g
O |(protetor de padrão):
A proteção de padrões do Clean pode ser usada como em muitas outras linguagens funcionais - no entanto, também pode ser usada como um imperativo if ... else .... E uma versão mais curta da expressão ternária.
Por exemplo, todos eles retornam o sinal de um número inteiro:
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
Obviamente, o último que usa a proteção mais tradicionalmente é o mais curto, mas o primeiro mostra que você pode aninhar eles (mas apenas duas cláusulas de retorno incondicionais podem aparecer na mesma linha na regra de layout), e o segundo mostra o que o primeiro faz logicamente.
Uma nota:
Você pode usar essas expressões basicamente em qualquer lugar. Em lambdas, case ... of, let ... in, etc.
Se você estiver usando um, Stringvocê deve estar usandoText
Conversão para strings e manipulação de strings (do tipo {#Char}/ Stringnão do [Char]tipo) é bastante longa e ruim para o golfe. O Textmódulo corrige isso.
Conversão:
Textdefine o operador <+para dois tipos toStringdefinidos.
Este operador, usado como a<+bé o mesmo que toString a+++toString b- salva pelo menos 19 bytes . Mesmo que você inclua a importação extra ,Text, e a use apenas uma vez, ela ainda salva 14 bytes!
Manipulação:
Textdefine alguns grampos de manipulação de cadeia que estão ausentes StdEnv:
O operador +para seqüências de caracteres, que é muito menor que +++(de StdEnv)
indexOf, com o comportamento tipo C de retornar em -1vez de Nothingfalhar
concat, que concatena uma lista de cadeias
join, que une uma lista de cadeias usando uma cadeia separadora
split, que divide uma string em uma lista de strings em uma substring
Às vezes, você se vê usando uma expressão lambda (para passar para map, ou sortByetc.). Quando você está fazendo isso (escrevendo lambdas), há várias maneiras de fazer isso.
O caminho certo:
Isto é sortBy, com uma lista de classificação lambda idiomática do maior para o menor
sortBy (\a b = length a > length b)
O outro caminho certo:
Se você estiver usando Data.Func, você também pode fazer
sortBy (on (>) length)
O caminho mais curto:
É a mesma coisa, mas com uma sintaxe de golfista
sortBy(\a b=length a>length b)
O outro jeito:
O uso da composição não é mais curto desta vez, mas às vezes pode ser mais curto
sortBy(\a=(>)(length a)o length)
A outra maneira:
Embora seja um pouco artificial aqui, você pode usar guardas em lambdas
sortBy(\a b|length a>length b=True=False)
E também expressões de nó anteriores
sortBy(\a b#l=length
=l a>l b)
Uma nota:
Existem mais duas formas de lambda, (\a b . ...)e (\a b -> ...)a última é idêntica à =variante, e a primeira existe por algum motivo e geralmente parece que você está tentando acessar uma propriedade de alguma coisa em vez de definir uma lambda, então não Não use.
Depois de ver alguns dos seus programas golfed, eu tinha tenho a impressão \a=...era sintaxe lambda habitual de Limpeza: P
Ørjan Johansen
Você também pode adicionar os guardas em lambda, como usado aqui . Isso não está documentado (até contradiz o relatório do idioma), mas funciona. Além disso, ->e =para lambdas são idênticos no que diz respeito ao compilador ( ->é uma sintaxe antiga). Só .é diferente (mas não sei exatamente como).
E neste exemplo em particular, você pode considerar o uso on(<)length, embora a Data.Funcimportação o interrompa, a menos que você já precise.
@Keelan Cool. Vou atualizar isso hoje mais tarde. Eu acho que você também pode usar let-before ( #) em lambdas.
Οurous
Sim, você pode :-)
0
Usar literais da lista de caracteres
Um literal da lista de caracteres é uma maneira abreviada de escrever algo como ['h','e','l','l','o']as ['hello'].
O Clean possui várias funções realmente úteis nas bibliotecas padrão, algumas das quais são incrivelmente detalhadas para serem usadas sem acesso *World, e o uso *Worldno code-golf geralmente é uma má idéia de qualquer maneira.
Para contornar esse problema, geralmente existem ccalls que você pode usar dentro de codeblocos.
O acima é de 58 bytes, mas você pode salvar 17 bytes (até 40 + 1) com:
t::!Int->Int
t _=code{ccall time "I:I"
}
Números aleatórios
Este não salva bytes por si só, mas evita passar uma lista de genRandInt
s::!Int->Int
s _=code{ccall time "I:I"ccall srand "I:I"
}
r::!Int->Int
r _=code{ccall rand "I:I"
}
Outros usos
Além desses dois, que provavelmente são os principais usos para isso no code-golf, você pode chamar qualquer função nomeada (incluindo, mas não se limitando a cada syscall), incorporar assembléia arbitrária instruction <byte>e código da máquina ABC.
import StdEnv
+a and b
(21 bytes) não é menor que%[a:r]|a= %r=a;%_=True
(22 bytes)? Ou seriaimport StdEnv
+a=True and b=True
(31 bytes); nesse caso, é definitivamente mais curto? (Eu nunca programado em Limpo, btw.)map f list -> [f x\\x<-list] (11 bytes saved)
(ou algo semelhante)).Saiba como aprender a língua
Afinal, como alguém pode jogar golfe em um idioma que não pode usar!
Conectados
Clean não é uma linguagem conhecida ou bem documentada, e o nome certamente não facilita a localização dos recursos necessários para solucionar esses problemas ... ou não?
O Clean era originalmente chamado de Limpeza Simultânea , que ainda é usado no prefácio de quase todos os documentos relacionados à Limpeza - portanto, se você estiver procurando por Limpeza, procure por Limpeza Simultânea.
Uma das semelhanças mais notáveis de Clean com Haskell (das quais existem muitas) é a existência do Cloogle , que é um mecanismo de busca de funções que cobre as bibliotecas com as quais o Clean é fornecido.
Localmente
As bibliotecas com as quais o Clean é fornecido estão na forma de arquivos de origem limpa com comentários decentes e um pouco auto-documentáveis, que podem ser navegados usando o IDE.
(Ele também vem com programas de exemplo completos, em
$INSTALL/Examples
.)Falando nisso, a versão para Windows do Clean vem com um IDE - embora seja bastante limitado pelos padrões modernos, é um mundo melhor do que usar um editor de texto e a linha de comando.
Os dois recursos mais úteis (no contexto do aprendizado) são:
[Ctrl]+[D]
para abrir o arquivo de definição (ou usá-lo[Ctrl]+[I]
para o arquivo de implementação) e alternar entre o arquivo de definição e implementação com[Ctrl]+[/]
fonte
Esqueça a codificação de caracteres
O compilador do Clean não se importa com a codificação que você acha que salvou o arquivo de origem, apenas com os valores de bytes no arquivo. Isso tem algumas conseqüências legais.
No corpo do código-fonte, somente bytes com pontos de código correspondentes aos caracteres ASCII imprimíveis são permitidos, além dos caracteres
\t\r\n
.Literais:
Em
String
e[Char]
literais ("stuff"
e['stuff']
, respectivamente), nenhum bytes excepto 0 são permitidos, com a ressalva de que"
e'
deve ser precedido (porString
e[Char]
, respectivamente), e que as novas linhas e retorna Carraige deve ser substituído com\n
e\r
(também respectivamente).Em
Char
literais, qualquer byte, exceto 0, é permitido, o que significa que:São os mesmos, mas o segundo é um byte mais curto.
Escapando:
Além da letra padrão de escape
\t\r\n
(etc.), todas as seqüências de escape não numéricas no Clean são para a barra ou para a cotação usada para delimitar o literal em que o escape está dentro.Para seqüências de escape numéricas, o número é tratado como um valor octal finalizado após três dígitos. Isso significa que, se você deseja um nulo seguido pelo caractere
1
aString
, precisa usar"\0001"
(ou"\0\61"
) e não"\01"
. No entanto, se você seguir a fuga com qualquer coisa, exceto números, poderá omitir os zeros à esquerda.Consequências:
Essa peculiaridade de como o Clean lida com seus arquivos de origem permite
String
e['Char']
efetivamente se torna sequências de números de um dígito com base 256 - que tem uma infinidade de usos para golfe de código, como armazenar índices (até 255, é claro).fonte
Funções de nome com símbolos
Ao definir uma função, geralmente é mais curto usar alguma combinação do
!@$%^&*~-+=<:|>.?/\
que caracteres alfanuméricos, porque permite omitir o espaço em branco entre os identificadores.Por exemplo:
?a=a^2
é menor quef a=a^2
, e invocá-lo também é menor.No entanto :
Se o identificador da função for usado adjacente a outros símbolos, que podem ser combinados para formar um identificador diferente, mas válido , todos serão analisados como um identificador e você verá um erro.
Por exemplo:
?a+?b
analisa como? a +? b
Além disso:
É possível substituir identificadores importados em Limpo, e assim os únicos identificadores símbolo de um carácter que já não são utilizados na
StdEnv
estão@$?
. A substituição^-+
(etc.) pode ser útil se você precisar de mais identificadores simbólicos, mas tenha cuidado para não substituir um que esteja usando.fonte
Conheça seus nós
KAlgumas das construções mais fortes (para o golfe) em linguagens funcionais são
let ... in ...
.Limpo, claro, tem isso, e algo melhor - o
#
.O que é um nó?
Clean's
#
e onipresente|
(padrão de guarda) são conhecidos como 'expressões de nó'.Notavelmente, eles permitem programar imperatively- ish em Limpo (que é realmente bom aqui!).
O
#
(let-before):Ambos calculam o valor de um número inteiro dado como uma string, multiplicado pela soma de seus caracteres
Observe como a versão com
#
é mais curta e como podemos redefinirs
. Isso é útil se não precisarmos do valor que uma variável possui quando a recebemos, para que possamos apenas reutilizar o nome. (let
pode ter problemas quando você faz isso)Mas usar
let
é mais fácil quando você precisa de algo comoflip f = let g x y = f y x in g
O
|
(protetor de padrão):A proteção de padrões do Clean pode ser usada como em muitas outras linguagens funcionais - no entanto, também pode ser usada como um imperativo
if ... else ...
. E uma versão mais curta da expressão ternária.Por exemplo, todos eles retornam o sinal de um número inteiro:
Obviamente, o último que usa a proteção mais tradicionalmente é o mais curto, mas o primeiro mostra que você pode aninhar eles (mas apenas duas cláusulas de retorno incondicionais podem aparecer na mesma linha na regra de layout), e o segundo mostra o que o primeiro faz logicamente.
Uma nota:
Você pode usar essas expressões basicamente em qualquer lugar. Em lambdas,
case ... of
,let ... in
, etc.fonte
Se você estiver usando um,
String
você deve estar usandoText
Conversão para strings e manipulação de strings (do tipo
{#Char}
/String
não do[Char]
tipo) é bastante longa e ruim para o golfe. OText
módulo corrige isso.Conversão:
Text
define o operador<+
para dois tipostoString
definidos.Este operador, usado como
a<+b
é o mesmo quetoString a+++toString b
- salva pelo menos 19 bytes . Mesmo que você inclua a importação extra,Text
, e a use apenas uma vez, ela ainda salva 14 bytes!Manipulação:
Text
define alguns grampos de manipulação de cadeia que estão ausentesStdEnv
:+
para seqüências de caracteres, que é muito menor que+++
(deStdEnv
)indexOf
, com o comportamento tipo C de retornar em-1
vez deNothing
falharconcat
, que concatena uma lista de cadeiasjoin
, que une uma lista de cadeias usando uma cadeia separadorasplit
, que divide uma string em uma lista de strings em uma substringfonte
Use lambdas mais curtas
Às vezes, você se vê usando uma expressão lambda (para passar para
map
, ousortBy
etc.). Quando você está fazendo isso (escrevendo lambdas), há várias maneiras de fazer isso.O caminho certo:
Isto é
sortBy
, com uma lista de classificação lambda idiomática do maior para o menorO outro caminho certo:
Se você estiver usando
Data.Func
, você também pode fazerO caminho mais curto:
É a mesma coisa, mas com uma sintaxe de golfista
O outro jeito:
O uso da composição não é mais curto desta vez, mas às vezes pode ser mais curto
A outra maneira:
Embora seja um pouco artificial aqui, você pode usar guardas em lambdas
E também expressões de nó anteriores
Uma nota:
Existem mais duas formas de lambda,
(\a b . ...)
e(\a b -> ...)
a última é idêntica à=
variante, e a primeira existe por algum motivo e geralmente parece que você está tentando acessar uma propriedade de alguma coisa em vez de definir uma lambda, então não Não use.fonte
\a=...
era sintaxe lambda habitual de Limpeza: P->
e=
para lambdas são idênticos no que diz respeito ao compilador (->
é uma sintaxe antiga). Só.
é diferente (mas não sei exatamente como).on(<)length
, embora aData.Func
importação o interrompa, a menos que você já precise.#
) em lambdas.Usar literais da lista de caracteres
Um literal da lista de caracteres é uma maneira abreviada de escrever algo como
['h','e','l','l','o']
as['hello']
.Este não é o limite da notação, por exemplo:
repeat'c'
torna-['c','c'..]
se torna- se['cc'..]
['z','y'..'a']
torna-se['zy'..'a']
['beginning']++[a,b,c]++['end']
torna-se['beginning',a,b,c,'end']
['prefix']++suffix
torna-se['prefix':suffix]
Também funcionam em correspondência:
['I don't care about the next character',_,'but I do care about these ones!']
fonte
Às vezes
code
é mais curtoO Clean possui várias funções realmente úteis nas bibliotecas padrão, algumas das quais são incrivelmente detalhadas para serem usadas sem acesso
*World
, e o uso*World
no code-golf geralmente é uma má idéia de qualquer maneira.Para contornar esse problema, geralmente existem
ccall
s que você pode usar dentro decode
blocos.Alguns exemplos:
Hora do sistema
O acima é de 58 bytes, mas você pode salvar 17 bytes (até 40 + 1) com:
Números aleatórios
Este não salva bytes por si só, mas evita passar uma lista de
genRandInt
Outros usos
Além desses dois, que provavelmente são os principais usos para isso no code-golf, você pode chamar qualquer função nomeada (incluindo, mas não se limitando a cada syscall), incorporar assembléia arbitrária
instruction <byte>
e código da máquina ABC.fonte