Dicas para jogar golfe no Mathematica

41

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

alefalpha
fonte

Respostas:

30

As dicas abaixo variam do mais econômico ao mais usado:

  1. Use os comandos de alto nível do Mathematica sempre que possível, mesmo os volumosos:

  2. Use Graphics and Textpara arte Ascii: por exemplo, programação em estrela! e construa um relógio analógico

  3. Símbolos dedicados:

    • símbolos de operações lógicas e de conjunto em vez de seus nomes longos: ⋂, ⋃, ∧, ∨

    • Mape Apply: /@, //@. @@,@@@

  4. Notação de prefixo e infixo:

    • Print@"hello" no lugar de Print["hello"]

    • a~f~b no lugar de f[a,b]

  5. Quando uma função é usada apenas uma vez, uma função pura pode economizar um ou dois caracteres.

  6. Juntando seqüências de caracteres em uma lista. ""<>{"a","b","c"}ao invés deStringJoin@{"a","b","c"}

  7. Explorar funções listáveis. Quanto mais longas as listas, melhor.

    {a, b, c} + {x, y, z}= {a+x, b+y, c+z}

    {2, 3, 4} {5, 6, 7}= {10, 18, 28}

    {{a, b}, {c, d}}^{2, 3} = {{a^2, b^2}, {c^3, d^3}}

DavidC
fonte
2
É sempre mais curto para escrever (Norm[#-#2]&)do que para EuclideanDistance.
User202729 18/0118
32

Algumas funções internas com nomes longos podem ser substituídas por expressões mais curtas.

Por exemplo:

  • Total => Tr
  • Transpose=> Threadou\[Transpose]
  • True => 1<2
  • False => 1>2
  • Times => 1##&
  • Alternatives => $|##&
  • IntegerQ => ⌊#⌋==#&
  • a[[1]] => #&@@a
  • a[[All,1]] => #&@@@a
  • ConstantArray[a,n]=> Array[a&,n]ouTable[a,{n}]
  • Union@a=> {}⋃aoua⋃a
  • ToExpression@n=> FromDigits@nse né um número
  • Divisible[n,m] => m∣n
  • FromDigits[n,2]=> Fold[#+##&,n]Se né uma lista de 0s e 1s
  • Complex@z=> {1,I}.zonde zestá uma lista do formulário{x,y}
alefalpha
fonte
5
@belisarius Thread[{{a,b},{c,d}}]== Thread[List[{a,b},{c,d}]]== {List[a,c],List[b,d]}== {{a,c},{b,d}}==Transpose[{{a,b},{c,d}}]
alephalpha
2
Acho que seu Foldtruque FromDigitstambém funciona para qualquer outra base, exceto 10. Por exemplo FromDigits[n,5]-> Fold[4#+##&,n](com o bônus de salvar um byte extra para bases 100e 1000).
22826 Martin Ender
11
@ mbomb007 3 bytes em UTF-8. De fato, esse personagem é U+F3C7.
alephalpha
11
Finalmente instalei o 10.3. Se estamos considerando programas completos, não acho que Echoseja uma opção, porque ela imprime >>(e um espaço) em STDOUT antes de imprimir a string real.
Martin Ender
2
Pois Complex[x,y] => {1,I}.{x,y}, eu acho que x+y*Ié muito mais curto com o mesmo efeito?
Shieru Asakoto 27/02
22

Listas com valores repetidos

Este é um vetor bastante comum para se trabalhar:

{0,0}

Acontece que isso pode ser reduzido por um byte:

0{,}

Ainda mais bytes são salvos se o vetor for maior que dois zeros. Isso também pode ser usado para inicializar matrizes zero, por exemplo, o seguinte fornece uma matriz 2x2 de zeros:

0{{,},{,}}

Isso também pode ser usado para valores diferentes de zero se forem suficientemente grandes ou muitos ou negativos. Compare os seguintes pares:

{100,100}
0{,}+100
{-1,-1}
0{,}-1
{3,3,3,3}
0{,,,}+3

Mas lembre-se de que, a partir de 6 valores, você estará melhor 1~Table~6nesse caso (potencialmente mais cedo, dependendo dos requisitos de precedência).

A razão pela qual isso funciona é que ,introduz dois argumentos na lista, mas argumentos omitidos (em qualquer lugar do Mathematica) são implícitos Null. Além disso, a multiplicação é Listablee 0*xé 0para quase todos x(exceto para coisas como Infinitye Indeterminate), então aqui está o que está acontecendo:

  0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}

Para listas de 1s, você pode usar um truque semelhante usando regras de exponenciação. Existem duas maneiras diferentes de salvar bytes, se você tiver pelo menos três 1s na lista:

{1,1,1}
1^{,,}
{,,}^0
Martin Ender
fonte
7
+1; isso apenas mostra que, embora o Mathematica possa ter um recurso interno para tudo, jogar golfe pode ser um desafio real.
precisa saber é o seguinte
Se você deseja uma matriz que finalmente seja preenchida com 1s, 1^{,,,}é um byte menor que 0{,,,}+1.
Misha Lavrov #
@MishaLavrov Oh, boa captura. Isso diminui em três valores e você também pode usar {,,}^0. Eu vou editar a postagem.
Martin Ender
19

Conheça seus argumentos de função pura

Ao jogar código no golfe, você costuma empregar uma abordagem funcional, na qual usa funções anônimas (puras) com a &sintaxe abreviada. Existem várias maneiras diferentes de acessar os argumentos de uma função e, geralmente, você pode economizar alguns bytes tendo uma boa noção das possibilidades.

Acessando argumentos únicos

Você provavelmente sabe disso se já usou funções puras antes. O n º argumento é referido como #n, e #atua como um alias para #1. Portanto, se, digamos, você quiser escrever uma função que tome como parâmetros outra função e seu argumento (para passar o argumento para essa função), use

#@#2&

Isso não funciona com números negativos (como você pode usar ao acessar listas).

Acessando argumentos nomeados (novo na V10)

Um dos principais recursos novos de linguagem do Mathematica 10 é o Associations, que são basicamente mapas de valores-chave com tipos de chave arbitrários, escritos como

<| x -> 1, "abc" -> 2, 5 -> 3 |>

Se essa associação for passada como o primeiro argumento para uma função pura, você poderá acessar alguns se os argumentos forem parâmetros nomeados:

{#, #2, #3, #abc, #xyz} & [<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th"]
(* {<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th", "1st", "2nd"} *)

Observe que #ainda se refere a toda a associação conforme o esperado. Para que os parâmetros nomeados funcionem, as chaves devem ser cadeias de caracteres (não funcionará se você usar variáveis ​​indefinidas, por exemplo), e essas cadeias devem começar com uma letra e conter apenas letras e dígitos.

O argumento "auto" #0

Um recurso menos conhecido é que #0também existe e fornece o próprio objeto de função. Isso pode ser realmente útil em quines e em generalizados. De fato, o menor quine Mathematica (eu sei) é

ToString[#0][] & []

O que é um pouco chato é que ele não fornece os caracteres exatos digitados. Por exemplo, se usado @para aplicação de função, ele ainda será renderizado [...]e espaços serão inseridos em alguns lugares. Isso normalmente tornará o quine um pouco mais longo do que você gostaria, mas sempre funcionará, jogando o quine primeiro e depois apenas copiando sua saída - que agora deve ser um quine real.

Além de quines, isso também significa que você pode escrever código recursivo sem precisar nomear sua função. Compare estas três implementações de Fibonacci (ingênuas, mas eficientes):

f@0=0;f@1=1;f@n_:=f[n-1]+f[n-2]
f@n_:=If[n<2,n,f[n-1]+f[n-2]]
If[#<2,#,#0[#-1]+#0[#-2]]&

Sequências de argumentos

Agora é aqui que a mágica real começa. As seqüências não são usadas frequentemente no golfe, porque Sequenceé um nome muito longo para valer a pena na maioria das vezes. Mas em funções puras é onde elas brilham. Se você não estiver familiarizado com sequências, elas são basicamente como splats em alguns outros idiomas; se você usar uma sequência em uma Listou na lista de argumentos de uma função, seus elementos serão automaticamente expandidos em slots separados. tão

{1, Sequence[2, 3, 4], 5} == {1, 2, 3, 4, 5}
f["a", Sequence[0, {}], "b"] == f["a", 0, {}, "b"]

Agora, em funções puras ##ou ##1é uma sequência de todos os argumentos. Da mesma forma, ##2é uma sequência de todos os argumentos a partir do segundo, ##3todos os argumentos a partir do terceiro etc. Portanto, para começar, podemos apenas reimplementar Sequencecomo ##&, economizando 5 bytes. Como exemplo de uso, isso nos fornece uma alternativa para Join@@list(consulte esta dica ), que não salva nenhum bytes, mas é bom saber de qualquer maneira:

 ##&@@@list

Isso aplaina efetivamente o primeiro nível de uma lista aninhada. O que mais podemos fazer com isso? Aqui está uma alternativa 2 bytes mais curta para RotateLeft:

 RotateLeft@list
 {##2,#}&@list

Só para essas coisas, vale a pena manter esse recurso em mente. No entanto, podemos fazer melhor! As sequências ficam realmente interessantes quando consideramos que os operadores são realmente implementados como funções ocultas. Por exemplo, a+bna verdade avalia como Plus[a,b]. Então, se dermos uma sequência ...

1+##&[1,2,3]
=> Plus[1,##] 
=> Plus[1,1,2,3]
=> 7

Este truque foi usado nesta dica para economizar um byte Times, porque a justaposição tecnicamente também é apenas um operador:

1##&[1,2,3]
=> Times[1,##]
=> Times[1,1,2,3]
=> 6

Você também pode usá-lo para salvar um byte Unequalse você tiver um valor de caractere único ou variável que você sabe que não esteja em seus argumentos ( Nprovavelmente funcionará em 99% dos casos):

Unequal[a,b,c]
N!=##&[a,b,c]

Isso fica ainda mais interessante com operadores unários -e /- os dois últimos são realmente implementados em termos de multiplicação e exponenciação. Aqui está uma lista de coisas que você pode fazer, onde a última coluna assume que a função recebeu os argumentos a, b, c:

Operator    Function                Expanded                    Equivalent to

+##         Plus[##]                Plus[a,b,c]                 a+b+c
1##         Times[1,##]             Times[1,a,b,c]              a*b*c
-##         Times[-1,##]            Times[-1,a,b,c]             -a*b*c
x+##        Plus[x,##]              Plus[x,a,b,c]               x+a+b+c
x-##        Plus[x,Times[-1,##]]    Plus[x,Times[-1,a,b,c]]     x-a*b*c
x##         Times[x,##]             Times[x,a,b,c]              x*a*b*c
x/##        Times[x,Power[##,-1]]   Times[x,Power[a,b,c,-1]]    x*a^b^c^-1
##/x        Times[##,Power[x,-1]]   Times[a,b,c,Power[x,-1]]    a*b*c/x
x^##        Power[x,##]             Power[x,a,b,c]              x^a^b^c
##^x        Power[##,x]             Power[a,b,c,#]              a^b^c^x
x.##        Dot[x,##]               Dot[x,a,b,c]                x.a.b.c

Outros operadores comuns são !=, ==, &&, ||. Os menos comuns para se manter em mente são |, @*, /*. Para concluir, aqui está um pequeno truque de bônus:

####        Times[##,##]            Times[a,b,c,a,b,c]          (a*b*c)^2

Continue experimentando isso e deixe-me saber se você encontrar outras aplicações úteis ou particularmente interessantes!

Martin Ender
fonte
15

Sqrt@2ou 2^.5=>√2

a[[1]]=>a〚1〛

#+#2&=>+##&

Flatten@a=> Join@@a(às vezes)

Function[x,x^2]=> xx^2ou#^2&

a〚1;;-1;;2〛=>a〚;;;;2〛

a〚2;;-1 ;;2〛=>a〚2;;;;2〛

a〚All,1〛=>a〚;;,1〛

{{1}}〚1,1〛=>Tr@{{1}}

0&~Array~10=>0Range@10

Range[10^3]=>Range@1*^3

chyanog
fonte
11
Observe que ao medir por bytes, use e use 3 bytes cada (assuma UTF8) #
user202729 18/18/18
12

Operadores como funções

Inspirado pela recente descoberta de Dennis para Julia , pensei em investigar isso no Mathematica. Eu sabia que o Mathematica define um grande número de operadores não utilizados, mas nunca prestou muita atenção a ele.

Para referência, a lista de todos os operadores pode ser encontrada aqui na forma de uma tabela de precedência. O triângulo na última coluna indica se esse operador tem um significado interno ou não. Embora nem todos os que não podem ser definidos facilmente, a maioria deles pode.

Convenientemente, existem dois operadores não utilizados com um ponto de código menor que 256, para que possam ser usados ​​como bytes únicos em um arquivo de origem codificado ISO 8859-1:

  • ± (0xB1) pode ser usado como um operador de prefixo unário ou como um operador de infixo binário.
  • · (0xB7) pode ser usado como um operador de infix variável ou n-ária, para n> 2.

Porém, há mais um problema: por alguma razão estranha ao definir esses operadores, você precisa de um espaço na frente deles, ou o Mathematica tenta analisar uma multiplicação. Ao usá-los, você não precisa de espaços:

±x_:=2x
x_ ±y_:=x+y
x_ ·y_ ·z_:=x*y+z
Print[±5]  (* 10 *)
Print[3±4] (*  7 *)
Print[3·4·5] (* 17 *)

Compare isso com:

f@x_:=2x
x_~g~y_:=x+y
h[x_,y_,z_]:=x*y+z
Print[f@5]   (* 10 *)
Print[3~g~4] (*  7 *)
Print[h[x,y,z]] (* 17 *)

Portanto, isso economiza um byte ao definir a função e dois bytes ao usá-la. Observe que a definição de ·não salvará bytes para quatro operandos e começará a custar bytes para mais operandos, mas o uso ainda poderá salvar bytes, dependendo da precedência dos operadores usados ​​nos argumentos. Também é bom observar que você pode definir de forma barata uma função variadica que pode ser chamada com muito mais eficiência:

x_ ·y__:={y}
Print[1·2·3·4·5] (* {2, 3, 4, 5} *)

Mas observe que não é possível chamar facilmente essas funções variadas com um único argumento. (Você pode fazer isso CenterDot[x]ou, ##&[]·xse realmente precisar, há uma boa chance de você estar melhor com uma solução diferente.)

Obviamente, isso não está salvando nada para soluções em que uma função sem nome é suficiente, mas às vezes você precisa definir funções auxiliares para uso posterior, e às vezes é mais curto definir funções nomeadas, por exemplo, para definir definições diferentes para parâmetros diferentes. Nesses casos, o uso de um operador pode salvar uma quantidade decente de bytes.

Observe que o uso desses arquivos codificados ISO 8859-1 precisa $CharacterEncodingser definido como um valor compatível, como o padrão do Windows WindowsANSI. Em alguns sistemas, esses padrões UTF-8não podem ler esses pontos de código a partir de bytes únicos.

Martin Ender
fonte
Isso é realmente ótimo, eu não sabia que o Mathematica tinha uma lista de operadores e até incluí sua precedência. Esses dois operadores que você encontrou, com certeza, serão úteis.
milhas
8

Escolhendo valores com base no número inteiro

A abordagem ingênua para escolher entre ye z, dependendo de xser 0ou 1não

If[x<1,y,z]

No entanto, há uma maneira mais curta:

y[z][[x]]

Isso funciona porque [[0]]dá a Headexpressão de, neste caso y, enquanto [[1]]apenas fornece o primeiro elemento - nesse caso, o primeiro argumento z,.

Você pode usar isso para escolher entre mais de dois valores:

u[v,w][[x]]

Observe que isso não funcionará se ufor uma função que realmente avalia algo. É importante que o Mathematica continue u[v,w]como está. No entanto, isso funciona na maioria dos casos, incluindo se ué um número, uma sequência ou uma lista.

Os créditos para esse truque vão para alefalpha - eu descobri isso em uma de suas respostas.

Se xfor baseado em 1 e não em zero, use apenas

{y,z}[[x]]

ou

{u,v,w}[[x]]

Em alguns casos raros, você pode até usar o fato de que a multiplicação não é avaliada para alguns valores:

{"abc","def"}[[x]]
("abc""def")[[x]]

Observe, porém, que o Mathematica realmente reordenará os argumentos, de uma multiplicação se permanecer sem avaliação, portanto, o acima é idêntico a

("def""abc")[[x]]
Martin Ender
fonte
8

Alternativas para Length

Isso foi totalmente reescrito com algumas sugestões de LegionMammal978 e Misha Lavrov. Muito obrigado a ambos.

Em muitos casos, Lengthpode ser reduzido um pouco usando Tr. A idéia básica é transformar a entrada em uma lista de 1s, para que a Trsoma seja igual à duração da lista.

A maneira mais comum de fazer isso é usar 1^x(para uma lista x). Isso funciona porque Poweré Listablee 1^npara a maioria dos valores atômicos né justo 1(incluindo todos os números, cadeias e símbolos). Portanto, já podemos salvar um byte com isso:

Length@x
Tr[1^x]

Obviamente, isso pressupõe que xé uma expressão com maior precedência que ^.

Se xcontiver apenas 0s e 1s, podemos salvar outro byte usando Factorial(supondo xque a precedência seja maior que !):

Length@x
Tr[x!]

Em alguns casos raros, xpode ter precedência menor do ^que a multiplicação, mas ainda maior. Nesse caso, ele também terá menor precedência do que @, portanto, realmente precisamos comparar com Length[x]. Um exemplo desse operador é .. Nesses casos, você ainda pode salvar um byte com este formulário:

Length[x.y]
Tr[0x.y+1]

Finalmente, algumas observações sobre que tipo de lista isso funciona:

Como mencionado na parte superior, isso funciona em listas simples contendo apenas números, seqüências de caracteres e símbolos. No entanto, ele também funcionará em algumas listas mais profundas, embora na verdade calcule algo ligeiramente diferente. Para uma matriz retangular n- D, use Trfornece a menor dimensão (em oposição à primeira). Se você sabe que a dimensão mais externa é a mais curta, ou sabe que são todas iguais, as Trexpressões -existem ainda Length.

Martin Ender
fonte
3
Só encontrei uma solução ainda mais curto: Length@x == Tr[1^x]. Deve funcionar com a maioria das listas.
usar o seguinte
@ LegionMammal978 isso é incrível, obrigado :). Vou editá-lo em breve.
Martin Ender
11
Duas vezes agora, encontrei-me usando em Tr[x!]vez de Tr[1^x]salvar um byte no caso especial em que xapenas contém zeros e uns.
Misha Lavrov #
@MishaLavrov Isso é realmente legal! :)
Martin Ender
7
  1. Explore soluções recursivas - o Mathematica é multiparadigma, mas a abordagem funcional é geralmente a mais econômica. NestWhilepode ser uma solução muito compacta para pesquisar problemas NestWhileListe FoldListé poderosa quando você precisa retornar ou processar os resultados de iterações intermediárias. Map (/@), Apply (@@, @@@), MapThread, E realmente tudo sobre o Wolfram Programação Funcional página de documentação é coisa potente.

  2. Formulário reduzido para incremento / decremento - por exemplo, em vez de While[i<1,*code*;i++]você pode fazer
    While[i++<1,*code*]

  3. Não esqueça que você pode pré-incrementar / diminuir - por exemplo, em --ivez de i--. Às vezes, isso pode economizar alguns bytes no código circundante, eliminando uma operação preparatória.

  4. Corolário de # 5 de David Carraher: Quando a mesma função é usada várias vezes, atribuir um símbolo a ela pode salvar bytes. Por exemplo, se você estiver usando ToExpressionquatro vezes em uma solução, t=ToExpressionpoderá usá-lo t@*expression*posteriormente. No entanto, antes de fazer isso, considere se a aplicação repetida da mesma função indica uma oportunidade para uma abordagem recursiva mais econômica.

Jonathan Van Matre
fonte
MapThreadmuitas vezes pode ser substituído por \[Transpose]. TIO .
user202729
7

Não use {}se você estiver usando @@@.

Em alguns casos, você pode encontrar uma expressão como:

f@@@{{a,b},{c,d}}

É possível reduzir bytes escrevendo:

f@@@{a|b,c|d}

Alternativestem uma precedência muito baixa, por isso geralmente é bom escrever expressões (uma exceção notável são funções puras; você pode usá-lo apenas no elemento mais à esquerda de Alternatives).

f@@@{f@a|b~g~1,#^2&@c|d@2}

Observe que f@@a|b|c(em vez de f@@{a,b,c}) não funciona porque Applytem uma precedência maior que Alternative.

Nesse caso, você deve simplesmente usar f@@{a,b,c}.

JungHwan Min
fonte
6

Apenas Mathematica 10

Formulários do operador

O Mathematica 10 suporta as chamadas "formas de operador", o que basicamente significa que algumas funções podem ser curry. Currying uma função é criar uma nova função, corrigindo um de seus operadores. Digamos, você está usando SortBy[list, somereallylongfunction&]muitos lists diferentes . Antes, você provavelmente teria atribuído SortBya sea função pura para fassim

s=SortBy;
f=somereallylongfunction&;
list1~s~f;
list2~s~f;
list3~s~f;

Agora você pode curry SortBy, o que significa que agora você pode fazer

s=SortBy[somereallylongfunction&];
s@list1;
s@list2;
s@list3;

O mesmo funciona para um monte de outras funções, que levam um argumento lista ou função, incluindo (mas não limitado a) Select, Map, Nearest, etc.

ybeltukov no Mathematica.SE conseguiu produzir uma lista completa dos seguintes :

{"AllTrue", "AnyTrue", "Append", "Apply", "AssociationMap", "Cases", 
 "Count", "CountDistinctBy", "CountsBy", "Delete", "DeleteCases", 
 "DeleteDuplicatesBy", "Extract", "FirstCase", "FirstPosition", 
 "FreeQ", "GroupBy", "Insert", "KeyDrop", "KeyExistsQ", "KeyMap", 
 "KeySelect", "KeySortBy", "KeyTake", "Map", "MapAt", "MapIndexed", 
 "MatchQ", "MaximalBy", "MemberQ", "Merge", "MinimalBy", "NoneTrue", 
 "Position", "Prepend", "Replace", "ReplacePart", "Scan", "Select", 
 "SelectFirst", "SortBy", "StringCases"}

Composição e composição correta

Existem novas abreviações para Composition( @*) e RightComposition( /*). Um exemplo obviamente artificial, em que estes podem salvar caracteres, é visto nas três linhas equivalentes a seguir

Last@Range@# & /@ Range[5]
Last@*Range /@ Range[5]
Range /* Last /@ Range[5]
Martin Ender
fonte
5

Não escreva funções com 0 argumentos

Não há necessidade de código como este:

f[]:=DoSomething[1,2]
(*...*)
f[]
(*...*)
f[]

Você pode simplesmente usar uma variável com :=para forçar a reavaliação do lado direito:

f:=DoSomething[1,2]
(*...*)
f
(*...*)
f

Isso também significa que você pode alternar qualquer ação que você executa com freqüência (mesmo que seja apenas algo parecido n++) para um único caractere ao custo de 5 bytes. Então, no caso de n++ele pagar depois do quarto uso:

n++;n++;n++;n++
f:=n++;f;f;f;f
Martin Ender
fonte
5

Use %para obter uma variável livre

Essa dica é aplicável apenas se o ambiente REPL do Mathematica puder ser assumido. %não está definido quando o código é executado como um script.

Quando você puder usar os recursos do REPL, não faça isso:

a=someLongExpression;some[other*a,expression@a,using^a]

Em vez disso, lembre-se de que o Mathematica armazena a última expressão avaliada (terminada por nova linha) em %:

someLongExpression;
some[other*%,expression@%,using^%]

A nova linha adicionada custa um byte, mas você está economizando dois com a remoção a=. Portanto, no geral, isso economiza um byte.

Em alguns casos (por exemplo, quando você deseja imprimir o valor de aqualquer maneira), pode até deixar de fora ;, salvando dois bytes:

someLongExpression
some[other*%,expression@%,using^%]

Um ou dois bytes podem parecer razoavelmente menores, mas esse é um caso importante, pois torna a extração de expressões repetidas (que é uma técnica muito comum) muito mais útil ao jogar golfe:

A técnica normal de extrair expressões repetidas custa quatro bytes de sobrecarga, que precisam ser salvos por outros usos da expressão. Aqui está uma tabela curta do número mínimo de usos de uma expressão (pelo comprimento da expressão) para extração em uma variável nomeada para salvar qualquer coisa:

Length   Min. Uses
2        6
3        4
4        3
5        3
6        2
...      2

Usando a variável sem nome, será possível salvar alguns bytes com muito mais frequência:

When ; is required                        When ; can be omitted

Length   Min. Uses                        Length   Min. Uses
2        5                                2        4
3        3                                3        3
4        3                                4        2
5        2                                ...      2
...      2

Eu não penso %%ou %nposso ser usado para jogar golfe, porque se você não os usar pelo menos duas vezes, poderá colocar a expressão exatamente onde é necessário. E se você usá-lo duas vezes, o caractere adicional no nome da variável cancela a economia por omitir alguns x=.

Martin Ender
fonte
Observe que ele não funciona no modo de script.
Alephalpha #
@alephalpha O que é o modo de script?
Martin Ender
Um script mathematica .
alephalpha
@alephalpha Oh, certo, desliguei meu cérebro por um segundo ... então isso significa que ele não pode ser realmente usado, a menos que o ambiente REPL possa ser assumido.
Martin Ender
5

Verificando se uma lista está classificada

Este é essencialmente um corolário dessa dica, mas é uma tarefa suficientemente comum que acho que merece sua própria resposta.

A maneira ingênua de verificar se uma lista está em ordem é usar

OrderedQ@a

Podemos fazer um byte melhor com

Sort@a==a

No entanto, isso não funcionará se ainda não tivermos o que queremos verificar em uma variável. (Precisávamos de algo como o Sort[a=...]==aque é desnecessariamente longo.) No entanto, há outra opção:

#<=##&@@a

A melhor coisa é que isso pode ser usado para verificar se a entrada é classificada inversamente para a mesma contagem de bytes:

#>=##&@@a

Mais um byte pode ser salvo se: a) sabemos que os elementos da lista são distintos eb) sabemos um limite inferior entre 0 e 9 (inclusive; ou limite superior para ordem inversa):

0<##&@@a
5>##&@@a

Para ver por que isso funciona, consulte "Sequências de argumentos" na dica vinculada na parte superior.

Martin Ender
fonte
Alternativamente, (rigoroso) limite inferior para o reverso-classificados também trabalho: ##>0&@@a. Semelhante para o limite superior para classificado.
user202729
@ user202729 Bem, fique à vontade para editar (caso contrário, tentarei fazê-lo no fim de semana, se me lembro).
Martin Ender
5

Repetindo uma string

Em vez de StringRepeat[str,n]usar (0Range[n]+str)<>"". Ou se strnão depender de nenhum argumento de slot, o melhor é Array[str&,n]<>""usar esta dica.

feersum
fonte
11
Corolário: em vez de StringRepeat[s,n+1]usar Array[s&,n]<>s(mesmo quando você já possui n+1uma variável).
Martin Ender
Melhor,Table[str,n]<>""
attinat 18/07
5

Se você precisar de uma lista de números ordenados ao contrário, não use

Reverse@Sort@x

mas

-Sort@-x

para salvar seis bytes. A classificação por um valor negativo também é útil para SortBycenários:

Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]
Martin Ender
fonte
2
Que tal -Sort@-x?
JungHwan Min
11
@JungHwanMin Oh, uhhh, sim, isso é muito melhor. :)
Martin Ender
4

Você pode colar uma expressão na Breakqual pode salvar um ou dois caracteres. Exemplo ( outros detalhes não são utilizados para maior clareza ):

result = False;
Break[]

pode ser transformado em

Break[result = False]

para salvar um caractere. Se a expressão em questão não tiver precedência menor que o aplicativo de funções, você poderá salvar outro caractere:

Print@x;
Break[]

pode ser transformado em

Break@Print@x

Embora não documentado, o argumento Breakparece retornar ao loop circundante, o que pode potencialmente levar a ainda mais economia.

Martin Ender
fonte
4

Para remover todo o espaço em branco de uma string s, use

StringSplit@s<>""

Ou seja, use StringSplito padrão (dividido em componentes que não sejam espaços em branco) e simplesmente junte-os novamente. O mesmo provavelmente ainda é o mais curto se você quiser se livrar de qualquer outro caractere ou substring:

s~StringSplit~"x"<>""
Martin Ender
fonte
4

Alternativas para Range

Uma tarefa muito comum é aplicar algum tipo de função a todos os números de 1 a a n(geralmente dados como entrada). Existem essencialmente três maneiras de fazer isso (usando uma função de identidade sem nome como exemplo):

#&/@Range@n
Array[#&,n]
Table[i,{i,n}]

Costumo optar pelo primeiro (por qualquer motivo), mas essa raramente é a melhor escolha.

Usando em Arrayvez disso

O exemplo acima mostra que o uso Arraytem a mesma contagem de bytes. No entanto, tem a vantagem de ser uma expressão única. Em particular, se você quiser processar o resultado com uma função, fpoderá usar a notação de prefixo, que salva um byte Range:

f[#&/@Range@n]
f@Array[#&,n]

Além disso, você pode omitir parênteses em torno de sua função sem nome, com a qual você pode precisar Range, por exemplo

15/(#&)/@Range@n
15/Array[#&,n]

Se você não quiser usá-lo mais (ou com um operador que tenha menor precedência), poderá escrever- Arrayse em notação de infixo e também salvar um byte:

#&/@Range@n
#&~Array~n

Portanto, Arrayé quase certamente melhor que Range.

Usando em Tablevez disso

Agora, a tabela precisa compensar 3 bytes ou pelo menos 2 quando a notação infix é uma opção:

#&/@Range@n
i~Table~{i,n}

Quando nãoTable estiver usando notação infix, poderá omitir parênteses se sua função consistir em várias instruções:

(#;#)&/@Range@n
Table[i;i,{i,n}]

Isso ainda é mais longo, mas gera economia extra no caso mencionado abaixo.

A economia real deriva do fato de Tabledar um nome à variável em execução não deve ser descartada. Freqüentemente, você terá funções anônimas aninhadas nas quais deseja usar a variável externa dentro de uma das funções internas. Quando isso acontece, Tableé mais curto que Range:

(i=#;i&[])&/@Range@n
Table[i&[],{i,n}]
i&[]~Table~{i,n}

Você não apenas salva os caracteres para atribuir i, como também pode reduzir a função a uma única instrução no processo, o que permite o uso de notação infix sobre ela. Para referência, Arraytambém é mais longo neste caso, mas ainda menor que Range:

(i=#;i&[])&~Array~n

Quando você realmente usaria Range?

Sempre que você não precisar de uma chamada de função para processar os valores, por exemplo, quando o mapeamento puder ser realizado por meio de uma operação vetorizada. Por exemplo:

5#&~Array~n
5Range@n
#^2&~Array~n
Range@n^2

Obviamente, também é mais curto se você não deseja mapear nenhuma função, por exemplo,

Mean@Array[#&,n]
Mean@Range@n
Martin Ender
fonte
Por fim, alguém que usa f/@Range[x]regularmente ... #
24615
4

Encontrar o menor número que satisfaça uma condição

Algumas construções i=1;While[cond[i],i++]são boas como estão, mas há uma alternativa que é dois bytes mais curta:

1//.i_/;cond[i]:>i+1

O código acima substitui repetidamente um número ipor i+1enquanto ele atende à condição cond[i]. Nesse caso, icomeça em 1.

Observe que o número máximo padrão de iterações é 2 ^ 16 (= 65536). Se você precisar de mais iterações do que isso, Whileseria melhor. ( MaxIterations->∞é muito longo)

JungHwan Min
fonte
4

Avaliação de curto-circuito de abuso

Às vezes, você pode substituir Ifpor um operador lógico.

Por exemplo, digamos que você queira criar uma função que verifique se um número é primo e print 2*(number) - 1é se for verdadeiro:

If[PrimeQ@#,Print[2#-1]]&

É mais curto se você usar &&:

PrimeQ@#&&Print[2#-1]&

Mesmo quando você tem várias expressões, ainda salva byte (s):

If[PrimeQ@#,a++;Print[2#-1]]&

PrimeQ@#&&a++&&Print[2#-1]&
(* or *)
PrimeQ@#&&(a++;Print[2#-1])&

Você pode usar ||para casos em que deseja que a condição seja False:

If[!PrimeQ@#,Print[2#-1]]&
(* or *)
If[PrimeQ@#,,Print[2#-1]]&
(* can become *)
PrimeQ@#||Print[2#-1]&

Esses truques funcionam porque os operadores lógicos podem sofrer um curto-circuito ; o segundo argumento e, posteriormente, nem precisam ser expressões booleanas válidas.

Obviamente, isso não funcionará se você precisar do valor de retorno Ifou quando precisar de argumentos verdadeiros e falsos de If.

JungHwan Min
fonte
3

Aqui está uma lista com vários formulários de entrada do operador que podem reduzir muitas coisas. Algumas delas foram mencionadas em outros posts, mas a lista é longa e sempre me surpreendo ao descobrir algumas coisas novas por lá:

Martin Ender
fonte
No entanto, isso só funciona quando o operador usa menos UTF-8 bytes.
usar o seguinte
3

Usando Optional (:)

Optional (:) pode ser usado para expandir listas em substituições, sem precisar definir uma regra separada para a expansão.

Esta resposta por mim e esta resposta por @ngenisis são exemplos.

Uso

... /. {p___, a_: 0, b_, q___} /; cond[b] :> ...

A substituição acima usa primeiro o padrão {p___, a_, b_, q___}e encontra uma correspondência que batenda a uma determinada condição.

Quando nenhuma correspondência é encontrada, ela é omitida a_e, em vez disso , é procurada {p___, b_, q___}. anão está incluído na pesquisa e Assume-se que o valor 0.

Observe que a segunda pesquisa de padrões funcionaria apenas para bisso ocorrendo no início da lista; se um bvalor que satisfaz uma condição estiver no meio, então {p___, a_, b_, q___}(que tem uma precedência mais alta) corresponderá a ele.

A substituição é equivalente a anexar a 0quando bocorre uma condição satisfatória no início da lista. (ou seja, não há necessidade de definir uma regra separada {b_, q___} /; cond[b] :> ...)

JungHwan Min
fonte
3

Saiba quando (e quando não) usar argumentos de função pura nomeados

Para código de golfe, Functionargumentos puros são mais comumente referenciados usando Slots; por exemplo, #para o primeiro argumento, #2para o segundo, etc. (veja esta resposta para mais detalhes).

Em muitos casos, você desejará aninhar Functions. Por exemplo, 1##&@@#&é um Functionque pega uma lista como seu primeiro argumento e gera o produto de seus elementos. Aqui está essa função em TreeForm:

insira a descrição da imagem aqui

Argumentos passados para o nível superior Functionsó pode preenchimento Slots e SlotSequences presente no nível superior, que neste meio de caso que o SlotSequenceno interior Functionnão terá qualquer maneira de acessar argumentos para o nível superior Function.

Em alguns casos, no entanto, convém que um Functionaninhado em outro Functionseja capaz de referenciar argumentos para o externo Function. Por exemplo, você pode querer algo como Array[fun,...]&, onde a função fundepende de um argumento para o nível superior Function. Para concretude, digamos que fundeve dar ao restante do quadrado do seu módulo de entrada a entrada para o nível superior Function. Uma maneira de conseguir isso é atribuir o argumento de nível superior a uma variável:

(x=#;Array[Mod[#^2,x]&,...])&

Onde quer que xapareça no interior Function Mod[#^2,x]&, ele se referirá ao primeiro argumento para o exterior Function, enquanto que #se referirá ao primeiro argumento ao interior Function. Uma abordagem melhor é usar o fato de Functionter uma forma de dois argumentos em que o primeiro argumento é um símbolo ou lista de símbolos que representará argumentos nomeados para Function(em oposição a Slots sem nome ). Isso acaba poupando três bytes neste caso:

xArray[Mod[#^2,x]&,...]

é o caractere de uso privado de três bytes que U+F4A1representa o operador de infixo binário \[Function]. Você também pode usar a forma binária de Functiondentro de outra Function:

Array[xMod[x^2,#],...]&

Isso é equivalente ao acima. A razão é que, se você estiver usando argumentos nomeados, então SlotS e SlotSequencessão assumidos pertencer a próxima Functionacima do qual não utiliza argumentos nomeados.

Agora, apenas porque podemos aninhar Functions dessa maneira, não significa que devemos sempre. Por exemplo, se quisermos escolher os elementos de uma lista que são menores que a entrada, podemos ficar tentados a fazer algo como o seguinte:

Select[...,xx<#]&

Na verdade, seria mais curto usar Casese evitar a necessidade de um aninhado Functioninteiramente:

Cases[...,x_/;x<#]&
ngenisis
fonte
2

Você pode salvar um byte por trabalhar em torno Prependou PrependTo:

l~Prepend~x
{x}~Join~l
{x,##}&@@l

ou

l~PrependTo~x
l={x}~Join~l
l={x,##}&@@l

Infelizmente, isso não ajuda no mais comum Append, o que parece ser o equivalente mais curto de um Array.push()em outros idiomas.

Martin Ender
fonte
2

Mathematica 10.2: BlockMapé Partition+Map

Essa dica também pode ser intitulada "Leia as notas de versão, todas elas". (Para referência, aqui estão as notas de versão 10.2 e aqui da versão 10.3 de hoje .)

De qualquer forma, mesmo versões menores contêm diversos recursos novos, e um dos mais úteis (para jogar golfe) da versão 10.2 é a nova BlockMapfunção. Essencialmente combina Partitione Map, o que é ótimo para os golfistas, porque Partitioné usado com bastante frequência e é um nome de função realmente irritantemente longo. A nova função não será reduzida Partitionpor si só, mas sempre que você quiser mapear uma função para as partições (o que provavelmente acontece com mais frequência), agora você pode salvar um ou dois bytes:

#&/@l~Partition~2
BlockMap[#&,l,2]
#&/@Partition[l,3,1]
BlockMap[#&,l,3,1]

A economia fica ainda maior quando a nova posição da função sem nome permite salvar alguns parênteses:

#&@@(#&/@Partition[l,3,1])
#&@@BlockMap[#&,l,3,1]

Infelizmente, não faço ideia por que não adicionei também BlockApplyenquanto estavam lá ...

Observe também que BlockMapnão suporta o quarto parâmetro com o qual você pode usar Partitionpara obter uma lista cíclica:

Partition[Range@5, 2, 1, 1]
(* Gives {{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 1}} *)
BlockMap[f, Range@5, 2, 1, 1]
(* Nope... *)
Martin Ender
fonte
2

Armazenando funções e expressões em uma variável

Se sua resposta acabar usando as mesmas funções ou expressões várias vezes, considere armazená-las em variáveis.

Se sua expressão é longa le você a usa nvezes, normalmente usaria l * nbytes.

No entanto, se você armazená-lo em uma variável de comprimento 1, levaria apenas 3 + l + nbytes (ou 2 + l + nbytes, se você atribuir a variável aonde não precisará CompoundExpression (;)nem parênteses).


Por exemplo, vamos considerar um problema simples, encontrando primos gêmeos menores que N.

Pode-se escrever esta solução de 54 bytes:

Select[Range@#,PrimeQ@#&&(PrimeQ[#+2]||PrimeQ[#-2])&]&

Neste exemplo, a função PrimeQé usada três vezes.

Ao atribuir PrimeQum nome de variável, a contagem de bytes pode ser reduzida. Ambos são 48 bytes (54 - 6 bytes):

Select[p=PrimeQ;Range@#,p@#&&(p[#+2]||p[#-2])&]&
Select[Range@#,(p=PrimeQ)@#&&(p[#+2]||p[#-2])&]&
JungHwan Min
fonte
2

Para obter uma lista crescente de valores-chave, use em Sortvez deSortBy

Para listas como list = {{1, "world"}, {0, "universe"}, {2, "country"}}, as três instruções a seguir são quase equivalentes.

SortBy[list,#[[1]]&]
list~SortBy~First
Sort@list

Combine SelecteSortBy

Às vezes, precisamos escolher entradas de um conjunto maior e classificá-las para encontrar um mínimo / máximo. Sob algumas circunstâncias , duas operações podem ser combinadas em uma.

Por exemplo, no mínimo, as duas instruções a seguir são quase equivalentes.

SortBy[Select[l,SomeFormula==SomeConstant&],SortValue&]
SortBy[l,SortValue+99!(SomeFormula-SomeConstant)^2&]

e

SortBy[Select[l,SomeFormula!=SomeConstant&],SortValue&]
SortBy[l,SortValue+1/(SomeFormula-SomeConstant)&]

1/0é ComplexInfinity, que é "maior" que todos os números reais.

Para uma lista de valores-chave, por exemplo:

{SortValue,#}&/@SortBy[Select[l,SomeFormula==SomeConstant],SortValue&]
Sort[{SortValue+99!(SomeFormula-SomeConstant)^2,#})&/@l]
Keyu Gan
fonte
1

Achatando um Arraycom##&

Ao usar uma Matriz multidimensional para calcular uma lista de resultados que precisam ser nivelados, use ##&como o quarto argumento. Isso substitui as cabeças da matriz por ##&(equivalente a Sequence) em vez de List, portanto, o resultado final será um (plano) Sequencede resultados.

Em duas dimensões, compare

{Array[f,dims,origin,##&]}
Join@@Array[f,dims,origin]

Obviamente, Join@@Array[f,dims] ainda são 2 (ou 3, se a notação infix puder ser usada) bytes menores que {Array[f,dims,1,##&]}.

Em três ou mais dimensões, {Array[f,dims,origin,##&]}é sempre menor que a alternativa, mesmo que a origem seja 1.

{Array[f,dims,1,##&]}
f~Array~dims~Flatten~2
attinat
fonte
1

Valores padrão

Os valores padrão lidam com argumentos de padrão ausentes de maneira eficiente. Por exemplo, se quisermos correspondência de padrões Exp[c_*x]em uma regra para qualquer valor de c, a ingênua

Exp[x] + Exp[2x] /. {Exp[c_*x] -> f[c], Exp[x] -> f[1]}
(*    f[1] + f[2]    *)

usa muito mais bytes do que se usarmos o valor padrão csempre que estiver ausente:

Exp[x] + Exp[2 x] /. Exp[c_.*x] -> f[c]
(*    f[1] + f[2]    *)

O uso de um padrão é indicado com um ponto após o padrão: c_..

Os valores padrão estão associados às operações: no exemplo acima, a operação está Timesinserida c_.*xe, c_portanto , um valor ausente é obtido do valor padrão associado a Times, que é 1. Para Plus, o valor padrão é 0:

Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(*    f[0] + f[2]    *)

Para Powerexpoentes, o padrão é 1:

x + x^2 /. x^n_. -> p[n]
(*    p[1] + p[2]    *)
romano
fonte