Wskazówki dotyczące gry w golfa w Julii

20

Jakie masz ogólne wskazówki na temat gry w golfa w Julii? Szukam pomysłów, które można by zastosować do problemów z golfem w kodzie, które są przynajmniej nieco specyficzne dla Julii (np. „Usuń komentarze” nie jest odpowiedzią).

Jonathan Van Matre
źródło

Odpowiedzi:

19

UWAGA: poniżej może zawierać kilka przestarzałych wskazówek, ponieważ Julia nie jest jeszcze całkowicie ustabilizowana pod względem struktury.

Kilka sztuczek, aby uratować kilka postaci

  1. Przeciąż operatory często używanymi funkcjami binarnymi . Na przykład, jeśli musisz wykonać wiele podziałów całkowitych i nie potrzebujesz podziału odwrotnego, użyj \ =div, a następnie możesz wpisać a\bzamiast div(a,b). Zwróć uwagę na spację - jest to konieczne, aby uniknąć parsowania jako operatora „\ =”. Należy również pamiętać, że w przypadku przeciążenia na poziomie monitu REPL użyj (\)=Base.(\)lub, \ =Base. \aby go zresetować. UWAGA: niektóre funkcje mają wstępnie zdefiniowane istniejące operatory UTF-8, takie jak ÷dla div, jak zauważył Alex A.
  2. Użyj ^ z łańcuchami, aby uzyskać wyjście warunkowe . To znaczy, zamiast a>0?"Hi":""używać "Hi"^(a>0)do zapisania jednego bajtu, lub dla wartości logicznej a, "Hi"^ado zapisania trzech bajtów.
  3. (czasami) Przechowuj małe wektory o stałej wielkości jako osobne zmienne . Na przykład, zamiast a=split("Hi there"," ")być w stanie uniknąć a[1]i a[2]za pomocą a,b=split("Hi there"," "), które można określić jako ai b, oszczędzając trzy bajty dla każdego użycia, kosztem tylko dwóch dodatkowych znaków przy przypisaniu. Oczywiście nie rób tego, jeśli możesz pracować z operacjami wektorowymi.
  4. Uzyskaj dostęp do pierwszego elementu tablicy za pomocą[] - w przypadku tablic wyrażenie A[]jest równoważne z A[1]. Zauważ, że nie działa to dla Ciągów, jeśli chcesz zdobyć pierwszą postać, lub dla Tuples.
  5. Nie używaj isempty dla tablic, krotek lub ciągów - zamiast tego używaj ==[]dla tablic i ==()krotek; podobnie, dla negatywu, użyj !=[]i !=(). W przypadku łańcuchów używaj ==""dla pustych, ale używaj >""dla niepustych, ponieważ „” jest leksykograficznie przed każdym innym ciągiem.
  6. Użyć właściwego operatora zwarcia logicznego zamiast „if” . Może to być nieco mniej specyficzne dla Julii, ale warto o tym pamiętać. x<=1&&"Hi"można zapisać jako x>1||"Hi", zapisując znak (o ile powrót wartości logicznej nie jest ważny).
  7. Nie używaj zawiera, aby sprawdzić obecność znaku w ciągu - jeśli jesteś ograniczony do podstawowego ASCII, użyj in('^',s)raczej niż contains(s,"^"). Jeśli możesz użyć innych znaków, możesz zaoszczędzić nieco więcej '^'∈s, ale pamiętaj, że w UTF-8 jest to 3 bajty.
  8. Szukasz minimalnych / maksymalnych wartości w tablicy? Nie używaj minimum lub maksimum - zamiast używać minimum(x)lub maximum(x), użyj min(x...)lub max(x...), aby zgolić jeden znak z twojego kodu, jeśli wiesz, że xbędą miały co najmniej dwa elementy. Alternatywnie, jeśli wiesz, że wszystkie elementy xbędą nieujemne, użyj minabs(x)lubmaxabs(x)
  9. Tam, gdzie jest to możliwe i dozwolone przez wyzwanie, użyj rzeczywistego nowego wiersza zamiast \ n - pamiętaj, że to utrudni czytanie kodu i może oznaczać, że musisz dostarczyć wersję „bez golfa”, aby ludzie mogli zrozumieć to.
  10. Umieść opcje po ciągu wyrażenia regularnego - jeśli chcesz mieć ciąg wyrażenia regularnego w trybie wielowierszowym, na przykład nie pisz r"(?m)match^ this", pisz r"match^ this"m, zachowując trzy znaki.
  11. Odwróć tablice 1-D za pomocą flipud - reverse(x)jest o jeden bajt dłuższy flipud(x)i wykona tę samą operację, więc ta druga jest lepsza.
  12. Tam, gdzie to możliwe, używaj konkatenacji tablic zamiast push !, unshift !, append !, lub prepend!- w przypadku zwykłych tablic można to łatwo zrobić. W przypadku tablic typu Any z elementami tablicy potrzebne będą nawiasy klamrowe wokół dodanych tablic (to znaczy {[1,2]}nie {1,2}) - w przypadku Julii 0.4 byłoby to potrzebne Any[[1,2]].
  13. Użyj indeksowania tablic, aby uzyskać rozmiar tablicy lub łańcucha - gdy używasz endindeksowania tablic, automatycznie jest konwertowany na długość tablicy / łańcucha. Zamiast tego k=length(A)użyj, A[k=end]aby zapisać 3 znaki. Pamiętaj, że może to nie być korzystne, jeśli chcesz natychmiast użyć k. Działa to również w przypadku wielowymiarowym -A[k=end,l=end] otrzyma rozmiar każdego wymiaru A- jednak (k,l)=size(A)w tym przypadku jest krótszy o jeden bajt, więc używaj go tylko wtedy, gdy chcesz natychmiast uzyskać dostęp do ostatniego elementu w tym samym czasie.
  14. Uzyskaj iterator indeksu za pomocą indeksowania tablic - podobnie jak w przypadku 13, możesz również uzyskać iterator pasujący do długości tablicy za pomocą A[k=1:end], w którym to przypadku kzachowa iterator pasujący1:length(A) . Może to być przydatne, gdy chcesz jednocześnie korzystać z tablicy A.
  15. Nie używaj metody kolekcjonowania do konwersji łańcucha znaków na tablicę znaków - zamiast collect(A), użyj[A...] , który zrobi to samo i pozwoli zaoszczędzić 4 bajty.
  16. Potrzebujesz liczby przekonwertowanej na ciąg? Użyj "$(s[i])"lubdec(s[i]) do wyrażeń lub zmiennych wieloznakowych oraz "$i"do zmiennych jednoznakowych.
  17. Użyj ?:zamiast &&lub ||do przypisania warunkowego - to znaczy, jeśli chcesz wykonać przypisanie tylko pod pewnymi warunkami, możesz zapisać jeden bajt, pisząc cond?A=B:1zamiast cond&&(A=B), cond?1:A=Ba niecond||(A=B) . Zauważ, że 1tutaj jest to wartość pozorna.
  18. Użyj unionlub zamiastunique - union(s)zrobi to samo co unique(s)i zapisze bajt w tym procesie. Jeśli możesz użyć znaków spoza ASCII, ∪(s)zrobisz to samo i kosztuje tylko 3 bajty zamiast 5 bajtów w union.
Glen O
źródło
2
Och, jak chciałbym tę pierwszą sztuczkę w Pythonie.
patrz
Możesz podzielić na spacje, używając po prostu, split("Hi there")ponieważ argument wzorca domyślnie jest spacją.
Alex A.,
@AlexA. - Wiem, ale to nie jest cel napiwku, a napiwek stosuje się równie dobrze w obu kierunkach.
Glen O
Punkt 12 zmienił się w 0,5.
Lyndon White
@ Oxinabox - nie jestem zaskoczony, jestem pewien, że niektóre z nich są już nieaktualne. Myślę, że pierwotnie napisałem większość wskazówek dotyczących 0.3.
Glen O
15

Przedefiniuj operatory, aby zdefiniować funkcje

Przedefiniowanie operatorów pozwala zaoszczędzić wiele bajtów w nawiasach i przecinkach.

Rekurencyjne operatory jednoargumentowe

Aby uzyskać jedyny przykład, porównaj następujące rekurencyjne implementacje sekwencji Fibonacciego:

F(n)=n>1?F(n-1)+F(n-2):n # 24 bytes
!n=n>1?!~-n+!(n-2):n     # 20 bytes
!n=n>1?!~-n+!~-~-n:n     # 20 bytes

Wypróbuj online!

Przedefiniowany operator zachowuje swój pierwotny priorytet.

Zauważ, że nie mogliśmy po prostu zamienić się !na ~, ponieważ ~jest już zdefiniowany dla liczb całkowitych, podczas gdy !jest zdefiniowany tylko dla boolean.

Operatory binarne

Nawet bez rekurencji ponowne zdefiniowanie operatora jest krótsze niż zdefiniowanie funkcji binarnej. Porównaj następujące definicje prostego testu podzielności.

f(x,y)=x==0?y==0:y%x==0 # 23 bytes
(x,y)->x==0?y==0:y%x==0 # 23 bytes
x->y->x==0?y==0:y%x==0  # 22 bytes
x\y=x==0?y==0:y%x==0    # 20 bytes

Wypróbuj online!

Rekurencyjne operatory binarne

Poniżej przedstawiono sposób przedefiniowania operatora binarnego w celu obliczenia funkcji Ackermann:

A(m,n)=m>0?A(m-1,n<1||A(m,n-1)):n+1    # 35 bytes
^ =(m,n)->m>0?(m-1)^(n<1||m^~-n):n+1   # 36 bytes
| =(m,n)->m>0?m-1|(n<1||m|~-n):n+1     # 34 bytes
m\n=m>0?~-m\(n<1||m\~-n):n+1           # 28 bytes

Wypróbuj online!

Pamiętaj, że ^jest to nawet dłużej niż zwykły identyfikator, ponieważ jego priorytet jest zbyt wysoki.

Jak wspomniano wcześniej

m|n=m>0?m-1|(n<1||m|~-n):n+1           # 28 bytes

nie działałby dla argumentów liczb całkowitych, ponieważ |w tym przypadku jest już zdefiniowany. Definicję liczb całkowitych można zmienić za pomocą

m::Int|n::Int=m>0?m-1|(n<1||m|~-n):n+1 # 38 bytes

ale to zbyt długo. Jednak to robi pracę, jeśli mijamy pływaka jako argumentu lewej i prawej całkowitą jako argumentu.

Dennis
źródło
11
  1. Nie daj się łatwo uwieść czynnikowi (n) Kusząca funkcja biblioteki podstawowej factor(n)ma fatalną wadę: zwraca faktoryzację liczby całkowitej w nieuporządkowanym Dicttypie. W związku z tym wymaga kosztownych collect(keys())i collect(values())potencjalnie także a cati a, sortaby uzyskać potrzebne dane. W wielu przypadkach taniej jest po prostu wziąć pod uwagę podział prób. Smutne ale prawdziwe.

  2. Użyj mapy map to świetna alternatywa dla zapętlania. Zdawać sobie sprawę z różnicy pomiędzy mapa map!i wykorzystać funkcjonalność ta ostatnia, kiedy można w miejscu.

  3. Zastosowanie mapreduce mapreduce jeszcze bardziej rozszerza funkcjonalność mapy i może być znaczącym oszczędzaniem bajtów.

  4. Anonimowe funkcje są świetne! .. szczególnie, gdy przejdziemy do wyżej wymienionych mapfunkcji.

  5. Funkcje skumulowanej tablicy cumprod , cumsumaromatyczne cummini inne podobnie nazwane funkcje umożliwiają kumulatywne operacje wzdłuż określonego wymiaru n-wymiarowej tablicy. (Lub * un * określono, jeśli tablica ma wartość 1-d)

  6. Krótka notacja dla Any Jeśli chcesz wybrać cały konkretny wymiar tablicy wielowymiarowej (lub Dict), np. A[Any,2]Możesz zapisać bajty za pomocąA[:,2]

  7. Użyj funkcji jednowierszowej dla funkcji Zamiast tego function f(x) begin ... endczęsto możesz uprościćf(x)=(...)

  8. Użyj operatora trójskładnikowego Może to być oszczędność miejsca dla konstrukcji z wyrażeniem pojedynczym wyrażenia If-Then-Else. Ostrzeżenia: Chociaż w niektórych innych językach jest to możliwe, nie można pominąć części po dwukropku w Julii. Ponadto operator ma poziom wyrażeń w Julii, więc nie można go używać do warunkowego wykonywania całych bloków kodu.
    if x<10 then true else false endvs
    x<10?true:false

Jonathan Van Matre
źródło
3
Jak u licha „korzystanie z operatora trójskładnikowego” jest specyficzne dla Julii? Jest odpowiedni dla każdego języka, który go posiada.
Peter Taylor
5
To ważne, że to ma. Wiele języków ma również funkcje map, anonimowe lub czysto, rodzaj skrótu dla dowolnej / wszystkich, funkcje skumulowane itp. Gdybyśmy zredukowali każdy wątek do wskazówek tylko do funkcji absolutnie unikalnych dla tego języka, bardzo niewiele byłoby wskazówek .
Jonathan Van Matre
3
Rany, tylko wszystkie funkcjonalne na początek, gdzie wszystko zwraca wartość, więc trójka operacyjna byłaby zbędna. Jeśli musisz mieć konkretne przykłady: Go, Haskell, Scala, Lua, VB, Prolog, PL / SQL ... nawet Python nie dla wielu wersji. Spośród kilkunastu języków, których wątki w poradach wspominają o operatorze trójskładnikowym, czy jest jakiś powód, dla którego zdecydowałeś się przyjechać tylko moim prowincjonalnym?
Jonathan Van Matre
3
Hej, przynajmniej jesteś curmudgeonem o równych szansach. ヘ ( ̄ ー  ̄ ヘ)
Jonathan Van Matre
1
Czy mogę zasugerować dostosowanie końcówki 3? mapreduce jest dłuższy niż mapfoldl lub mapfoldr i może mieć różne zachowanie w zależności od implementacji. mapfoldl i mapfoldr są spójne (odpowiednio) z lewej i prawej strony, a zatem są lepszym wyborem. Dotyczy to również bardziej ogólnie redukcji (użyj foldl lub foldr).
Glen O
9

Iteracja po funkcjach

Jest to również możliwe w innych językach, ale zwykle dłużej niż prosta metoda. Jednak zdolność Julii do redefiniowania jej jednoargumentowych i binarnych operatorów sprawia, że ​​gra jest dość golfowa.

Na przykład, aby wygenerować tabelę dodawania, odejmowania, mnożenia i dzielenia dla liczb naturalnych od 1 do 10, można użyć

[x|y for x=1:10,y=1:10,| =(+,-,*,÷)]

który redefines binarnego operatora |jak +, -, *i ÷, następnie oblicza x|ydla każdej operacji, a xi yw pożądanych granicach.

Działa to również dla jednoargumentowych operatorów. Na przykład, aby obliczyć liczby zespolone 1 + 2i , 3-4i , -5 + 6i i -7-8i , ich negatywy, ich złożone koniugaty i ich multiplikatywne inwersje, można użyć

[~x for~=(+,-,conj,inv),x=(1+2im,3-4im,-5+6im,-7-8im)]

który redefines jednoargumentowy operatora ~jak +, -, conji inv, następnie oblicza ~xdla wszystkich żądanych liczb zespolonych.

Przykłady w rzeczywistych konkursach

Dennis
źródło
6
  1. Słowa kluczowe mogą czasami natychmiast następować po stałych bez potrzeby stosowania spacji lub średnika. Na przykład:

    n->(for i=1:n n-=1end;n)

    Zwróć uwagę na brak spacji między 1i end. Dotyczy to również zjawiska endwystępującego po zamknięciu miąższu, tj )end.

  2. Wykonać Integer podział stosując ÷zamiast div()lub przeciążenia operatora. Zauważ, że ÷w UTF-8 jest wart 2 bajty.

  3. Użyj vec()lub A[:](dla niektórych tablic A) zamiast, reshape()gdy to możliwe.

  4. Twórz funkcje zamiast pełnych programów, jeśli jest to dozwolone w regułach wyzwania. Krótsze jest zdefiniowanie funkcji, która akceptuje dane wejściowe niż definiowanie zmiennych poprzez odczyt ze standardowego wejścia. Na przykład:

    n->(n^2-1)
    n=read(STDIN,Int);n^2-1
  5. Zmienne można zwiększać wewnątrz argumentu funkcji. Na przykład, oto moja odpowiedź na wyzwanie Znajdź następny 1-rzadki numer binarny :

    n->(while contains(bin(n+=1),"11")end;n)

    Jest to krótsze niż zwiększanie nwewnątrz pętli.

Alex A.
źródło
6
  1. Nie piszreturn f(x)=x+4 jest identyczny, ale krótszy niżf(x)=return x+4 . Julia zawsze zwraca wynik ostatniej instrukcji.
  2. USE = zamiast w . [x for x in 1:4]ma 3 znaki dłuższe niż, ale równoważne[x for x=1:4]
gggg
źródło
5

Użyj wywołań funkcji nadawania.

Wprowadzono w Julii 0.5. To jest jak mapa, ale zużywa mniej znaków i zachowuje się w stosunku do swoich argumentów - co oznacza, że ​​możesz napisać mniej lambda do radzenia sobie z rzeczami.

Zamiast:

  • map(f,x) - 8 znaków.
  • f.(x) - 5 znaków

Jeszcze lepiej:

  • map(a->g(a,y),x) - 16 znaków
  • g.(x,[y]) - 9 znaków
Lyndon White
źródło