Suggerimenti per giocare a golf in J


33

GolfScript si fa strada troppo spesso e ritengo che un deposito di suggerimenti utili per giocare a golf a J possa aiutare nella lotta contro l'impero malvagio. Che consigli hai per abbreviare questo linguaggio già conciso?

Per coloro che vogliono imparare J, il posto più ovvio per iniziare è il jsoftware sito e in particolare il vocabolario , l' apprendimento J guida e la J per i programmatori C guida.


1
C'è qualcosa di divertente GolfScript gets its own way far too oftennella lettura nel 2019.
Unrelated String

Risposte:


14

Ci sono una serie di sottigliezze per eliminare gli ultimi caratteri in J. Per quanto segue, supponiamo che ogni maiuscola sia un verbo primitivo (cioè sto rimuovendo gli spazi che sarebbero altrimenti necessari per delimitare i nomi).

  • Quando hai un treno in marcia e devi applicare una funzione sopra un'altra a metà strada ([:FLGR)e (LF@:GR)avere lo stesso numero di caratteri, ma ne (LF@GR)salva uno. Se il frame di G è maggiore o uguale al rango di monade di F, questa è una trasformazione valida. In particolare, tutti i treni hanno un rango infinito, così come la , ,. ,: ~. /: \: [ ]maggior parte degli usi di #e |..

  • Se devi selezionare le stringhe da un elenco e queste stringhe non hanno spazi, usa >i{ab`cd`ef. È sporco, ma salva i caratteri per ogni nuova stringa con cui hai a che fare, a meno che tu non stia semplicemente tirando singoli caratteri e anche in questo caso la lista dei caratteri deve essere lunga 4 per essere più corta. Quello che sta succedendo è che i nomi indefiniti sono trattati come riferimenti ai verbi e quando prendi i gerundi di quei verbi ottieni una stringa inscatolata del nome. Tutti i nomi che sono già definiti come aventi nome sostantivo, avverbio o congiunzione non possono essere usati in questo modo, perché quei nomi che vengono risolti prima `possono averli.

  • Se sei abbastanza fortunato da avere un'espressione con cui lavorare e non solo un verbo tacito, vale quasi sempre la pena assegnare qualsiasi bit che riutilizzi alle variabili, siano essi nomi, verbi o avverbi. A volte i genitori si ripagano adattandosi esattamente dove prima avevi spazi, e la maggior parte di tali definizioni vale la pena se vengono riutilizzate ancora una volta.

  • Congiunzioni come (FGH)^:(u`v`w)possono essere riscritte u`v`w(FGH^:). Funziona per qualsiasi lunghezza del treno, anche 1, anche se risparmi qualcosa solo se questo trucco rimuove le parentesi dall'argomento giusto. Questo trucco funziona solo quando si precarica l'operando di sinistra. (Non hai idea di cosa sia appena successo? Cerca 'avverbi taciti' e studia la sezione Analisi ed esecuzione del Dizionario J.)

  • Non usare a.&i., usa u:! {&a.e 3&u:sono equivalenti in lunghezza, tuttavia, e il primo potrebbe essere più utile in una congiunzione (a seconda della congiunzione).

  • Cose come (2%~F)e (F%2:)sono equivalenti in lunghezza. Ciò è utile perché a volte, a seconda dell'aspetto del resto del treno, è possibile ristrutturarlo con @trucchi come scritto nel primo punto, per salvare alcuni personaggi disperati. (E ovviamente, se lo Fè ]e il treno è una monade, usando %&2save a char, duh.)

  • Treni ad uncino con ]o [come verbo più a sinistra, ad es (]FGH).

    • ]ti permette di dividere un'applicazione diadica e usare solo l'argomento giusto. (Scambia a sinistra con(]FGH)~ , con una penalità di almeno 1 personaggio, forse di più.) Salva un personaggio (FGH)@], ed è molto utile in gerundi!
    • [in un gancio applicato monadicamente ti consente di fare qualcosa per gli effetti collaterali sul lato destro, quindi restituire nuovamente l'argomento. L'utilizzo più comune è con 1!:2, possibilmente con formattazione indesiderata.
  • L'I / O fa schifo. Accelera il processo creando loop da tutto ciò che puoi. 1!:1ha rango 0, ed entrambi 1!:2 3hanno rango _ 0, ad esempio, quindi utilizzalo creando array di 1s e 1!:1passandoci sopra. Nota che ".ha anche il rango 1, quindi di solito puoi metterlo direttamente anche dopo 1!:1, e non devi collegarlo tramite @o rango shenanigans.

  • Non è facile trovare posti dove metterlo, ma ::può essere utile.

    • ::]^:_è una combinazione particolarmente potente, ad esempio, che ti consente di fare qualcosa di pericoloso fino a quando non puoi più farlo. (Fatte salve le solite ^:_avvertenze "come-a-loop".)

    • Questo ti consente anche di utilizzare gli {elenchi che non hanno l'indice desiderato, poiché genera un errore di dominio quando ciò accade. Utile, ad esempio, per prendere la testa di un elenco solo se esiste (provare a utilizzare ::]per restituire l'elenco vuoto o ::_1:per restituire un codice di errore e così via).

  • ]`($:@u)@.vdi solito può essere ridotto rispetto a u^:v^:_, specialmente sulle definizioni di ue con vcui si può giocare. Un caso simile vale per il u^:(1-v)vs. condizionale ]`u@.v. Considera le tue opzioni, specialmente quando hai molti verbi nominati che fluttuano. È anche un po 'più flessibile, ma ricorda, se lo usi $:, c'è una profondità di ricorsione su cui è facile imbattersi. (Di solito qualcosa come 1800 iterazioni?)


Il trucco negativo è davvero fantastico.
FUZxxl,

"salva alcuni personaggi disperati" Quando studi illuminato, tutto sembra un epiteto trasferito! :)
Soham Chowdhury

1
"usando %&2salva un personaggio, duh." E ne -:salva un altro!
Lynn,

11

La cosa più importante quando si gioca a golf in J è non solo capire il problema, ma ridurlo a una serie di trasformazioni di array. Devi capire questo modo di pensare per avere successo con il codice J.

Ad esempio, una recente sfida ha chiesto di risolvere il più grande problema di subarray . L'algoritmo di borsa per risolvere questo problema è che l'algoritmo di Kadane ha la seguente descrizione informale:

Passare attraverso l'array e in ciascuna posizione e trovare la somma del sottoarray più grande che termina qui, che è il massimo di 0 o il valore nell'indice corrente più la somma del sottoarray più grande che termina nella posizione precedente. Calcola il massimo di questi sottoparametri man mano che vai a trovare il sottoarray più grande dell'intero array.

Una traduzione in codice imperativo è semplice:

  1. lascia che A sia l'array di input.
  2. hmi ← 0.
  3. se i ≥ len (A) ritorna m .
  4. h ← max (0, h + A [ i ]).
  5. m ← max ( m , h ).
  6. ii + 1.
  7. vai a 3.

Questi algoritmi sembrano complicati per J a prima vista in quanto esiste un ciclo esplicito che all'inizio non sembra una riduzione. Se ti rendi conto di cosa sta facendo l'algoritmo, puoi districare i singoli passaggi e vedere che esegue effettivamente due semplici operazioni di array:

  1. Scansione attraverso l'array per calcolare le lunghezze dei sottoarray più grandi che terminano in ciascun indice.
  2. Ridurre queste lunghezze con la funzione max per trovare il massimo.

Ora questi due passaggi sono molto facili da implementare in J. Ecco una traduzione:

  1. (0 >. +)/\. y , 0- Questo passaggio opera dall'altra estremità dell'array per adattarsi meglio al paradigma di J. 0 >. +è tacito per 0 >. x + y.
  2. >./ y

Complessivamente, otteniamo un'implementazione molto concisa dell'algoritmo:

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

Se impari questo modo di affrontare l'implementazione degli algoritmi, le tue soluzioni saranno tanto concise quanto questo codice.

Ecco alcuni trucchi che ho accumulato nel tempo. Questo elenco verrà ampliato man mano che acquisirò maggiori conoscenze nel golf di J.

  • Impara il dizionario. Contiene molti verbi davvero oscuri che non hanno alcun senso fino a quando non vedi quanto siano utili. Ad esempio, il monadic =è strano all'inizio, ma è molto utile nelle sfide dell'arte ASCII.
  • Usa diadico &in contesti taciti quando vuoi una congiunzione di potere. Il vocabolario suggerisceu@[&0 come tacito sostituto di4 : 'u^:x y e anche io.
  • In molti casi puoi evitare una [:o @:in una sequenza come u@vscegliendo una variante uche ha un argomento sinistro. Ad esempio, per eliminare il primo elemento del risultato di v, utilizzare 1}.vinvece di [:}.vif se }.@vnon è possibile per qualche motivo.
  • ] vè spesso più breve di v@]se si desidera utilizzare la monade vin un contesto diadico. Ciò è utile soprattutto quando vè un lungo treno di verbi.
  • A volte puoi scrivere m (n v w) yinvece di (n v m&w) y. Ciò può consentire di evitare spazi e parentesi.
  • #\invece di >:@i.@#.
  • u &. vè utile quando vha un dritto. Altrimenti, potresti voler usare [: vinv u & vou & (v :. vinv) invece.
  • Comprendi il grado e come usarlo. Prova a giocherellare con la congiunzione di rango fino a ottenere qualcosa che si adatta. Aiuta a capire come il grado influenza il tuo codice.
  • ^:_ è estremamente utile per gli algoritmi in cui si desidera raggiungere la convergenza, come inondazioni o simulazioni.
  • Conosci la tua libreria standard. Contiene funzioni molto utili che ti fanno risparmiare tonnellate di personaggi.
  • Il copulæ =.e =:può essere incorporato ovunque in una frase. Usa questo per creare una riga in cui la notazione tacita non è sufficiente.
  • Utilizzare ,riduzioni monadiche anziché multiple per ridurre le matrici multidimensionali.
  • Comprendi quali frasi sono supportate da un codice speciale quando la sfida impone limiti di runtime. Alcune cose utili operano in O ( n ) invece di O ( n 2 ) in modo contro intuitivo.
  • Le scatole sono utili per gli alberi.

J è abbastanza intelligente da eseguire la tua soluzione di subarray max in O (n) memorizzando nella cache i calcoli riutilizzati, o farà la cosa semplice ed eseguirà in O (n ^ 2)?
Giona il

@Jonah I think it runs in quadratic time.
FUZxxl

10

Be wary of using loops.

While J has looping structures (for. do. end., while. do. end. and variations), if you find yourself using them there's a possibility that your algorithm is not playing to J's golfing strengths and that there are character savings to be made.

^: the power conjunction is your friend. To execute a verb x times:

verb^:x

If you need the result of each iteration in a list:

verb^:(i.x)

You can also use ^: to execute a verb conditionally:

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

Double +: if ^: the item is greater than 3 3<] (the "0 changes the rank of the verb so it works an item at a time).


Boxed values act like the (i.x) example, i.e. f^:(<x) is equivalent to f^:(i.x).
FireFly

9

Input

1!:1[1 will take one line of input terminated by pressing the enter key.

1!:1[3 will take a number of lines of input (terminated by Ctrl-D on my Mac, Ctrl-C on Windows).

If you're trying to input numbers, using ". will evaluate the string and return a list of numbers ready to be manipulated. If you're taking in one number but need to operate on the digits individually, ".,. (thanks to Jan Dvorak's comment for this) or "."0 will split the string into separate digits:

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

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

If you're reading in strings, the shortest way to get a boxed list of separate strings is to use ;:. This works best for space separated strings:

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

Out of curiosity, (I've only played with J a little) what would 1!:1[2 work out as (if anything)?
Gaffi

From what I can gather on the 1!: page (I'm no J expert) 2 is the screen, so input from screen doesn't make much sense.
Gareth

Thanks for the link. From there, it actually looks like 2 is not valid? I don't have my J computer on me to try it out at the moment. Where I see 2, just below the notes about 1!:1, it is for 1!:2.
Gaffi

@Gaffi The file numbers for input and output seem to be sequential, so I'm guessing that those are fixed and that 2, 4 and 5 only appear under output because it doesn't make any sense to try and input from them. Same goes the other way around for 1 and 3.
Gareth

Since ". is rank 1-x-x and ,. always produces a 2D array, ".,' ',. (stitch with space, ravel and evaluate; 8 characters) can be replaced with just ".,. (ravel items and evaluate; 4 characters).
John Dvorak

6

Using iteration to compute sequences

Typically, solving an OEIS sequence challenge will require using one of the formulas given on its page. Some of these adapt well for J, and others not so much. Recursive formulas are straight-forward, however, iteration might not be simple. A pattern I've begun to use is

(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

where s is the first value in the sequence, f is a verb that will compute the next term given the previous term, and n is the zero-based index of the term you want to compute. This method relies on the fact that when computing the power of a dyad, the LHS is bound to the dyad to form a new monad, and that monad is nested on the initial value. The dyad given to the power adverb is a hook where (]f) is given the index n on the LHS and the value of a term in the sequence s. The hook will apply f on s as a monad, and then ignore n to return the result of f s.

Standard library

Sometimes, you might find that J will have support for a verb in its standard library. For example, most of the bitwise integer operations are bound to names which are shorter than using the primitive call.

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

Date and time builtins are also available.

Ranges

If you have a set of values [a, b, c] and you want to form a range based on their product like [0, 1, 2, ..., a*b*c-1], the typical approach would be to find their product and then form a range which might be [:i.*/ which costs 6 bytes. A shorter way is ,@i. for 4 bytes since i. can form multidimensional arrays while still counting up, and flattening it will produce an equivalent range.

Printing continuously

A tacit way to print a value and continue to use it without an explicit loop is ([echo) for a monadic case. echo is a verb in the standard library that prints its contents to stdout in the same format used in the interpreter. The hook then passes the same input value out using the left [ verb.

Base 10 digits of an integer

The standard way of acquiring the base 10 digits of an integer is 10#.inv] which costs 8 bytes, too much! An alternative is to convert it to a string and parse it at rank 0 "."0@": which saves a byte, but an even better way is ,.&.": which saves another byte making the final cost 6 bytes instead of 8.


5

Consider using explicit definition instead of writing a tacit verb; sure the 3 :' and ' cost 5 bytes, but you can save a lot of @, @: and [: that way.


5

Some (fairly) common tricks I've seen

I'm sharing a few things that have come in handy for me. Basically all of these are tips I've received myself, but I don't have credits for most.

Sum of a rank one array

Instead of using +/@:(FGH) use (1#.FGH). This means debase to base 1, which effectively means summing an array. Although it's longer than +/, it doesn't require a cap or composition, which often makes it much shorter than using +/.

Counting trailing truths

If you have a boolean list and you want to count the number of trailing truths, use #.~. See here. The APL answer provides a good explanation for how this works. Granted, this has only been helpful to me twice but I figured I'd share it anyways.

Under (&.)

Not a specific trick, but just a general suggestion: the adverb &.-under often leads to elegant and (more importantly) short solutions. Keep it in mind when you're golfing.

Often times it's useful for and other base conversion challenges, e.g. this code which removes the most significant bit from a number: }.&.#: (convert to list of binary digits, remove the first digit, then undo the conversion to a list of binary digits and convert back to decimal). The straightforward solution is two more bytes: #.@}.@#:.

Under is also helpful for challenges where you need to work with decimal digits, since you can use u&.":. For example, the short way miles gives to split to decimal digits uses under: ,.&.":.

A final example is finding the magnitude of a vector: +/&.:*:, note that you need to collect all of the results from *:-square with &.:-under since *:-square is rank zero.


4

Shorter ways to mess with ranks

Sometimes, you'll have code like <"0 i.3 3, where you want to apply a verb v at rank r. However, if you use a noun (like 0), you'll often have to include a space. To avoid this, you can use another verb u of equivalent rank and use u"v instead. For example, since + has rank 0 0 0, we can use <"+ instead of <"0.

Here is a table of all verbs and their ranks (obtainable by using 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:

To use this table, find the desired rank r on the left hand side, then choose an appropriate verb v from the right hand side. E.g., if I need to vectorize a verb v at depth 2 _ 2, then I find that rank on the left and choose %. from the right. Then I use v"%. instead of v"2 _ 2.


3

strings library: golfing tips

The strings library is vastly helpful for doing anything with string manipulation. Sure, it takes include'strings' (which is very costly, considering J), but you may at times reap the benefits.

stringreplace

Find yourself using string replace? Observe that A stringreplace B is the same as B rplc A.

In fact, this is how rplc is implemented:

   rplc
 stringreplace~

cuts

The verb cuts provides thus:

cut y at x (conjunction)
string (verb cuts n) text
  n=_1  up to but not including string
  n= 1  up to and including string
  n=_2  after but not including string
  n= 2  after and including string

So it's really slicing a string.


3

Getting numbers from 0 to 4

If there’s a restriction on using numbers in your code:

0 %_: one divided by infinity.
1 #_: how many infinities?
2 #_ _: two infinities.
3 verb: there’s a built-in.
4 dyad: another built-in.

Getting numbers from 10 to 35

Base-inifinity literals: 11:_bb, 26:_bq etc.


3

Tacit programming

Basics

Dyadic verb

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

Monadic verb

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

Misc

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

Tricks

(F x) G (H y)

Tacit solution: (G~F)~H; depending on the actual verbs, consider rearranging the left and right arguments to remove ~.

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)

Monadic-Dyadic replacements

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

1
(G~F)~H is pure bubbly goodness!
Jonah

2

& is your friend, use it wisely

v is a verb, n is a noun, x and y are left and right arguments, respectively.

Monad &: Introduce ~ inside adverb/conjunction chain

An adverb/conjunction chain evaluates from the left. So something like _2&+/\&.> won't work because it parses like (_2&+)/\&.> while we want _2&(+/\)&.>. In this case, swapping the left/right of +/\ can save a byte, as in +/\~&_2&.> because this one parses as ((+/\)~)&_2&.>. To see why this works:

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

Dyad &: Repeat x times

Did you know that if you give a left argument x to &, the function applies it x times to y? Quite a few challenges ask you to do certain operation x times. It is mainly achievable in two ways:

  • Use the power operator ^: without right operand

If the operation is v, then v^: becomes an adverb train that, when given a left operand, becomes a monadic verb. So v is applied to y, x times.

x(v^:)y
is equivalent to
(v^:x)y
  • Use the dyadic & as the outermost conjunction

To use this, you need to identify a constant n and a dyadic verb u, so that either n u y or y u n is equivalent to v. Then you can write n&u or u&n to solve the entire task. This form is most effective when the choice of the constant is obvious, e.g. 3 in 3 u: (convert chars to ASCII values).

Also, u&n is slightly preferred over n&u when the outermost structure of u is a conjunction or adverb (in which case n&u should be n&(u); you can do u~&n instead).

Note that you can place the dyadic & anywhere in a train to achieve repeating arbitrary function to arbitrary argument, in the similar sense to dynamic ^:.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.