Dicas para jogar golfe em Clean


17

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 .

Respostas:


10

Evite import StdEnvquando possível

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.

Esta dica foi usada com sucesso aqui .


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]+[/]

4

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 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).


3

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 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.


3

Conheça seus nós K

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.


1

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

1

Use lambdas mais curtas

À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.


1
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'].

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!']

0

Às vezes codeé mais curto

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.

Alguns exemplos:

Hora do sistema

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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.