Respostas:
As dicas abaixo variam do mais econômico ao mais usado:
Use os comandos de alto nível do Mathematica sempre que possível, mesmo os volumosos:
MorphologicalComponents
: Código-Golfe: Count Islands
Recursos de manipulação de imagem: por exemplo, hoje (24 de setembro) é o aniversário da HONDA
Subsets
IntegerPartitions
Medidas de distância e semelhança: por exemplo, EuclideanDistance
pode ser um economizador de bytes. Observe, no entanto, que geralmente é mais curto escrever em Total@Abs[a-b]
vez de a~ManhattanDistance~b
e em Max@Abs[a-b]
vez de a~ChessboardDistance~b
.
Use Graphics and
Text
para arte Ascii: por exemplo, programação em estrela!
e construa um relógio analógico
Símbolos dedicados:
símbolos de operações lógicas e de conjunto em vez de seus nomes longos: ⋂, ⋃, ∧, ∨
Map
e Apply
: /@
, //@
. @@
,@@@
Notação de prefixo e infixo:
Print@"hello"
no lugar de Print["hello"]
a~f~b
no lugar de f[a,b]
Quando uma função é usada apenas uma vez, uma função pura pode economizar um ou dois caracteres.
Juntando seqüências de caracteres em uma lista. ""<>{"a","b","c"}
ao invés deStringJoin@{"a","b","c"}
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}}
Algumas funções internas com nomes longos podem ser substituídas por expressões mais curtas.
Por exemplo:
Total
=> Tr
Transpose
=> Thread
ou\[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
=> {}⋃a
oua⋃a
ToExpression@n
=> FromDigits@n
se n
é um númeroDivisible[n,m]
=> m∣n
FromDigits[n,2]
=> Fold[#+##&,n]
Se n
é uma lista de 0
s e 1
sComplex@z
=> {1,I}.z
onde z
está uma lista do formulário{x,y}
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}}]
Fold
truque FromDigits
també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 100
e 1000
).
U+F3C7
.
Echo
seja uma opção, porque ela imprime >>
(e um espaço) em STDOUT antes de imprimir a string real.
Complex[x,y] => {1,I}.{x,y}
, eu acho que x+y*I
é muito mais curto com o mesmo efeito?
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~6
nesse 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 é Listable
e 0*x
é 0
para quase todos x
(exceto para coisas como Infinity
e Indeterminate
), então aqui está o que está acontecendo:
0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}
Para listas de 1
s, 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 1
s na lista:
{1,1,1}
1^{,,}
{,,}^0
1^{,,,}
é um byte menor que 0{,,,}+1
.
{,,}^0
. Eu vou editar a postagem.
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.
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).
Um dos principais recursos novos de linguagem do Mathematica 10 é o Association
s, 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.
#0
Um recurso menos conhecido é que #0
també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]]&
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 List
ou 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, ##3
todos os argumentos a partir do terceiro etc. Portanto, para começar, podemos apenas reimplementar Sequence
como ##&
, 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+b
na 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 Unequal
se você tiver um valor de caractere único ou variável que você sabe que não esteja em seus argumentos ( N
provavelmente 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!
Sqrt@2
ou 2^.5
=>√2
a[[1]]
=>a〚1〛
#+#2&
=>+##&
Flatten@a
=> Join@@a
(às vezes)
Function[x,x^2]
=> xx^2
ou#^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
〚
e use 〛
3 bytes cada (assuma UTF8) #
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, ##&[]·x
se 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 $CharacterEncoding
ser definido como um valor compatível, como o padrão do Windows WindowsANSI
. Em alguns sistemas, esses padrões UTF-8
não podem ler esses pontos de código a partir de bytes únicos.
A abordagem ingênua para escolher entre y
e z
, dependendo de x
ser 0
ou 1
não
If[x<1,y,z]
No entanto, há uma maneira mais curta:
y[z][[x]]
Isso funciona porque [[0]]
dá a Head
expressã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 u
for 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 x
for 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]]
Length
Isso foi totalmente reescrito com algumas sugestões de LegionMammal978 e Misha Lavrov. Muito obrigado a ambos.
Em muitos casos, Length
pode ser reduzido um pouco usando Tr
. A idéia básica é transformar a entrada em uma lista de 1
s, para que a Tr
soma seja igual à duração da lista.
A maneira mais comum de fazer isso é usar 1^x
(para uma lista x
). Isso funciona porque Power
é Listable
e 1^n
para 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 x
contiver apenas 0
s e 1
s, podemos salvar outro byte usando Factorial
(supondo x
que a precedência seja maior que !
):
Length@x
Tr[x!]
Em alguns casos raros, x
pode 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 Tr
fornece 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 Tr
expressões -existem ainda Length
.
Length@x == Tr[1^x]
. Deve funcionar com a maioria das listas.
Tr[x!]
vez de Tr[1^x]
salvar um byte no caso especial em que x
apenas contém zeros e uns.
Explore soluções recursivas - o Mathematica é multiparadigma, mas a abordagem funcional é geralmente a mais econômica. NestWhile
pode ser uma solução muito compacta para pesquisar problemas NestWhileList
e 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.
Formulário reduzido para incremento / decremento - por exemplo, em vez de While[i<1,*code*;i++]
você pode fazerWhile[i++<1,*code*]
Não esqueça que você pode pré-incrementar / diminuir - por exemplo, em --i
vez de i--
. Às vezes, isso pode economizar alguns bytes no código circundante, eliminando uma operação preparatória.
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 ToExpression
quatro vezes em uma solução, t=ToExpression
poderá 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.
{}
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}
Alternatives
tem 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 Apply
tem uma precedência maior que Alternative
.
Nesse caso, você deve simplesmente usar f@@{a,b,c}
.
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 list
s diferentes . Antes, você provavelmente teria atribuído SortBy
a s
ea função pura para f
assim
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]
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
%
para obter uma variável livreEssa 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 a
qualquer 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 %n
posso 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=
.
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=...]==a
que é 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.
##>0&@@a
. Semelhante para o limite superior para classificado.
Em vez de StringRepeat[str,n]
usar (0Range[n]+str)<>""
. Ou se str
não depender de nenhum argumento de slot, o melhor é Array[str&,n]<>""
usar esta dica.
StringRepeat[s,n+1]
usar Array[s&,n]<>s
(mesmo quando você já possui n+1
uma variável).
Table[str,n]<>""
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 SortBy
cenários:
Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]
-Sort@-x
?
Você pode colar uma expressão na Break
qual 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 Break
parece retornar ao loop circundante, o que pode potencialmente levar a ainda mais economia.
Para remover todo o espaço em branco de uma string s
, use
StringSplit@s<>""
Ou seja, use StringSplit
o 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"<>""
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.
Array
vez dissoO exemplo acima mostra que o uso Array
tem 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, f
poderá 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- Array
se em notação de infixo e também salvar um byte:
#&/@Range@n
#&~Array~n
Portanto, Array
é quase certamente melhor que Range
.
Table
vez dissoAgora, 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 Table
dar 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, Array
também é mais longo neste caso, mas ainda menor que Range
:
(i=#;i&[])&~Array~n
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
f/@Range[x]
regularmente ... #
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 i
por i+1
enquanto ele atende à condição cond[i]
. Nesse caso, i
começ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, While
seria melhor. ( MaxIterations->∞
é muito longo)
Às vezes, você pode substituir If
por 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 If
ou quando precisar de argumentos verdadeiros e falsos de If
.
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á:
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 b
atenda a uma determinada condição.
Quando nenhuma correspondência é encontrada, ela é omitida a_
e, em vez disso , é procurada {p___, b_, q___}
. a
não está incluído na pesquisa e Assume-se que o valor 0
.
Observe que a segunda pesquisa de padrões funcionaria apenas para b
isso ocorrendo no início da lista; se um b
valor 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 0
quando b
ocorre 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] :> ...
)
Para código de golfe, Function
argumentos puros são mais comumente referenciados usando Slot
s; por exemplo, #
para o primeiro argumento, #2
para o segundo, etc. (veja esta resposta para mais detalhes).
Em muitos casos, você desejará aninhar Function
s. Por exemplo, 1##&@@#&
é um Function
que pega uma lista como seu primeiro argumento e gera o produto de seus elementos. Aqui está essa função em TreeForm
:
Argumentos passados para o nível superior Function
só pode preenchimento Slot
s e SlotSequence
s presente no nível superior, que neste meio de caso que o SlotSequence
no interior Function
não terá qualquer maneira de acessar argumentos para o nível superior Function
.
Em alguns casos, no entanto, convém que um Function
aninhado em outro Function
seja capaz de referenciar argumentos para o externo Function
. Por exemplo, você pode querer algo como Array[fun,...]&
, onde a função fun
depende de um argumento para o nível superior Function
. Para concretude, digamos que fun
deve 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 x
apareç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 Function
ter 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 Slot
s sem nome ). Isso acaba poupando três bytes neste caso:
xArray[Mod[#^2,x]&,...]
é o caractere de uso privado de três bytes que U+F4A1
representa o operador de infixo binário \[Function]
. Você também pode usar a forma binária de Function
dentro de outra Function
:
Array[xMod[x^2,#],...]&
Isso é equivalente ao acima. A razão é que, se você estiver usando argumentos nomeados, então Slot
S e SlotSequences
são assumidos pertencer a próxima Function
acima do qual não utiliza argumentos nomeados.
Agora, apenas porque podemos aninhar Function
s 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[...,xx<#]&
Na verdade, seria mais curto usar Cases
e evitar a necessidade de um aninhado Function
inteiramente:
Cases[...,x_/;x<#]&
Você pode salvar um byte por trabalhar em torno Prepend
ou 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.
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 BlockMap
função. Essencialmente combina Partition
e 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 Partition
por 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 BlockApply
enquanto estavam lá ...
Observe também que BlockMap
não suporta o quarto parâmetro com o qual você pode usar Partition
para 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... *)
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 l
e você a usa n
vezes, normalmente usaria l * n
bytes.
No entanto, se você armazená-lo em uma variável de comprimento 1, levaria apenas 3 + l + n
bytes (ou 2 + l + n
bytes, 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 PrimeQ
um 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])&]&
Sort
vez 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
Select
eSortBy
À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]
Array
com##&
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) Sequence
de 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
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 c
sempre 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á Times
inserida c_.*x
e, 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 Power
expoentes, o padrão é 1:
x + x^2 /. x^n_. -> p[n]
(* p[1] + p[2] *)
(Norm[#-#2]&)
do que paraEuclideanDistance
.