Dicas para jogar golfe em J


33

O GolfScript tem seu próprio caminho com muita frequência e sinto que um repositório de dicas úteis para jogar golfe em J pode ajudar na luta contra o império do mal. Que dicas você tem para tornar essa linguagem já concisa mais curta?

Para aqueles que querem aprender J, o lugar óbvio para começar é o jsoftware local e, particularmente, o vocabulário , a Aprendizagem J guia e o J para programadores C guia.


11
Há algo de engraçado em ler GolfScript gets its own way far too oftenem 2019.
String não relacionada

Respostas:


14

Existem várias sutilezas para extrair os últimos caracteres em J. Para o seguinte, suponha que cada letra maiúscula seja um verbo primitivo (ou seja, estou removendo os espaços que seriam necessários para delimitar nomes).

  • Quando você tem um trem indo e precisa aplicar uma função no topo de outro meio, ([:FLGR)além de (LF@:GR)ter o mesmo número de caracteres, mas (LF@GR)salva um. Se o quadro de G for maior ou igual à classificação de mônada de F, essa é uma transformação válida. Notavelmente, todos os trens têm uma classificação infinita, assim como a , ,. ,: ~. /: \: [ ]maioria dos usos de #e |..

  • Se você precisar escolher cadeias de caracteres de uma lista e essas cadeias não tiverem espaços, use >i{ab`cd`ef. Está sujo, mas salva caracteres para cada nova string com a qual você precisa lidar, a menos que você esteja apenas puxando caracteres únicos e, mesmo assim, a lista de caracteres precisa ter o tamanho 4 para ser menor. O que está acontecendo é que nomes indefinidos são tratados como referências a verbos, e quando você pega os gerúndios desses verbos, obtém uma sequência em caixa do nome. Qualquer nome que já esteja definido como tendo o tipo substantivo, advérbio ou conjunção não pode ser usado dessa maneira, porque esses nomes são resolvidos antes `.

  • Se você tiver a sorte de ter uma expressão para trabalhar e não apenas um verbo tácito, quase sempre vale a pena atribuir quaisquer bits que você reutilizar a variáveis, sejam eles substantivos, verbos ou advérbios. Às vezes, os parênteses se recompensam ajustando-se exatamente onde você tinha espaços antes, e a maioria dessas definições vale a pena se forem reutilizadas ainda mais uma vez.

  • Conjunções como (FGH)^:(u`v`w)podem ser reescritas u`v`w(FGH^:). Isso funciona para qualquer extensão de trem, mesmo 1, embora você salve apenas qualquer coisa se esse truque remover os parênteses do argumento correto. Esse truque só funciona quando você pré-carrega o operando esquerdo. (Não tem idéia do que aconteceu? Procure 'advérbios tácitos' e estude a seção Análise e Execução do Dicionário J.)

  • Não use a.&i., use u:! {&a.e 3&u:são equivalentes em comprimento, no entanto, e o primeiro pode ser mais útil em uma conjunção (dependendo da conjunção).

  • Coisas como (2%~F)e (F%2:)são equivalentes em comprimento. Isso é útil porque, às vezes, dependendo da aparência do resto do seu trem, você pode reestruturá-lo com @truques, conforme escrito no primeiro ponto, para salvar alguns personagens desesperados. (E, claro, se Ffor ]e o trem for uma mônada, usar %&2salva um char, duh.)

  • Trens semelhantes a ganchos com ]ou [como o verbo mais à esquerda, por exemplo (]FGH).

    • ]permite dividir um aplicativo diádico e usar apenas o argumento correto. (Troque para a esquerda com (]FGH)~uma penalidade de pelo menos 1 caractere, talvez mais.) Salva um caractere (FGH)@]e é muito útil em gerúndios!
    • [em um gancho aplicado monadicamente, você pode fazer algo pelos efeitos colaterais do lado direito e retornar o argumento novamente. O uso mais comum é com 1!:2, possivelmente com a formatação de lixo eletrônico.
  • E / S é uma merda. Acelere o processo criando loops de tudo o que puder. 1!:1possui classificação 0e ambas 1!:2 3possuem classificação _ 0, por exemplo, utilize-a criando matrizes de 1s e passando 1!:1diretamente sobre elas. Observe que ".também possui a classificação 1; portanto, você também pode colocá-la diretamente depois 1!:1e também não precisar anexá-la por meio de @ou classificar travessuras.

  • Não é fácil encontrar lugares para colocar isso, mas ::pode ser útil.

    • ::]^:_é uma combinação particularmente poderosa, por exemplo, que permite fazer algo perigoso até que você não possa mais fazer isso. (Sujeito às ^:_advertências usuais como um loop.)

    • Isso também permite que você use {em listas que não possuem o índice desejado, porque gera um erro de domínio quando isso acontece. Útil, por exemplo, assumir o topo de uma lista somente se ela existir (tente usar ::]para retornar a lista vazia ou ::_1:retornar um código de erro e assim por diante).

  • ]`($:@u)@.vgeralmente pode ser menor do que u^:v^:_, especialmente nas definições de ue com as vquais você pode brincar. Um caso semelhante vale para a condicional-like u^:(1-v)vs. ]`u@.v. Considere suas opções, especialmente quando você tem muitos verbos nomeados flutuando. Também é um pouco mais flexível, mas lembre-se, se estiver usando $:, há uma profundidade de recursão que é fácil de enfrentar. (Geralmente algo como 1800 iterações?)


O truque adverso é muito legal.
FUZxxl

"salve alguns personagens desesperados" Quando você estuda iluminado, tudo parece um epíteto transferido! :)
Soham Chowdhury

11
"usar %&2salva um caractere, duh." E -:salva outro!
Lynn

11

A coisa mais importante ao jogar golfe em J é não apenas entender o problema, mas reduzi-lo a uma série de transformações de matriz. Você precisa entender essa maneira de pensar para obter sucesso com o código J.

Por exemplo, um desafio recente pediu para resolver o maior problema de sub-matriz . O algoritmo de estoque para resolver esse problema é o algoritmo de Kadane, com a seguinte descrição informal:

Percorra a matriz e em cada posição e encontre a soma do maior sub-arranjo que termina aqui, que é o máximo de 0 ou o valor no índice atual mais a soma do maior sub-arranjo que termina na posição anterior. Calcule o máximo desses sub-arranjos à medida que procura o maior sub-arranjo em toda a matriz.

Uma tradução para código imperativo é direta:

  1. seja A a matriz de entrada.
  2. hmi ← 0.
  3. se i ≥ len (A) retornar m .
  4. h ← max (0, h + A [ i ]).
  5. m ← máx ( m , h ).
  6. ii + 1.
  7. ir para 3.

Este algoritmo parece complicado para J de relance, pois há um loop explícito que não parece uma redução a princípio. Se você perceber o que o algoritmo está fazendo, poderá desvendar as etapas individuais e verificar se ele realmente executa duas operações simples de matriz:

  1. Examine a matriz para calcular os comprimentos das maiores sub-matrizes que terminam em cada índice.
  2. Reduza esses comprimentos com a função max para encontrar o máximo.

Agora, essas duas etapas são muito fáceis de implementar em J. Aqui está uma tradução:

  1. (0 >. +)/\. y , 0- Esta etapa opera da outra extremidade da matriz para melhor se ajustar ao paradigma de J. 0 >. +é tácito 0 >. x + y.
  2. >./ y

Em conjunto, obtemos uma implementação muito concisa do algoritmo:

>./ (0 >. +)/\. y , 0

Se você aprender essa maneira de abordar a implementação de algoritmos, suas soluções serão tão concisas quanto esse código.

Aqui estão alguns truques que acumulei ao longo do tempo. Esta lista será expandida à medida que eu tiver mais conhecimento em golfe.

  • Aprenda o dicionário. Ele contém muitos verbos realmente obscuros que não fazem sentido até você ver como eles são úteis. Por exemplo, a monádica =é estranha no começo, mas é muito útil nos desafios da arte ASCII.
  • Use diádico &em contextos tácitos quando desejar uma conjunção de poder. O vocabulário sugere u@[&0como uma substituição tácita para 4 : 'u^:x ye eu também.
  • Em muitos casos, você pode evitar uma [:ou @:em uma sequência u@v, escolhendo uma variante ucom argumento à esquerda. Por exemplo, para descartar o primeiro item do resultado de v, use em 1}.vvez de [:}.vse }.@vnão for possível por algum motivo.
  • ] vgeralmente é mais curto do que v@]se você quiser usar monádico vem um contexto diádico. Isso é útil principalmente quando vhá um longo conjunto de verbos.
  • Às vezes você pode escrever em m (n v w) yvez de (n v m&w) y. Isso pode permitir evitar espaços e parênteses.
  • #\em vez de >:@i.@#.
  • u &. vé útil quando vtem um anverso. Caso contrário, você pode usar [: vinv u & vou u & (v :. vinv)não.
  • Entenda a classificação e como usá-la. Tente mexer com a conjunção de classificação até conseguir algo que se encaixe. Isso ajuda a entender como a classificação influencia seu código.
  • ^:_ é extremamente útil para algoritmos em que você deseja alcançar a convergência, como um preenchimento ou simulações.
  • Conheça a sua biblioteca padrão. Ele contém funções muito úteis que economizam muitos caracteres.
  • A copula =.e =:pode ser incorporada em qualquer lugar da frase. Use isso para criar uma linha onde a notação tácita não é suficiente.
  • Use monádico em ,vez de várias reduções ao reduzir matrizes multidimensionais.
  • Entenda quais frases são suportadas por código especial quando o desafio impõe limites de tempo de execução. Algumas coisas úteis operam em O ( n ) em vez de O ( n 2 ) contra-intuitivamente.
  • Caixas são úteis para árvores.

J é inteligente o suficiente para executar sua solução de subarray máxima em O (n), armazenando em cache cálculos reutilizados ou fará isso de maneira direta e executará em O (n ^ 2)?
Jonah

@ Jonah, acho que é executado no tempo quadrático.
FUZxxl 23/10/1918

10

Desconfie de usar loops.

Enquanto J possui estruturas em loop ( for. do. end., while. do. end.e variações), se você as usar, existe a possibilidade de que seu algoritmo não esteja de acordo com os pontos fortes do golfe de J e que haja uma economia de caracteres a ser feita.

^:a conjunção de poder é sua amiga. Para executar um verbo xvezes:

verb^:x

Se você precisar do resultado de cada iteração em uma lista:

verb^:(i.x)

Você também pode usar ^:para executar um verbo condicionalmente:

  +:^:(3<])"0[ 1 2 3 4 5 6
1 2 3 8 10 12

Dobrar +:se ^:o item for maior que 3 3<](isso "0altera a classificação do verbo para que funcione um item de cada vez).


Valores em caixa agem como o (i.x)exemplo, ou seja, f^:(<x)é equivalente a f^:(i.x).
FireFly

9

Entrada

1!:1[1 terá uma linha de entrada finalizada pressionando a tecla Enter.

1!:1[3 terá várias linhas de entrada (finalizadas com Ctrl-D no meu Mac, Ctrl-C no Windows).

Se você estiver tentando inserir números, o uso ".avaliará a sequência e retornará uma lista de números prontos para serem manipulados. Se você estiver digitando um número, mas precisar operar os dígitos individualmente, ".,.(graças ao comentário de Jan Dvorak por isso) ou "."0dividirá a sequência em dígitos separados:

   "."0[1!:1[1
12345
1 2 3 4 5

   ".,.1!:1[1
12345
1 2 3 4 5

Se você estiver lendo em strings, a maneira mais curta de obter uma lista em caixa de strings separadas é usar ;:. Isso funciona melhor para cadeias separadas por espaço:

   ;:1!:1[1
hello world
┌─────┬─────┐
│hello│world│
└─────┴─────┘

Por curiosidade, (eu só brinquei com J um pouco) o que 1!:1[2funcionaria como (se alguma coisa)?
Gaffi 12/07/12

Pelo que consigo reunir na 1!:página (não sou especialista em J) 2 é a tela; portanto, a entrada da tela não faz muito sentido.
Gareth

Obrigado pelo link. A partir daí, realmente parece que 2não é válido? Não tenho meu computador J para testá-lo no momento. Onde eu vejo 2, logo abaixo das notas sobre 1!:1, é para 1!:2.
Gaffi

@ Gaffi Os números dos arquivos de entrada e saída parecem ser seqüenciais, então acho que esses números são fixos e que 2, 4 e 5 só aparecem na saída porque não faz sentido tentar digitar a partir deles. O mesmo vale para o 1 e o 3.
Gareth

Como ".está no rank 1-xx e ,.sempre produz uma matriz 2D, ".,' ',.(pontos com espaço, passear e avaliar; 8 caracteres) podem ser substituídos por apenas ".,.(passear itens e avaliar; 4 caracteres).
John Dvorak

6

Usando iteração para calcular seqüências

Normalmente, resolver um desafio de sequência OEIS exigirá o uso de uma das fórmulas fornecidas em sua página. Alguns deles se adaptam bem a J e outros nem tanto. As fórmulas recursivas são diretas; no entanto, a iteração pode não ser simples. Um padrão que comecei a usar é

(s(]f)^:[~]) n
          ]  Gets n
 s           The first value in the sequence
         ~   Commute the argument order, n is LHS and s is RHS
        [    Gets n
      ^:     Nest n times with an initial argument s
  (]f)         Compute f s
             Returns (f^n) s

onde sé o primeiro valor da sequência, fé um verbo que calculará o próximo termo, dado o termo anterior, e né o índice baseado em zero do termo que você deseja calcular. Esse método baseia-se no fato de que, ao calcular o poder de uma díade, o LHS está vinculado à díade para formar uma nova mônada, e essa mônada está aninhada no valor inicial. A díade dada ao advérbio de poder é um gancho, onde (]f)é dado o índice nno LHS e o valor de um termo na sequência s. O gancho será aplicado fna scomo uma mônada, e depois ignorar na devolver o resultado de f s.

Biblioteca padrão

Às vezes, você pode achar que J terá suporte para um verbo em sua biblioteca padrão . Por exemplo, a maioria das operações com números inteiros de bits é vinculada a nomes mais curtos que o uso da chamada primitiva.

AND =: (17 b.) NB. it is actually '$:/ :(17 b.)'

Data e hora incorporadas também estão disponíveis.

Gamas

Se você tiver um conjunto de valores [a, b, c]e desejar formar um intervalo com base no produto deles [0, 1, 2, ..., a*b*c-1], a abordagem típica seria encontrar o produto e formar um intervalo que pode [:i.*/custar 6 bytes. Um caminho mais curto é ,@i.para 4 bytes, pois i.pode formar matrizes multidimensionais enquanto continua a contagem e, se o achatamento, ele produzirá um intervalo equivalente.

Imprimindo continuamente

Uma maneira tácita de imprimir um valor e continuar a usá-lo sem um loop explícito é ([echo)para um caso monádico. echoé um verbo na biblioteca padrão que imprime seu conteúdo stdoutno mesmo formato usado no intérprete. O gancho passa o mesmo valor de entrada usando o [verbo esquerdo .

Base 10 dígitos de um número inteiro

A maneira padrão de adquirir os 10 dígitos básicos de um número inteiro é o 10#.inv]que custa 8 bytes, demais! Uma alternativa é convertê-lo em uma string e analisá-lo na classificação 0, "."0@":que salva um byte, mas uma maneira ainda melhor é ,.&.":salvar outro byte, tornando o custo final de 6 bytes em vez de 8.


5

Considere usar definição explícita em vez de escrever um verbo tácito; -se que o 3 :'e 'custam 5 bytes, mas você pode economizar muito @, @:e [:dessa forma.


5

Alguns truques (razoavelmente) comuns que eu já vi

Estou compartilhando algumas coisas que foram úteis para mim. Basicamente, todas essas são dicas que recebi, mas não tenho créditos para a maioria.

Soma de uma matriz de classificação um

Em vez de usar +/@:(FGH)use (1#.FGH). Isso significa degradar para a base 1, o que efetivamente significa somar uma matriz. Embora seja mais longo que +/isso, não requer uma tampa ou composição, o que geralmente o torna muito mais curto que o uso +/.

Contando as verdades finais

Se você possui uma lista booleana e deseja contar o número de verdades finais, use #.~. Veja aqui . A resposta da APL fornece uma boa explicação de como isso funciona. É verdade que isso só me foi útil duas vezes, mas achei que fosse compartilhar de qualquer maneira.

Sob (&.)

Não é um truque específico, mas apenas uma sugestão geral: o advérbio &.- geralmente leva a soluções elegantes e (mais importante) curtas. Lembre-se quando estiver jogando golfe.

Muitas vezes, é útil para desafios de conversão e outras bases, por exemplo, este código que remove o bit mais significativo de um número: }.&.#:(converter para lista de dígitos binários, remover o primeiro dígito e desfazer a conversão para uma lista de dígitos binários e converter de volta ao decimal). A solução simples é mais dois bytes: #.@}.@#:.

Under também é útil para desafios em que você precisa trabalhar com dígitos decimais, pois é possível usá-lo u&.":. Por exemplo, a maneira como as milhas curto dá para dividir a dígitos decimais usos em: ,.&.":.

Um exemplo final é encontrar a magnitude de um vetor :, +/&.:*:observe que você precisa coletar todos os resultados de *:-square com &.:-under, pois *:-square está na classificação zero.


4

Maneiras mais curtas de mexer com as fileiras

Às vezes, você terá um código como <"0 i.3 3, onde deseja aplicar um verbo vna classificação r. No entanto, se você usar um substantivo (como 0), geralmente precisará incluir um espaço. Para evitar isso, você pode usar outro verbo ude classificação equivalente e usar u"v. Por exemplo, como +tem classificação 0 0 0, podemos usar em <"+vez de <"0.

Aqui está uma tabela de todos os verbos e suas classificações (obtidos usando-se v b. 0):

0 0 0     > + * - % ^ | ! ? <. <: >. >: +. +: *. *: %: ^. j. o. q: r.
0 _ _     -. -: E. i: p:
1 0 1     p..
1 0 _     { A.
1 1 0     p.
1 1 1     #.
1 1 _     C.
1 _ _     ;: ". i. I.
2 _ 2     %.
_ 0 0     = < ~. ~: {: }: ?. L.
_ 1 0     #:
_ 1 _     $ # |. |: {. }. ": {::
_ _ _     , ; [ ] _: $. $: ,. ,: /: \: [: e. s: u: x: 0:

Para usar esta tabela, encontre a classificação desejada rno lado esquerdo e escolha um verbo apropriado vno lado direito. Por exemplo, se eu precisar vetorizar um verbo vem profundidade 2 _ 2, encontro essa classificação à esquerda e escolha %.à direita. Então eu uso em v"%.vez de v"2 _ 2.


3

strings biblioteca: dicas de golfe

A biblioteca de strings é muito útil para fazer qualquer coisa com manipulação de strings. Claro, é preciso include'strings'(o que é muito caro, considerando J), mas às vezes você pode colher os benefícios.

stringreplace

Você se encontra usando a cadeia substituir? Observe que A stringreplace Bé o mesmo que B rplc A.

De fato, é assim que rplcé implementado:

   rplc
 stringreplace~

cuts

O verbo cutsfornece assim:

corte y em x (conjunção)
string (verbo corta n) texto
  n = _1 até, mas sem incluir a sequência
  n = 1 até e incluindo a string
  n = _2 depois, mas sem incluir a sequência
  n = 2 depois e incluindo string

Então, está realmente cortando uma corda.


3

Obtendo números de 0 a 4

Se houver uma restrição no uso de números no seu código:

0 %_ : um dividido pelo infinito.
1 #_ : quantos infinitos?
2 #_ _ : dois infinitos.
3 verb : há um embutido.
4 dyad : outro embutido.

Obtendo números de 10 a 35

Literais base-inifinity: 11 : _bb, 26 : _bqetc.


3

Programação tácita

Noções básicas

Verbo diádico

x (F G H) y == (x F y) G (x H y)
x (F G) y == x F (G y)
x ([: G H) y == G (x H y)  NB. G is called monadically

NB. Verbs are grouped from the right by units of 3.
NB. For the following, think like G, I, K are replaced by the results of (x G y) etc.
NB. and then the sentence is run as usual.
x (F G H I J K) y == x (F (G H (I J K))) y
                  == x F ((x G y) H ((x I y) J (x K y)))

NB. Using conjunctions for dyadic verb
x F@G y == F (x G y)  NB. Atop; Same as x ([: F G) y; Consider as golfing alternatives
x F&G y == (G x) F (G y)  NB. Compose; G is applied monadically to both arguments

Verbo monádico

(F G H) y == (F y) G (H y)
(G H) y == y G (H y)  NB. Note that this is different from APL
([: G H) y == G (H y)
(F G H I J K) y == (F (G H (I J K))) y
                == y F ((G y) H ((I y) J (K y)))
F@G y == F (G y)

Diversos

x&F y == x F y
F&y x == x F y
y F~ x == x F y
F~ y == y F y

Truques

(F x) G (H y)

Solução tácita: (G~F)~H; dependendo dos verbos reais, considere reorganizar os argumentos esquerdo e direito para remover ~.

x ((G~F)~H) y
x (G~F)~ (H y)
(H y) (G~F) x
(H y) G~ (F x)
(F x) G (H y)

Substituições monádico-diádicas

>:y == 1+y
<:y == 1-~y or _1+y
+:y == 2*y
-.y == 1-y
-:y == 2%~y
*:y == 2^~y
#.y == 2#.y
#.inv y == 2#.inv y  NB. #: doesn't work this way
{.y == 0{y
{:y == _1{y
}.y == 1}.y
+/y == 1#.y

11
(G~F)~Hé pura bondade borbulhante!
Jonah

2

& é seu amigo, use com sabedoria

vé um verbo, né um substantivo xe ysão argumentos à esquerda e à direita, respectivamente.

Mônada &: Introduzir ~cadeia interna de advérbios / conjunções

Uma cadeia de advérbio / conjunção é avaliada a partir da esquerda. Então, algo como _2&+/\&.>não funciona porque analisa como (_2&+)/\&.>queremos _2&(+/\)&.>. Nesse caso, trocar a esquerda / direita de +/\pode salvar um byte, como em +/\~&_2&.>porque este analisa como ((+/\)~)&_2&.>. Para ver por que isso funciona:

+/\~&_2 y
is equivalent to
y +/\~ _2
is equivalent to
_2 +/\ y
is equivalent to
_2&(+/\) y

Díada &: Repetir os xtempos

Você sabia que, se você der um argumento xà esquerda &, a função o aplicará xvezesy ? Alguns desafios pedem que você faça determinados xtempos de operação . É principalmente possível de duas maneiras:

  • Use o operador de energia ^:sem o operando direito

Se a operação for v, v^:torna-se um trem de advérbio que, quando recebe um operando esquerdo, torna-se um verbo monádico. Então vé aplicado a y, xtimes.

x(v^:)y
is equivalent to
(v^:x)y
  • Use o diádico &como a conjunção mais externa

Para usar isso, você precisa identificar um constante ne um verbo diádico u, de modo que n u you y u nseja equivalente a v. Então você pode escrever n&uou u&nresolver a tarefa inteira. Este formulário é mais eficaz quando a escolha da constante é óbvia, por exemplo, 3 pol 3 u:(converte caracteres em valores ASCII).

Além disso, u&né um pouco preferido n&uquando a estrutura mais externa de ué uma conjunção ou advérbio (nesse caso, n&udeve ser n&(u); você pode fazer isso u~&n).

Observe que você pode colocar o diádico &em qualquer lugar do trem para conseguir repetir a função arbitrária em argumentos arbitrários, no mesmo sentido que dinâmico ^:.

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.