Dicas para jogar golfe em Julia


20

Que dicas gerais você tem para jogar golfe em Julia? Estou procurando idéias que possam ser aplicadas para codificar problemas de golfe em geral que sejam pelo menos um pouco específicos para Julia (por exemplo, "remover comentários" não é uma resposta).

Respostas:


19

NOTA: O abaixo pode conter algumas dicas desatualizadas, pois Julia ainda não está totalmente estabilizada em termos de estrutura.

Alguns truques para salvar alguns caracteres

  1. Sobrecarregar operadores com funções binárias usadas com freqüência . Por exemplo, se você precisar fazer muitas divisões inteiras e não precisar de divisão inversa, use \ =dive poderá digitar em a\bvez de div(a,b). Observe o espaço - isso é necessário para evitar a análise como um operador "\ =". Observe também que, se sobrecarregado no nível do prompt do REPL, use (\)=Base.(\)ou \ =Base. \para redefini-lo. NOTA: algumas funções têm operadores UTF-8 existentes predefinidos, como ÷para div, conforme observado por Alex A.
  2. Use ^ com strings para saída condicional . Ou seja, a>0?"Hi":""use "Hi"^(a>0)para salvar um byte ou, para booleano a, use "Hi"^apara salvar três bytes.
  3. (às vezes) Armazene pequenos vetores de tamanho fixo como variáveis ​​separadas . Por exemplo, em vez de a=split("Hi there"," "), você pode evitar a[1]e a[2]usar a,b=split("Hi there"," "), que pode ser referenciado como ae b, economizando três bytes para cada uso, ao custo de apenas dois caracteres extras na atribuição. Obviamente, não faça isso se puder trabalhar com operações vetoriais.
  4. Acesse o primeiro elemento da matriz com[] - para matrizes, a expressão A[]é equivalente a A[1]. Observe que isso não funciona para Strings se você deseja obter o primeiro caractere ou para Tuplas.
  5. Não use isempty para matrizes, tuplas ou seqüências de caracteres ; em vez disso, use ==[]para matrizes e ==()para tuplas; Da mesma forma, para o negativo, use !=[]e !=(). Para cadeias, use ==""para vazio, mas use >""para não vazio, pois "" é lexicograficamente antes de qualquer outra cadeia.
  6. Use o operador booleano de curto-circuito certo no lugar de "se" . Pode ser um pouco menos específico para Julia, mas vale a pena ter em mente. x<=1&&"Hi"pode ser escrito como x>1||"Hi", salvando um caractere (desde que o retorno do booleano não seja importante).
  7. Não use contains para verificar a presença de caracteres na string - se você estiver restrito ao ASCII básico, use em in('^',s)vez de contains(s,"^"). Se você puder usar outros caracteres, poderá economizar um pouco mais com '^'∈s, mas observe que há 3 bytes em UTF-8.
  8. Procurando valores mínimos / máximos em uma matriz? Não use mínimo ou máximo - em vez de usar minimum(x)ou maximum(x), use min(x...)ou max(x...), para raspar um caractere do seu código, se você souber xque terá pelo menos dois elementos. Como alternativa, se você souber que todos os elementos xserão não negativos, use minabs(x)oumaxabs(x)
  9. Sempre que possível e permitido pelo desafio, use uma nova linha real em vez de \ n - observe que isso tornará o seu código mais difícil de ler e pode significar que você precisa fornecer uma versão "não destruída" para possibilitar que as pessoas realmente entendam isto.
  10. Coloque opções após a sequência de caracteres regex - se você quiser ter uma sequência de caracteres regex no modo de várias linhas, por exemplo, não digite r"(?m)match^ this", digite r"match^ this"m, salvando três caracteres.
  11. Matrizes 1-D reversas usando flipud - reverse(x)é um byte maior que flipud(x)e executará a mesma operação; portanto, o último é melhor.
  12. Sempre que possível, use concatenação de matriz em vez de push !, unshift !, append! Ou prepend! - para matrizes normais, isso pode ser feito facilmente. Para matrizes do tipo Qualquer com elementos de matriz, você precisará de colchetes em volta das matrizes adicionadas (ou seja {[1,2]}, não {1,2}) - para Julia 0.4, você precisaria Any[[1,2]].
  13. Use a indexação de matriz para obter o tamanho de uma matriz ou sequência - quando você usa enda indexação de matriz, ela é automaticamente convertida para o comprimento da matriz / sequência. Então, em vez de k=length(A), use A[k=end]para salvar 3 caracteres. Observe que isso pode não ser benéfico se você quiser usar k imediatamente. Isso também funciona em um caso multidimensional - A[k=end,l=end]obterá o tamanho de cada dimensão de A- no entanto, (k,l)=size(A)neste caso, é menor em um byte; portanto, use-o apenas se desejar acessar imediatamente o último elemento ao mesmo tempo.
  14. Obter um iterador de índice usando a indexação de matriz - Semelhante a 13, você também pode obter um iterador que corresponda ao comprimento de uma matriz usando A[k=1:end]; nesse caso, kele manterá uma correspondência de iterador 1:length(A). Isso pode ser útil quando você também deseja usar a matriz Aao mesmo tempo.
  15. Não use collect para converter uma string em uma matriz de caracteres - em vez de collect(A)use use [A...], que fará a mesma coisa e economizará 4 bytes.
  16. Precisa de um número convertido em uma string? Use "$(s[i])"ou dec(s[i])para expressões ou variáveis ​​com vários caracteres e "$i"para variáveis com um único caractere.
  17. Use em ?:vez de &&ou ||para atribuição condicional - ou seja, se você quiser executar uma atribuição apenas em alguma condição, poderá salvar um byte escrevendo em cond?A=B:1vez de cond&&(A=B)ou em cond?1:A=Bvez de cond||(A=B). Observe que 1aqui é um valor fictício.
  18. Use unionou em vez deunique - union(s)fará o mesmo que unique(s)e salve um byte no processo. Se você pode usar caracteres não ASCII, ∪(s)fará a mesma coisa e custará apenas 3 bytes em vez dos 5 bytes de polegada union.

2
Ah, como eu adoraria esse primeiro truque em Python.
seequ

Você pode dividir em espaços usando simplesmente split("Hi there")porque o argumento padrão é um espaço.
Alex A.

@AlexA. - Eu sei, mas não é o objetivo da dica, e a dica se aplica igualmente bem de qualquer maneira.
Glen O

O ponto 12 mudou em 0,5.
Lyndon Branco

@ Oxinabox - Não estou surpreso, tenho certeza de que alguns deles estão desatualizados agora. Originalmente, escrevi a maioria das dicas para o 0.3, acho.
Glen O

15

Redefinir operadores para definir funções

A redefinição de operadores pode salvar muitos bytes entre parênteses e vírgulas.

Operadores unários recursivos

Para um exemplo unário, compare as seguintes implementações recursivas da sequência de Fibonacci:

F(n)=n>1?F(n-1)+F(n-2):n # 24 bytes
!n=n>1?!~-n+!(n-2):n     # 20 bytes
!n=n>1?!~-n+!~-~-n:n     # 20 bytes

Experimente online!

O operador redefinido mantém sua precedência inicial.

Note que não poderíamos simplesmente trocar a !favor de ~, uma vez que ~já está definido para números inteiros, enquanto !está definido apenas para booleanos.

Operadores binários

Mesmo sem recursão, redefinir um operador é mais curto do que definir uma função binária. Compare as seguintes definições de um teste de divisibilidade simples.

f(x,y)=x==0?y==0:y%x==0 # 23 bytes
(x,y)->x==0?y==0:y%x==0 # 23 bytes
x->y->x==0?y==0:y%x==0  # 22 bytes
x\y=x==0?y==0:y%x==0    # 20 bytes

Experimente online!

Operadores binários recursivos

A seguir, ilustra como redefinir um operador binário para calcular a função Ackermann:

A(m,n)=m>0?A(m-1,n<1||A(m,n-1)):n+1    # 35 bytes
^ =(m,n)->m>0?(m-1)^(n<1||m^~-n):n+1   # 36 bytes
| =(m,n)->m>0?m-1|(n<1||m|~-n):n+1     # 34 bytes
m\n=m>0?~-m\(n<1||m\~-n):n+1           # 28 bytes

Experimente online!

Observe que ^é ainda mais longo do que usar um identificador comum, pois sua precedência é muito alta.

Como mencionado anteriormente

m|n=m>0?m-1|(n<1||m|~-n):n+1           # 28 bytes

não funcionaria para argumentos inteiros, pois |já está definido neste caso. A definição para números inteiros pode ser alterada com

m::Int|n::Int=m>0?m-1|(n<1||m|~-n):n+1 # 38 bytes

mas isso é proibitivamente longo. No entanto, ele faz o trabalho se passar por um flutuador como argumento esquerda e um número inteiro como argumento direita.


11
  1. Não seja seduzido com muita facilidade pelo fator (n) A tentadora função da biblioteca de base factor(n)tem uma falha fatal: ela retorna a fatoração do seu número inteiro em um Dicttipo não ordenado . Portanto, exige um custo alto collect(keys())e, collect(values())potencialmente, também um cate um sortpara obter os dados desejados. Em muitos casos, pode ser mais barato fatorar apenas por divisão de teste. Triste mas verdadeiro.

  2. Usar o mapa map é uma ótima alternativa ao loop. Esteja ciente da diferença entre mape map!explore a funcionalidade do local quando puder.

  3. O uso de mapreduce mapreduce amplia ainda mais a funcionalidade do mapa e pode ser uma economia significativa de bytes.

  4. Funções anônimas são ótimas! ..especialmente quando passado para as mapfunções mencionadas acima .

  5. Funções de matriz cumulativos cumprod , cumsum, o saboroso cummine as outras funções de nome semelhante permitir operações cumulativos ao longo de uma dimensão especificada de uma matriz n-dimensional. (Ou * un * especificado se a matriz for 1-d)

  6. Notação curta para Qualquer um Quando você deseja selecionar toda uma dimensão específica de uma matriz multidimensional (ou Dict), por exemplo A[Any,2], você pode salvar bytes usandoA[:,2]

  7. Use a notação de linha única para funções. Em vez de function f(x) begin ... endsimplificar, muitas vezesf(x)=(...)

  8. Use o operador ternário Pode ser uma economia de espaço para construções de expressão única If-Then-Else. Advertências: Embora seja possível em alguns outros idiomas, você não pode omitir a parte após os dois pontos em Julia. Além disso, o operador possui nível de expressão em Julia, portanto, você não pode usá-lo para execução condicional de blocos inteiros de código.
    if x<10 then true else false endvs
    x<10?true:false


3
Como na Terra é "use o operador ternário" um pouco específico para Julia? É relevante para todos os idiomas que o possuem.
Peter Taylor

5
É relevante que ele tenha. Muitos idiomas também possuem funções de mapa, anônimas ou puras, algum tipo de abreviação de qualquer / todos, funções cumulativas, etc. .
Jonathan Van Matre

3
Puxa, apenas todos os funcionais para iniciantes, onde tudo retorna um valor para que uma operação ternária seja redundante. Se você deve ter exemplos específicos: Go, Haskell, Scala, Lua, VB, Prolog, PL / SQL ... até o Python não possui muitas versões. Das quase uma dúzia de idiomas cujas linhas de dicas mencionam seu operador ternário, há algum motivo para você escolher apenas ser provincial na minha?
Jonathan Van Matre

3
Bem, ei, pelo menos você é um traficante de oportunidades iguais. (ヘ ( ̄ ー  ̄ ヘ))
Jonathan Van Matre

1
Posso sugerir o ajuste da dica 3? mapreduce é maior que mapfoldl ou mapfoldr e pode ter um comportamento variável, dependendo da implementação. mapfoldl e mapfoldr são associativos à esquerda e à direita (respectivamente) de forma consistente e, portanto, são uma escolha melhor. Isso também se aplica de maneira mais geral à redução (use foldl ou foldr).
Glen O

9

Iterar sobre funções

Isso também é possível em outros idiomas, mas geralmente é mais longo que o método direto. No entanto, a capacidade de Julia de redefinir seus operadores unários e binários o torna bastante desafiador.

Por exemplo, para gerar a tabela de adição, subtração, multiplicação e divisão para os números naturais de 1 a 10, pode-se usar

[x|y for x=1:10,y=1:10,| =(+,-,*,÷)]

que redefine o binário operador |como +, -, *e ÷, em seguida, calcula x|ypara cada operação e xe ynos intervalos desejados.

Isso funciona para operadores unários também. Por exemplo, para calcular números complexos 1 + 2i , 3-4i , -5 + 6i e -7-8i , seus negativos, seus conjugados complexos e seus inversos multiplicativos, pode-se usar

[~x for~=(+,-,conj,inv),x=(1+2im,3-4im,-5+6im,-7-8im)]

que redefine o unária operador ~como +, -, conje inv, em seguida, calcula ~xpara todos os números complexos desejados.

Exemplos em concursos reais


6
  1. Às vezes, as palavras-chave podem seguir imediatamente as constantes sem a necessidade de um espaço ou ponto e vírgula. Por exemplo:

    n->(for i=1:n n-=1end;n)

    Observe a falta de um espaço entre 1e end. Isto também é verdade para endocorrer após um parêntese próximo, ie )end.

  2. Execute inteiro divisão usando ÷, em vez de div()ou sobrecarregar um operador. Observe que ÷vale 2 bytes em UTF-8.

  3. Use vec()ou A[:](para alguma matriz A) em vez de reshape()sempre que possível.

  4. Crie funções em vez de programas completos quando permitido nas regras de desafio. É mais curto definir uma função que aceita entrada, em vez de definir variáveis, lendo a partir de stdin. Por exemplo:

    n->(n^2-1)
    n=read(STDIN,Int);n^2-1
  5. As variáveis ​​podem ser incrementadas dentro do argumento para uma função. Por exemplo, a seguir, é minha resposta ao desafio Localizar o próximo número binário 1 esparso :

    n->(while contains(bin(n+=1),"11")end;n)

    Isso é mais curto do que incrementar ndentro do loop.


6
  1. Não digitereturn f(x)=x+4 é idêntico, mas menor que f(x)=return x+4. Julia sempre retorna o resultado da última declaração.
  2. Use = em vez de em . [x for x in 1:4]tem 3 caracteres a mais, mas é equivalente a[x for x=1:4]

5

Use chamadas de função Broadcasting.

Introduzido em Julia 0.5. É como um mapa, mas usa menos caracteres e a transmissão se comporta de acordo com os argumentos - o que significa que você pode escrever menos lambda para lidar com as coisas.

Ao invés de:

  • map(f,x) - 8 caracteres.
  • f.(x) - 5 caracteres

Melhor ainda:

  • map(a->g(a,y),x) - 16 caracteres
  • g.(x,[y]) - 9 caracteres
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.