GolfScript zbyt często zdobywa swoją własną drogę i uważam, że repozytorium przydatnych wskazówek dotyczących gry w golfa w J może pomóc w walce ze złym imperium. Jakie masz wskazówki na temat skrócenia tego i tak krótkiego języka?
Dla tych, którzy chcą nauczyć się J, oczywistym miejscem do rozpoczęcia jest strona jsoftware, a zwłaszcza słownictwo , nauka J przewodnik i J dla programistów C przewodnikiem.
GolfScript gets its own way far too often
w czytaniu w 2019 roku.Odpowiedzi:
Istnieje kilka subtelności polegających na wyciskaniu kilku ostatnich znaków w J. Dla tego, załóżmy, że każda wielka litera jest prymitywnym czasownikiem (tj. Usuwam spacje, które w innym przypadku byłyby wymagane do rozgraniczenia nazw).
Gdy masz pociąg dzieje, i trzeba zastosować funkcję szczycie innego trakcie kopiowania,
([:FLGR)
i(LF@:GR)
mają taką samą liczbę znaków, ale(LF@GR)
ratuje jedno. Jeśli ramka G jest większa lub równa rangi monad F, jest to poprawna transformacja. W szczególności wszystkie pociągi mają nieskończoną rangę, podobnie jak, ,. ,: ~. /: \: [ ]
większość zastosowań#
i|.
.Jeśli musisz wybrać ciągi z listy, a ciągi te nie mają spacji, użyj
>i{ab`cd`ef
. Jest brudny, ale zapisuje znaki dla każdego nowego ciągu, z którym masz do czynienia, chyba że ciągniesz tylko pojedyncze znaki, a nawet wtedy lista znaków musi mieć długość 4, aby być krótsza. To, co się dzieje, jest takie, że niezdefiniowane nazwy są traktowane jako odniesienia do czasowników, a kiedy weźmiesz gerunds tych czasowników, otrzymujesz zapakowany ciąg nazwy. Nazwy, które są już zdefiniowane jako mające typ rzeczownik, przysłówek lub koniunkcja, nie mogą być używane w ten sposób, ponieważ te nazwy są rozpoznawane wcześniej, zanim będą`
mogły na nich zostać.Jeśli masz szczęście mieć wyrażenie do pracy, a nie tylko czasownik milczący, prawie zawsze warto przypisać dowolne bity, których ponownie używasz, do zmiennych, niezależnie od tego, czy są to rzeczowniki, czasowniki czy przysłówki. Pareny czasami się zwrócą, dopasowując się do miejsca, w którym wcześniej były spacje, a większość takich definicji jest tego warta, jeśli zostaną ponownie użyte jeszcze raz.
Koniunkcje jak
(FGH)^:(u`v`w)
można przepisaću`v`w(FGH^:)
. Działa to dla dowolnej długości pociągu, nawet 1, chociaż zapisujesz wszystko tylko wtedy, gdy ta sztuczka usuwa pareny z właściwego argumentu. Ta sztuczka działa tylko wtedy, gdy wstępnie załadujesz lewy operand. (Nie masz pojęcia, co się właśnie stało? Sprawdź „przysłówki ukryte” i przestudiuj sekcję Przetwarzanie i wykonywanie w słowniku J).Nie używaj
a.&i.
, używaju:
!{&a.
i3&u:
są równoważne pod względem długości, a te pierwsze mogą być bardziej przydatne w koniunkcji (w zależności od koniunkcji).Rzeczy takie jak
(2%~F)
i(F%2:)
mają równoważną długość. Jest to przydatne, ponieważ czasami, w zależności od tego, jak wygląda reszta twojego pociągu, możesz go zrestrukturyzować za pomocą@
sztuczek, jak napisano w pierwszym punkcie, aby uratować desperackie postacie. (I oczywiście, jeśliF
jest,]
a pociąg to monada, użycie%&2
ratuje char, duh.)Pociągi przypominające haczyki z czasownikiem
]
lub[
z najbardziej na lewo, np(]FGH)
.]
pozwala rozbić dynamiczną aplikację i użyć tylko właściwego argumentu. (Zamień w lewo za pomocą(]FGH)~
, za co najmniej 1 postacią, może więcej.) Oszczędza char(FGH)@]
i jest bardzo przydatny w gerundach![
w haku zastosowanym monadycznie pozwala zrobić coś dla efektów ubocznych po prawej stronie, a następnie zwrócić argument ponownie. Najczęstszym zastosowaniem jest1!:2
, prawdopodobnie z formatowaniem śmieci.I / O jest do bani. Przyspiesz ten proces, tworząc pętle ze wszystkiego, co możesz.
1!:1
ma rangę0
i oba1!:2 3
mają_ 0
na przykład rangę , więc wykorzystaj to, tworząc tablice 1s i przejeżdżając1!:1
bezpośrednio nad nimi. Zauważ, że".
ma również rangę 1, więc zazwyczaj możesz po prostu umieścić ją bezpośrednio po niej1!:1
i nie musisz dołączać jej za pośrednictwem@
ani rangi szantażystów.Znalezienie miejsca na umieszczenie tego nie jest łatwe, ale
::
może być przydatne.::]^:_
jest szczególnie potężną kombinacją, na przykład, która pozwala ci zrobić coś niebezpiecznego, dopóki nie będziesz już w stanie tego zrobić. (Z zastrzeżeniem zwykłych^:_
ostrzeżeń -as-a-loop.)Pozwala to również korzystać
{
z list, które nie mają pożądanego indeksu, ponieważ powoduje to błąd domeny, gdy to nastąpi. Przydatne jest np. Wzięcie nagłówka listy tylko wtedy, gdy istnieje (spróbuj użyć,::]
aby zwrócić pustą listę lub::_1:
kod błędu itd.).]`($:@u)@.v
zwykle może być krótszy niżu^:v^:_
, szczególnie w definicjach,u
iv
można się nim bawić. Podobny przypadek zachodzi dla warunkowego podobnegou^:(1-v)
vs.]`[email protected]
. Rozważ swoje opcje, szczególnie gdy masz dużo nazwanych czasowników. Jest również trochę bardziej elastyczny, ale pamiętaj, że jeśli używasz$:
, istnieje głębokość rekurencji, z którą łatwo się zderzyć. (Zwykle coś w rodzaju 1800 iteracji?)źródło
%&2
saves a char, duh." And-:
saves another!The most important thing when golfing in J is to not only understand the problem, but to reduce the problem to a series of array transformations. You need to understand this way of thinking to have any success with J code.
For example, a recent challenge asked to solve the largest subarray problem. The stock algorithm to solve this problem is Kadane's algorithm has the following informal description:
A translation into imperative code is straightforward:
This algorithms seems complicated for J at a glance as there is an explicit loop that doesn't look like a reduction at first. If you realize what the algorithm is doing you can untangle the individual steps and see that it actually performs two simple array operations:
Now these two steps are very easy to implement in J. Here is a translation:
(0 >. +)/\. y , 0
– This step operates from the other end of the array to better fit J's paradigm.0 >. +
is tacit for0 >. x + y
.>./ y
Put together, we get a very terse implementation of the algorithm:
If you learn this way of approaching the implementation of algorithms, your solutions will be as terse as this code.
Here are some tricks I accumulated over time. This list will be expanded as I get more knowledge in J golfing.
=
is strange at first but is very useful in ASCII art challenges.&
in tacit contexts when you want a power conjunction. The vocabulary suggestsu@[&0
as a tacit replacement to4 : 'u^:x y
and so do I.[:
or@:
in a sequence likeu@v
by choosing a variant ofu
that has a left argument. For instance, to drop the first item of the result ofv
, use1}.v
instead of[:}.v
if}.@v
isn't possible for some reason.] v
is often shorter thanv@]
if you want to use monadicv
in a dyadic context. This comes in useful especially whenv
is a long train of verbs.m (n v w) y
instead of(n v m&w) y
. This may make it possible to avoid spaces and parentheses.#\
instead of>:@i.@#
.u &. v
is useful whenv
has an obverse. When not, you might want to use[: vinv u & v
oru & (v :. vinv)
instead.^:_
is extremely useful for algorithms where you want to reach convergence, like a flood-fill or simulations.=.
and=:
can be embedded anywhere in a phrase. Use this to make one-liners where tacit notation isn't enough.,
instead of multiple reductions when reducing multi-dimensional arrays.źródło
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 verbx
times:If you need the result of each iteration in a list:
You can also use
^:
to execute a verb conditionally:Double
+:
if^:
the item is greater than 33<]
(the"0
changes the rank of the verb so it works an item at a time).źródło
(i.x)
example, i.e.f^:(<x)
is equivalent tof^:(i.x)
.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: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:źródło
1!:1[2
work out as (if anything)?1!:
page (I'm no J expert) 2 is the screen, so input from screen doesn't make much sense.2
is not valid? I don't have my J computer on me to try it out at the moment. Where I see2
, just below the notes about1!:1
, it is for1!:2
.".
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).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
where
s
is the first value in the sequence,f
is a verb that will compute the next term given the previous term, andn
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 indexn
on the LHS and the value of a term in the sequences
. The hook will applyf
ons
as a monad, and then ignoren
to return the result off 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.
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 sincei.
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 tostdout
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.źródło
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.źródło
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 binary 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.źródło
Shorter ways to mess with ranks
Sometimes, you'll have code like
<"0 i.3 3
, where you want to apply a verbv
at rankr
. However, if you use a noun (like0
), you'll often have to include a space. To avoid this, you can use another verbu
of equivalent rank and useu"v
instead. For example, since+
has rank0 0 0
, we can use<"+
instead of<"0
.Here is a table of all verbs and their ranks (obtainable by using
v b. 0
):To use this table, find the desired rank
r
on the left hand side, then choose an appropriate verbv
from the right hand side. E.g., if I need to vectorize a verbv
at depth2 _ 2
, then I find that rank on the left and choose%.
from the right. Then I usev"%.
instead ofv"2 _ 2
.źródło
strings
library: golfing tipsThe 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 asB rplc A
.In fact, this is how
rplc
is implemented:cuts
The verb
cuts
provides thus:So it's really slicing a string.
źródło
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.źródło
Tacit programming
Basics
Dyadic verb
Monadic verb
Misc
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~
.Monadic-Dyadic replacements
źródło
(G~F)~H
is pure bubbly goodness!&
is your friend, use it wiselyv
is a verb,n
is a noun,x
andy
are left and right arguments, respectively.Monad
&
: Introduce~
inside adverb/conjunction chainAn 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:Dyad
&
: Repeatx
timesDid you know that if you give a left argument
x
to&
, the function applies itx
times toy
? Quite a few challenges ask you to do certain operationx
times. It is mainly achievable in two ways:^:
without right operandIf the operation is
v
, thenv^:
becomes an adverb train that, when given a left operand, becomes a monadic verb. Sov
is applied toy
,x
times.&
as the outermost conjunctionTo use this, you need to identify a constant
n
and a dyadic verbu
, so that eithern u y
ory u n
is equivalent tov
. Then you can writen&u
oru&n
to solve the entire task. This form is most effective when the choice of the constant is obvious, e.g. 3 in3 u:
(convert chars to ASCII values).Also,
u&n
is slightly preferred overn&u
when the outermost structure ofu
is a conjunction or adverb (in which casen&u
should ben&(u)
; you can dou~&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^:
.źródło