Wskazówki do gry w golfa w Mathematica


41

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

Odpowiedzi:


30

Poniższe wskazówki różnią się od najbardziej ekonomicznych do najczęściej używanych:

  1. Jeśli to możliwe, używaj poleceń wysokiego poziomu Mathematiki, nawet nieporęcznych:

  2. Użyj Graphics and Textdo sztuki Ascii: np. Programowanie gwiazd! i zbuduj zegar analogowy

  3. Dedykowane symbole:

    • logiczne i ustawiaj symbole operacji zamiast ich długich nazw postaci: ⋂, ⋃, ∧, ∨

    • Mapi Apply: /@, //@. @@,@@@

  4. Notacja przedrostka i przyrostka:

    • Print@"hello" zamiast Print["hello"]

    • a~f~b zamiast f[a,b]

  5. Gdy funkcja jest używana tylko raz, czysta funkcja może oszczędzić jeden lub dwa znaki.

  6. Łączenie ciągów na liście. ""<>{"a","b","c"}zamiastStringJoin@{"a","b","c"}

  7. Wykorzystaj funkcje z listą. Im dłuższe listy, tym lepiej.

    {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}}


2
Pisanie jest zawsze krótsze (Norm[#-#2]&)niż EuclideanDistance.
user202729,

32

Niektóre wbudowane funkcje o długich nazwach można zastąpić krótszymi wyrażeniami.

Na przykład:

  • Total => Tr
  • Transpose=> Threadlub\[Transpose]
  • True => 1<2
  • False => 1>2
  • Times => 1##&
  • Alternatives => $|##&
  • IntegerQ => ⌊#⌋==#&
  • a[[1]] => #&@@a
  • a[[All,1]] => #&@@@a
  • ConstantArray[a,n]=> Array[a&,n]lubTable[a,{n}]
  • Union@a=> {}⋃aluba⋃a
  • ToExpression@n=> FromDigits@njeśli njest liczbą
  • Divisible[n,m] => m∣n
  • FromDigits[n,2]=> Fold[#+##&,n]if njest listą 0s i 1s
  • Complex@z=> {1,I}.zgdzie zjest lista formularza{x,y}

5
@belisarius 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}}]
alephalpha 10.10.14

2
Myślę, że twoja Foldsztuczka FromDigitsdziała również na każdą inną bazę oprócz 10. Np. FromDigits[n,5]-> Fold[4#+##&,n](z premią za zaoszczędzenie dodatkowego bajtu dla baz 100i 1000).
Martin Ender,

1
@ mbomb007 3 bajty w UTF-8. W rzeczywistości ta postać jest U+F3C7.
alephalpha

1
W końcu zainstalowałem 10.3. Jeśli rozważamy pełne programy, nie sądzę, że Echojest to opcja, ponieważ drukuje >>(i spację) na STDOUT przed wydrukowaniem rzeczywistego ciągu.
Martin Ender,

2
Bo Complex[x,y] => {1,I}.{x,y}myślę, że x+y*Ijest znacznie krótszy z tym samym efektem?
Shieru Asakoto

22

Listy z powtarzanymi wartościami

Jest to dość powszechny wektor do pracy z:

{0,0}

Okazuje się, że można to skrócić bajtem:

0{,}

Nawet więcej bajtów jest zapisywanych, jeśli wektor jest dłuższy niż dwa zera. Można to również wykorzystać do zainicjowania macierzy zerowych, np. Poniższe daje macierz 2x2 zer:

0{{,},{,}}

Można to również zastosować do wartości niezerowych, jeśli są one wystarczająco duże, wystarczająco liczne lub ujemne. Porównaj następujące pary:

{100,100}
0{,}+100
{-1,-1}
0{,}-1
{3,3,3,3}
0{,,,}+3

Pamiętaj jednak, że od 6 wartości lepiej jest 1~Table~6w tym przypadku (potencjalnie wcześniej, w zależności od wymagań pierwszeństwa).

Powodem tego jest to, że ,wprowadza dwa argumenty na listę, ale argumenty pominięte (gdziekolwiek w Mathematica) są niejawne Null. Co więcej, mnożenie jest Listablei 0*xjest 0do prawie każdego x(z wyjątkiem rzeczy takich jak Infinityi Indeterminate), więc oto co się dzieje:

  0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}

W przypadku list 1s można użyć podobnej sztuczki, korzystając z reguł potęgowania. Istnieją dwa różne sposoby zapisywania bajtów, jeśli 1na liście są co najmniej trzy s:

{1,1,1}
1^{,,}
{,,}^0

7
+1; pokazuje to tylko, że chociaż Mathematica może mieć wbudowane wszystko, gra w golfa może być prawdziwym wyzwaniem.
LegionMammal978

Jeśli chcesz tablicę, która ostatecznie zostanie wypełniona 1s, to 1^{,,,}jest o jeden bajt mniejszy niż 0{,,,}+1.
Misza Ławrow

@MishaLavrov Och, dobry połów. To sprawia, że ​​jest krótszy przy trzech wartościach i możesz także użyć {,,}^0. Zmienię post.
Martin Ender,

19

Poznaj swoje argumenty funkcji czystej

Podczas gry w golfa często stosujesz podejście funkcjonalne, w którym używasz anonimowych (czystych) funkcji ze &składnią stenografii. Istnieje wiele różnych sposobów na uzyskanie dostępu do argumentów takiej funkcji, i często można ogolić kilka bajtów, dobrze znając możliwości.

Dostęp do pojedynczych argumentów

Prawdopodobnie wiesz o tym, jeśli wcześniej używałeś czystych funkcji. N p argument dalej #n, i #działa jako aliasu #1. Jeśli więc powiedzmy, że chcesz napisać funkcję, która przyjmuje jako parametry inną funkcję i jej argument (aby przekazać argument do tej funkcji), użyj

#@#2&

Nie działa to z liczbami ujemnymi (takimi jak możesz użyć podczas uzyskiwania dostępu do list).

Dostęp do nazwanych argumentów (nowość w V10)

Jedną z głównych nowych funkcji językowych w Mathematica 10 są Associations, które są w zasadzie mapami klucz-wartość z dowolnymi typami kluczy, napisanymi jak

<| x -> 1, "abc" -> 2, 5 -> 3 |>

Jeśli takie powiązanie zostanie przekazane jako pierwszy argument do funkcji czystej, możesz uzyskać dostęp do niektórych, jeśli jej argumenty są nazwanymi parametrami:

{#, #2, #3, #abc, #xyz} & [<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th"]
(* {<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th", "1st", "2nd"} *)

Zauważ, że #nadal odnosi się do całego powiązania, zgodnie z oczekiwaniami. Aby nazwane parametry działały, klucze muszą być ciągami (nie będzie działać, jeśli użyjesz na przykład niezdefiniowanych zmiennych), a ciągi te muszą zaczynać się od litery i zawierać tylko litery i cyfry.

Argument „ja” #0

Istnieje mniej znana funkcja, która #0również istnieje i daje sam obiekt funkcji. Może to być bardzo przydatne w quinach i quinach uogólnionych. W rzeczywistości najkrótsza quine Mathematica (o której wiem) to

ToString[#0][] & []

Nieco denerwujące jest to, że nie da ci dokładnie tych znaków, które wprowadziłeś. Np. Jeśli użyjesz @aplikacji funkcji, nadal będzie renderować jako, [...]a spacje zostaną wstawione w niektórych miejscach. Zwykle spowoduje to, że quine będzie trochę dłuższa, niż byś chciał, ale zawsze będzie działać, najpierw grając w golfa, a następnie kopiując jego wynik - co powinno być teraz prawdziwym quine.

Oprócz quinesów oznacza to również, że możesz pisać kod rekurencyjny bez konieczności nazywania swojej funkcji. Porównaj te trzy (naiwne, ale gra w golfa) implementacje Fibonacciego:

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]]&

Sekwencje argumentów

Teraz zaczyna się prawdziwa magia. Sekwencje nie są często używane w golfie, ponieważ nazwa Sequencejest zbyt długa, aby przez większość czasu była tego warta. Ale w czystych funkcjach świecą. Jeśli nie jesteś zaznajomiony z sekwencjami, są one w zasadzie jak ikony w innych językach, jeśli użyjesz sekwencji Listna liście argumentów funkcji lub, jej elementy zostaną automatycznie rozwinięte w osobne miejsca. Więc

{1, Sequence[2, 3, 4], 5} == {1, 2, 3, 4, 5}
f["a", Sequence[0, {}], "b"] == f["a", 0, {}, "b"]

Teraz w czystych funkcjach ##lub ##1jest sekwencją wszystkich argumentów. Podobnie ##2jest sekwencją wszystkich argumentów rozpoczynających się od drugiego, ##3wszystkich argumentów rozpoczynających się od trzeciego itd. Tak więc na początek możemy po prostu zaimplementować Sequencejako ##&, oszczędzając 5 bajtów. Jako przykładowe użycie daje nam to alternatywę dla Join@@list(zobacz tę wskazówkę ), która nie zapisuje żadnych bajtów, ale i tak warto o tym wiedzieć:

 ##&@@@list

To skutecznie spłaszcza pierwszy poziom zagnieżdżonej listy. Co jeszcze możemy z tym zrobić? Oto 2 bajty krótsza alternatywa dla RotateLeft:

 RotateLeft@list
 {##2,#}&@list

W przypadku tych samych rzeczy warto pamiętać o tej funkcji. Możemy jednak zrobić lepiej! Sekwencje stają się naprawdę interesujące, gdy weźmiemy pod uwagę, że operatory są faktycznie realizowane jako funkcje pod maską. Np. a+bFaktycznie ocenia na Plus[a,b]. Więc jeśli podamy tę sekwencję ...

1+##&[1,2,3]
=> Plus[1,##] 
=> Plus[1,1,2,3]
=> 7

Ta sztuczka została użyta w tym poradniku, aby zaoszczędzić bajt Times, ponieważ zestawienie jest technicznie także tylko operatorem:

1##&[1,2,3]
=> Times[1,##]
=> Times[1,1,2,3]
=> 6

Możesz go również użyć do zapisania bajtu, Unequaljeśli masz wartość jednoznakową lub zmienną, o której wiesz, że nie ma jej w argumentach ( Nprawdopodobnie zadziała w 99% przypadków):

Unequal[a,b,c]
N!=##&[a,b,c]

Staje się to jeszcze bardziej interesujące w przypadku operatorów jednoargumentowych -i /- te dwa ostatnie są faktycznie realizowane pod względem mnożenia i potęgowania. Oto lista rzeczy, które możesz zrobić, gdzie w ostatniej kolumnie założono, że funkcja przekazała argumenty 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

Inne częste operatorzy !=, ==, &&, ||. Mniej spotykane, aby pamiętać to |, @*, /*. Podsumowując, oto mała sztuczka bonusowa:

####        Times[##,##]            Times[a,b,c,a,b,c]          (a*b*c)^2

Eksperymentuj z nimi i daj mi znać, jeśli znajdziesz jakieś inne przydatne lub szczególnie interesujące aplikacje!


15

Sqrt@2lub 2^.5=>√2

a[[1]]=>a〚1〛

#+#2&=>+##&

Flatten@a=> Join@@a(czasami)

Function[x,x^2]=> xx^2lub#^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


1
Należy pamiętać, że przy pomiarze przez bajtów, przy użyciu i zajmuje 3 bajty każda (zakładamy UTF8)
user202729

12

Operatory jako funkcje

Zainspirowany niedawnym odkryciem Dennisa dla Julii , pomyślałem, że przyjrzę się Mathematice. Wiedziałem, że Mathematica definiuje dużą liczbę nieużywanych operatorów, ale nigdy nie zwracałem na to większej uwagi.

W celach informacyjnych listę wszystkich operatorów można znaleźć tutaj w formie tabeli pierwszeństwa. Trójkąt w ostatniej kolumnie wskazuje, czy ten operator ma wbudowane znaczenie, czy nie. Chociaż nie wszystkie z nich, których nie można łatwo zdefiniować, większość z nich.

Dogodnie są dwa nieużywane operatory o punkcie kodowym mniejszym niż 256, tak że można ich używać jako pojedynczych bajtów w pliku źródłowym zakodowanym w standardzie ISO 8859-1:

  • ± (0xB1) może być używany zarówno jako operator jednoargumentowy, jak i operator binarny.
  • · (0xB7) może być użyte jako operator wariacyjny lub n-arylowy, dla n> 2.

Jest jeszcze jeden haczyk: z jakiegoś dziwnego powodu przy definiowaniu tych operatorów potrzebujesz przed nimi jednego miejsca, bo inaczej Mathematica spróbuje parsować mnożenie. Podczas ich używania nie potrzebujesz jednak żadnych spacji:

±x_:=2x
x_ ±y_:=x+y
x_ ·y_ ·z_:=x*y+z
Print[±5]  (* 10 *)
Print[3±4] (*  7 *)
Print[3·4·5] (* 17 *)

Porównaj to z:

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

To oszczędza jeden bajt podczas definiowania funkcji i dwa bajty podczas jej używania. Zauważ, że definicja ·nie zapisuje bajtów dla czterech argumentów i zacznie kosztować bajty dla większej liczby argumentów, ale użycie może nadal oszczędzać bajty, w zależności od priorytetu operatorów używanych w argumentach. Warto również zauważyć, że można tanio zdefiniować funkcję wariadyczną, którą można następnie wywołać znacznie wydajniej:

x_ ·y__:={y}
Print[1·2·3·4·5] (* {2, 3, 4, 5} *)

Należy jednak pamiętać, że wywołanie tych funkcji variadycznych przy pomocy jednego argumentu nie jest łatwe. (Można to zrobić CenterDot[x]lub ##&[]·xjeśli jednak trzeba, że istnieje duża szansa, że jesteś lepiej z innego rozwiązania.)

Oczywiście nie zapisuje to niczego dla rozwiązań, w których wystarczy funkcja bez nazwy, ale czasami trzeba zdefiniować funkcje pomocnicze do późniejszego użycia, a czasem krótsze jest zdefiniowanie nazwanych funkcji, np. W celu ustawienia różnych definicji dla różnych parametrów. W takich przypadkach użycie operatora może zaoszczędzić przyzwoitą liczbę bajtów.

Pamiętaj, że korzystanie z tych plików zakodowanych w standardzie ISO 8859-1 wymaga $CharacterEncodingustawienia zgodnej wartości, takiej jak wartość domyślna systemu Windows WindowsANSI. W niektórych systemach domyślnie UTF-8nie będzie w stanie odczytać tych punktów kodu z pojedynczych bajtów.


To jest naprawdę świetne, nie wiedziałem, że Mathematica ma listę operatorów, a nawet uwzględniła ich pierwszeństwo. Ci dwaj operatorzy, których znalazłeś, na pewno się przydadzą.
mile

8

Wybieranie wartości na podstawie liczby całkowitej

Naiwne podejście do wyboru pomiędzy yi z, w zależności od tego, czy xjest, 0czy 1jest

If[x<1,y,z]

Istnieje jednak krótsza droga:

y[z][[x]]

To działa, ponieważ [[0]]daje Headwyrażenia, w tym przypadku y, podczas gdy [[1]]po prostu daje pierwszy element - w tym przypadku pierwszego argumentu z.

Możesz nawet użyć tego, aby wybrać pomiędzy więcej niż dwiema wartościami:

u[v,w][[x]]

Zauważ, że to nie zadziała, jeśli ujest to funkcja, która faktycznie coś ocenia. Ważne jest, aby Mathematica zachował u[v,w]taki, jaki jest. Działa to jednak w większości przypadków, w tym jeśli ua jest liczbą, łańcuchem lub listą.

Kredyty za tę sztuczkę trafiają do alephalpha - odkryłem to w jednej z jego odpowiedzi.

Jeśli xjest oparty na 1 zamiast na zero, po prostu użyj

{y,z}[[x]]

lub

{u,v,w}[[x]]

W niektórych rzadkich przypadkach możesz nawet skorzystać z faktu, że mnożenie nie jest oceniane dla niektórych wartości:

{"abc","def"}[[x]]
("abc""def")[[x]]

Zauważ jednak, że Mathematica faktycznie zmieni kolejność argumentów mnożenia, jeśli pozostanie nieocenione, więc powyższe jest identyczne z

("def""abc")[[x]]

8

Alternatywy dla Length

Zostało to całkowicie przepisane z kilkoma sugestiami LegionMammal978 i Miszy Ławrow. Wielkie dzięki dla obu z nich.

W wielu przypadkach Lengthmożna go nieco skrócić, korzystając z Tr. Podstawową ideą jest przekształcenie danych wejściowych w listę 1s, tak że Trsumuje się je, co równa się długości listy.

Najczęstszym sposobem na to jest użycie 1^x(dla listy x). Działa Powerto, ponieważ jest Listablei 1^ndla większości wartości atomowych njest po prostu 1(w tym wszystkie liczby, łańcuchy i symbole). Dzięki temu możemy już zapisać jeden bajt:

Length@x
Tr[1^x]

Oczywiście zakłada się, że xjest to wyrażenie o wyższym priorytecie niż ^.

Jeśli xzawiera tylko 0si 1, możemy zapisać kolejny bajt przy użyciu Factorial(zakładając, że xma wyższy priorytet niż !):

Length@x
Tr[x!]

W niektórych rzadkich przypadkach xmoże mieć niższy ^priorytet niż mnożenie. W takim przypadku będzie mieć również niższy priorytet niż @, więc naprawdę musimy to porównać Length[x]. Przykładem takiego operatora jest .. W takich przypadkach nadal można zapisać bajt za pomocą tego formularza:

Length[x.y]
Tr[0x.y+1]

Na koniec kilka uwag na temat rodzaju list, na których to działa:

Jak wspomniano na górze, działa to na płaskich listach zawierających tylko liczby, ciągi znaków i symbole. Będzie jednak działał również na niektórych głębszych listach, chociaż w rzeczywistości oblicza coś nieco innego. W przypadku n -D tablicy prostokątnej, użycie Trdaje najkrótszy wymiar (w przeciwieństwie do pierwszego). Jeśli wiesz, że najbardziej zewnętrzny wymiar jest najkrótszy lub wiesz, że wszystkie są takie same, to Tr-wyrażenia są nadal równoważne Length.


3
Właśnie znalazłem jeszcze krótszy rozwiązanie: Length@x == Tr[1^x]. Powinien działać z większością list.
LegionMammal978

@ LegionMammal978 to niesamowite, dzięki :). Wkrótce to zmienię.
Martin Ender

1
Dwa razy teraz używałem Tr[x!]zamiast Tr[1^x]zapisywać jeden bajt w specjalnym przypadku, w którym xzawiera tylko zera i jedynki.
Misza Ławrow

@MishaLavrov To naprawdę miłe! :)
Martin Ender,

7
  1. Poznaj rozwiązania rekurencyjne - Mathematica to paradygmat, ale podejście funkcjonalne jest często najbardziej ekonomiczne. NestWhilemoże być bardzo zwartym rozwiązaniem problemów z wyszukiwaniem NestWhileListi FoldListma dużą moc, gdy trzeba zwrócić lub przetworzyć wyniki pośrednich iteracji. Map (/@), Apply (@@, @@@), MapThread, I naprawdę wszystko na Wolframa Functional Programming stronie dokumentacji jest silnym rzeczy.

  2. Skrócona forma inkrementacji / dekrementacji - Na przykład zamiast tego While[i<1,*code*;i++]możesz zrobić
    While[i++<1,*code*]

  3. Nie zapomnij, że możesz wstępnie zwiększyć / zmniejszyć - na przykład --izamiast i--. Może to czasem zaoszczędzić kilka bajtów w otaczającym kodzie, eliminując operację przygotowawczą.

  4. W następstwie nr 5 Davida Carrahera: Gdy ta sama funkcja jest używana wiele razy, przypisanie jej symbolu może zaoszczędzić bajty. Na przykład, jeśli używasz ToExpression4 razy w roztworze, t=ToExpressionumożliwia t@*expression*późniejsze użycie . Jednak zanim to zrobisz, zastanów się, czy wielokrotne stosowanie tej samej funkcji wskazuje na możliwość bardziej ekonomicznego podejścia rekurencyjnego.


MapThreadczęsto można zastąpić \[Transpose]. TIO .
user202729,

7

Nie używaj, {}jeśli używasz @@@.

W niektórych przypadkach możesz napotkać wyrażenie takie jak:

f@@@{{a,b},{c,d}}

Można zmniejszyć bajty pisząc:

f@@@{a|b,c|d}

Alternativesma bardzo niski priorytet, więc generalnie dobrze jest pisać wyrażenia (zauważalnym wyjątkiem są czyste funkcje; możesz używać go tylko w lewym skrajnym elemencie Alternatives).

f@@@{f@a|b~g~1,#^2&@c|d@2}

Zauważ, że f@@a|b|c(zamiast f@@{a,b,c}) nie działa, ponieważ Applyma wyższy priorytet niż Alternative.

W takim przypadku powinieneś po prostu użyć f@@{a,b,c}.


6

Tylko Mathematica 10

Formularze operatora

Mathematica 10 obsługuje tak zwane „formy operatora”, co w zasadzie oznacza, że ​​niektóre funkcje mogą być obsługiwane. Currywanie funkcji polega na utworzeniu nowej funkcji przez naprawienie jednego z jej operatorów. Powiedz, że używasz SortBy[list, somereallylongfunction&]wielu różnych lists. Przed, prawdopodobnie byłby przypisany SortBydo si czysta funkcja do fzarzutów

s=SortBy;
f=somereallylongfunction&;
list1~s~f;
list2~s~f;
list3~s~f;

Teraz możesz curry SortBy, co oznacza, że ​​możesz to zrobić

s=SortBy[somereallylongfunction&];
s@list1;
s@list2;
s@list3;

Te same prace dla wielu innych funkcji, które biorą listy lub argumentu funkcji, w tym (ale nie tylko) Select, Map, Nearest, itd.

ybeltukov na Mathematica.SE był w stanie stworzyć pełną listę :

{"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"}

Skład i prawy skład

Istnieją nowe skróty dla Composition( @*) i RightComposition( /*). Oczywiście wymyślony przykład, w którym mogą one zapisywać znaki, znajduje się w następujących trzech równoważnych wierszach

Last@Range@# & /@ Range[5]
Last@*Range /@ Range[5]
Range /* Last /@ Range[5]

5

Nie pisz funkcji 0-argumentowych

Nie ma potrzeby takiego kodu:

f[]:=DoSomething[1,2]
(*...*)
f[]
(*...*)
f[]

Możesz po prostu użyć zmiennej, :=aby wymusić ponowną ocenę prawej strony:

f:=DoSomething[1,2]
(*...*)
f
(*...*)
f

Oznacza to również, że możesz aliasować dowolną często wykonywaną akcję (nawet jeśli jest to coś podobnego n++) do jednego znaku, kosztem 5 bajtów. W takim przypadku n++zwraca się po czwartym użyciu:

n++;n++;n++;n++
f:=n++;f;f;f;f

5

Użyj, %aby uzyskać darmową zmienną

Ta wskazówka ma zastosowanie tylko wtedy, gdy można założyć środowisko REPL Mathematica. %nie jest zdefiniowany, gdy kod jest uruchamiany jako skrypt.

Kiedy można skorzystać z funkcji rEPL, nie rób tego:

a=someLongExpression;some[other*a,expression@a,using^a]

Zamiast tego pamiętaj, że Mathematica przechowuje ostatnio ocenione wyrażenie (zakończone znakiem nowej linii) w %:

someLongExpression;
some[other*%,expression@%,using^%]

Dodana nowa linia kosztuje bajt, ale oszczędzasz dwa, usuwając a=, więc ogólnie oszczędza to jeden bajt.

W niektórych przypadkach (np. Gdy i tak chcesz wydrukować wartość a), możesz nawet pominąć ;, oszczędzając dwa bajty:

someLongExpression
some[other*%,expression@%,using^%]

Jeden lub dwa bajty mogą wydawać się dość niewielkie, ale jest to ważny przypadek, ponieważ sprawia, że ​​ekstrakcja powtarzających się wyrażeń (co jest bardzo powszechną techniką) jest znacznie bardziej przydatna podczas gry w golfa:

Normalna technika wyodrębniania powtarzanych wyrażeń kosztuje cztery bajty narzutu, które należy zapisać przez dalsze użycie wyrażenia. Oto krótka tabela minimalnej liczby zastosowań wyrażenia (według długości wyrażenia) do wyodrębnienia do nazwanej zmiennej w celu zapisania czegokolwiek:

Length   Min. Uses
2        6
3        4
4        3
5        3
6        2
...      2

Przy użyciu zmiennej bez nazwy możliwe będzie znacznie częstsze zapisywanie kilku bajtów:

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

Nie sądzę %%ani nie %nmożna go użyć do gry w golfa, ponieważ jeśli nie użyjesz ich co najmniej dwa razy, możesz po prostu umieścić wyrażenie dokładnie tam, gdzie jest potrzebne. A jeśli użyjesz go dwa razy, dodatkowy znak w nazwie zmiennej anuluje oszczędności wynikające z pominięcia niektórych x=.


Pamiętaj, że nie działa w trybie skryptowym.
alephalpha

@alephalpha Co to jest tryb skryptowy?
Martin Ender


@alephalpha O tak, na sekundę wyłączyłem mózg ... więc to by oznaczało, że tak naprawdę nie można go w ogóle użyć, chyba że można założyć środowisko REPL.
Martin Ender

5

Sprawdzanie, czy lista jest posortowana

Jest to zasadniczo następstwo tej wskazówki, ale jest to wystarczająco powszechne zadanie, które moim zdaniem uzasadnia własną odpowiedź.

Naiwnym sposobem sprawdzenia, czy lista jest w porządku, jest użycie

OrderedQ@a

Możemy zrobić jeden bajt lepiej

Sort@a==a

Nie działa to jednak, jeśli nie mamy już tego, co chcielibyśmy sprawdzić w zmiennej. (Potrzebowalibyśmy czegoś, Sort[a=...]==aco jest niepotrzebnie długie.) Istnieje jednak inna opcja:

#<=##&@@a

Najlepszą rzeczą jest to, że można to wykorzystać do sprawdzenia, czy dane wejściowe są sortowane odwrotnie dla tej samej liczby bajtów:

#>=##&@@a

Kolejny bajt można zapisać, jeśli a) wiemy, że elementy listy są różne ib) znamy dolną granicę między 0 a 9 (włącznie; lub górną granicę dla odwrotnego porządku sortowania):

0<##&@@a
5>##&@@a

Aby zobaczyć, dlaczego to działa, sprawdź „Sekwencje argumentów” we wskazówce znajdującej się u góry.


Alternatywnie, (surowe) obniża związany z odwróconymi również posortowane pracy: ##>0&@@a. Podobne dla górnej granicy sortowania.
user202729,

@ user202729 O, dobra uwaga, nie krępuj się edytować (w przeciwnym razie spróbuję to zrobić w weekend, jeśli pamiętam).
Martin Ender

5

Powtarzanie łańcucha

Zamiast StringRepeat[str,n]używać (0Range[n]+str)<>"". Lub jeśli strnie zależy od argumentów dotyczących slotów, jeszcze lepiej jest Array[str&,n]<>""według tej wskazówki.


1
Następstwo: zamiast StringRepeat[s,n+1]używać Array[s&,n]<>s(nawet jeśli masz już n+1zmienną).
Martin Ender

LepiejTable[str,n]<>""
attinat

5

Jeśli potrzebujesz listy liczb posortowanych odwrotnie, nie używaj

Reverse@Sort@x

ale

-Sort@-x

aby zapisać sześć bajtów. Sortowanie według wartości ujemnej jest również przydatne w SortByscenariuszach:

Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]

2
Co -Sort@-x?
JungHwan Min

1
@JungHwanMin Oh, uhhh, tak, to o wiele lepsze. :)
Martin Ender

4

Możesz wstawić wyrażenie, w Breakktórym można zapisać jeden lub dwa znaki. Przykład ( inne szczegóły nie dla golfa dla jasności ):

result = False;
Break[]

można zamienić

Break[result = False]

aby uratować jedną postać. Jeśli dane wyrażenie nie ma niższego priorytetu niż aplikacja funkcji, możesz nawet zapisać inny znak:

Print@x;
Break[]

można zamienić

Break@Print@x

Chociaż nieudokumentowany, argument, który Breakwydaje się być zwracany przez otaczającą pętlę, może potencjalnie prowadzić do jeszcze większej oszczędności.


4

Aby usunąć wszystkie białe znaki z ciągu s, użyj

StringSplit@s<>""

To znaczy użyj StringSplitdomyślnego (podzielonego na komponenty niebiałe) i po prostu połącz je z powrotem. To samo jest prawdopodobnie najkrótsze, jeśli chcesz pozbyć się jakiejkolwiek innej postaci lub podłańcucha:

s~StringSplit~"x"<>""

4

Alternatywy dla Range

Bardzo częstym zadaniem jest zastosowanie jakiejś funkcji do wszystkich liczb od 1 do a n(zwykle podawanych jako dane wejściowe). Istnieją zasadniczo 3 sposoby, aby to zrobić (na przykład używając nienazwanej funkcji tożsamości):

#&/@Range@n
Array[#&,n]
Table[i,{i,n}]

Zwykle wybieram pierwszy (z jakiegokolwiek powodu), ale rzadko jest to najlepszy wybór.

Używanie Arrayzamiast tego

Powyższy przykład pokazuje, że użycie Arrayma tę samą liczbę bajtów. Ma jednak tę zaletę, że jest pojedynczym wyrażeniem. W szczególności, jeśli chcesz dalej przetwarzać wynik za pomocą funkcji f, możesz użyć notacji przedrostkowej, co oszczędza bajt Range:

f[#&/@Range@n]
f@Array[#&,n]

Ponadto możesz ominąć nawiasy wokół funkcji bez nazwy, których możesz potrzebować Rangenp

15/(#&)/@Range@n
15/Array[#&,n]

Jeśli nie chcesz go dalej używać (lub z operatorem, który ma mniejszy priorytet), możesz zamiast tego napisać Arraysię w notacji infiksowej, a także zapisać bajt:

#&/@Range@n
#&~Array~n

Dlatego Arrayprawie na pewno jest lepszy niż Range.

Używanie Tablezamiast tego

Teraz tabela musi składać się z 3 bajtów lub co najmniej 2, gdy zapis infix jest opcją:

#&/@Range@n
i~Table~{i,n}

Gdy nie używasz notacji infix, Tablemożesz pominąć nawiasy, jeśli twoja funkcja składa się z kilku instrukcji:

(#;#)&/@Range@n
Table[i;i,{i,n}]

Jest to jeszcze dłuższe, ale daje dodatkowe oszczędności w przypadku wymienionym poniżej.

Rzeczywiste oszczędności wynikają z faktu, że Tablenazwa działającej zmiennej nie powinna zostać odrzucona. Często zagnieżdżasz funkcje bez nazw, w których chcesz użyć zmiennej zewnętrznej w jednej z funkcji wewnętrznych. Kiedy tak się dzieje, Tablejest krótszy niż Range:

(i=#;i&[])&/@Range@n
Table[i&[],{i,n}]
i&[]~Table~{i,n}

Zapisujesz nie tylko znaki do przypisania i, ale możesz również zredukować funkcję do pojedynczej instrukcji w tym procesie, co pozwoli ci na użycie notacji infix. Dla porównania, Arrayw tym przypadku jest również dłuższy, ale wciąż krótszy niż Range:

(i=#;i&[])&~Array~n

Kiedy faktycznie używałbyś Range?

Ilekroć nie potrzebujesz wywołania funkcji do przetworzenia wartości, np. Kiedy mapowanie można wykonać za pomocą operacji wektorowej. Na przykład:

5#&~Array~n
5Range@n
#^2&~Array~n
Range@n^2

Oczywiście jest również krótszy, jeśli nie chcesz mapować żadnej funkcji, np

Mean@Array[#&,n]
Mean@Range@n

Wreszcie ktoś inny, kto f/@Range[x]regularnie używa ...
LegionMammal978

4

Znalezienie najmniejszej liczby spełniającej warunek

Niektóre podobne konstrukcje i=1;While[cond[i],i++]są w porządku, ale istnieje alternatywa, która jest o dwa bajty krótsza:

1//.i_/;cond[i]:>i+1

Powyższy kod wielokrotnie zastępuje szereg ize i+1podczas gdy spełnia warunek cond[i]. W takim przypadku izaczyna się od 1.

Zauważ, że domyślna maksymalna liczba iteracji wynosi 2 ^ 16 (= 65536). Jeśli potrzebujesz więcej iteracji, Whilebyłoby lepiej. ( MaxIterations->∞jest za długi)


4

Ocena zwarcia nadużycia

Czasami możesz zastąpić Ifoperatorem logicznym.

Załóżmy na przykład, że chcesz utworzyć funkcję, która sprawdza, czy liczba jest liczbą pierwszą, a wypisuje, 2*(number) - 1jeśli to prawda:

If[PrimeQ@#,Print[2#-1]]&

Jest krótszy, jeśli użyjesz &&zamiast tego:

PrimeQ@#&&Print[2#-1]&

Nawet jeśli masz wiele wyrażeń, nadal zapisujesz bajty:

If[PrimeQ@#,a++;Print[2#-1]]&

PrimeQ@#&&a++&&Print[2#-1]&
(* or *)
PrimeQ@#&&(a++;Print[2#-1])&

Możesz użyć ||w przypadkach, gdy chcesz, aby warunek był False:

If[!PrimeQ@#,Print[2#-1]]&
(* or *)
If[PrimeQ@#,,Print[2#-1]]&
(* can become *)
PrimeQ@#||Print[2#-1]&

Te sztuczki działają, ponieważ operatory logiczne mogą być zwarte ; drugi argument, a następnie nie muszą nawet być prawidłowymi wyrażeniami logicznymi.

Oczywiście nie działa to, jeśli potrzebujesz wartości zwracanej Iflub gdy potrzebujesz zarówno prawdziwych, jak i fałszywych argumentów If.



3

Za pomocą Optional (:)

Optional (:) może służyć do rozwijania list w zamianach, bez konieczności definiowania osobnej reguły dla rozwinięcia.

Ta odpowiedź przeze mnie i ta odpowiedź @ngenisis są przykładami.

Stosowanie

... /. {p___, a_: 0, b_, q___} /; cond[b] :> ...

Powyższa zamiana najpierw wykorzystuje wzorzec {p___, a_, b_, q___}i znajduje takie dopasowanie, które bspełnia określony warunek.

Gdy nie znaleziono takiego dopasowania, pomija a_i zamiast tego szuka {p___, b_, q___}. anie jest uwzględniany podczas wyszukiwania i zakłada się, że ma wartość 0.

Zauważ, że drugie wyszukiwanie wzorca działałoby tylko dla btego, które występuje na początku listy; jeśli bwartość spełniająca warunek znajduje się pośrodku, wówczas {p___, a_, b_, q___}(która ma wyższy priorytet) pasowałaby do niej.

Zastąpienie jest równoznaczne z dodaniem a, 0gdy bspełniony jest warunek na początku listy. (tzn. nie ma potrzeby definiowania oddzielnej reguły {b_, q___} /; cond[b] :> ...)


3

Dowiedz się, kiedy (a kiedy nie), aby użyć nazwanych argumentów funkcji czystej

W przypadku golfa kodowego czysto Functionargumenty są najczęściej przy użyciu Slots; np. #dla pierwszego argumentu, #2dla drugiego itd. ( więcej szczegółów znajdziesz w tej odpowiedzi ).

W wielu przypadkach będziesz chciał zagnieździć Functions. Na przykład 1##&@@#&jest to, Functionktóry bierze listę jako swój pierwszy argument i wyprowadza iloczyn swoich elementów. Oto ta funkcja w TreeForm:

wprowadź opis zdjęcia tutaj

Argumenty przekazywane do poziomu górnej Functionmoże jedynie wypełnienie Slots i SlotSequences obecni na najwyższym poziomie, co w tym przypadku oznacza, że SlotSequencew wewnętrzny Functionnie będzie miał żadnych argumentów sposób dostępu do poziomu górnego Function.

Jednak w niektórych przypadkach możesz chcieć, aby Functionzagnieżdżony w innym Functionmógł odwoływać się do argumentów zewnętrznych Function. Na przykład możesz chcieć czegoś takiego Array[fun,...]&, gdzie funkcja funzależy od argumentu na najwyższym poziomie Function. Dla konkretności, powiedzmy, że funresztę kwadratu modułu wejściowego należy przekazać na najwyższy poziom Function. Jednym ze sposobów osiągnięcia tego jest przypisanie argumentu najwyższego poziomu do zmiennej:

(x=#;Array[Mod[#^2,x]&,...])&

Gdziekolwiek xpojawia się w środku Function Mod[#^2,x]&, będzie odnosić się do pierwszego argumentu na zewnątrz Function, podczas gdy #odniesie się do pierwszego argumentu do wnętrza Function. Lepszym podejściem jest użycie faktu, który Functionma postać dwóch argumentów, gdzie pierwszy argument jest symbolem lub listą symboli, które będą reprezentować nazwane argumenty dla Function(w przeciwieństwie do nienazwanych Slot). W ten sposób oszczędzamy nam w tym przypadku trzy bajty:

xArray[Mod[#^2,x]&,...]

to trzy bajtowy prywatny znak U+F4A1reprezentujący binarny operator infix \[Function]. Możesz również użyć postaci binarnej Functionw innym Function:

Array[xMod[x^2,#],...]&

Jest to równoważne z powyższym. Powodem jest to, że jeśli używasz nazwanych argumentów, to Slots i SlotSequenceszakłada się, że należą do następnego Functionpowyżej, który nie używa nazwanych argumentów.

Teraz tylko dlatego, że możemy zagnieżdżać Functionw ten sposób, nie oznacza, że ​​zawsze powinniśmy. Na przykład, jeśli chcemy wybrać te elementy listy, które są mniejsze niż dane wejściowe, możemy ulec pokusie zrobienia czegoś takiego:

Select[...,xx<#]&

W rzeczywistości byłby krótszy w użyciu Casesi unikałby potrzeby Functioncałkowicie zagnieżdżonego :

Cases[...,x_/;x<#]&

2

Możesz zapisać bajt, obejście Prependlub PrependTo:

l~Prepend~x
{x}~Join~l
{x,##}&@@l

lub

l~PrependTo~x
l={x}~Join~l
l={x,##}&@@l

Niestety nie pomaga to w przypadku bardziej powszechnego Append, który wydaje się być najkrótszym odpowiednikiem Array.push()w innych językach.


2

Mathematica 10.2: BlockMapjest Partition+Map

Ta wskazówka może być również zatytułowana „Przeczytaj wszystkie informacje o wydaniu”. (Dla odniesienia, oto informacje o wersji 10.2 i dzisiejszej wersji 10.3 ).

W każdym razie nawet drobne wydania zawierają wiele nowych funkcji, a jedną z bardziej przydatnych (do gry w golfa) z 10.2 jest nowa BlockMapfunkcja. Zasadniczo łączy Partitioni Map, co jest świetne dla golfistów, ponieważ Partitionjest używany dość często i jest to naprawdę denerwująco długa nazwa funkcji. Nowa funkcja Partitionsama się nie skraca , ale ilekroć chcesz mapować funkcję na partycje (co prawdopodobnie zdarza się częściej niż nie), możesz teraz zapisać bajt lub dwa:

#&/@l~Partition~2
BlockMap[#&,l,2]
#&/@Partition[l,3,1]
BlockMap[#&,l,3,1]

Oszczędności stają się jeszcze większe, gdy nowa pozycja nienazwanej funkcji pozwala zaoszczędzić sobie kilka nawiasów:

#&@@(#&/@Partition[l,3,1])
#&@@BlockMap[#&,l,3,1]

Niestety nie mam pojęcia, dlaczego również nie dodałem BlockApply, kiedy byli przy tym ...

Pamiętaj też, że BlockMapnie obsługuje czwartego parametru, którego można użyć Partitiondo uzyskania cyklicznej listy:

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... *)

2

Przechowywanie funkcji i wyrażeń w zmiennej

Jeśli twoja odpowiedź kończy się wielokrotnym użyciem tych samych funkcji lub wyrażeń, możesz rozważyć przechowywanie ich w zmiennych.

Jeśli twoje wyrażenie ma długość li używasz go nrazy, zwykle l * nzużywa bajty.

Jeśli jednak przechowujesz ją w zmiennej o długości 1, zajmie to tylko 3 + l + nbajty (lub 2 + l + nbajty, jeśli przypiszesz zmienną tam, gdzie nie będziesz potrzebować CompoundExpression (;)lub w nawiasach).


Rozważmy na przykład prosty problem, znajdowanie podwójnych liczb pierwszych mniejszych niż N.

Można napisać to 54 bajtowe rozwiązanie:

Select[Range@#,PrimeQ@#&&(PrimeQ[#+2]||PrimeQ[#-2])&]&

W tym przykładzie funkcja PrimeQjest używana trzy razy.

Przypisując PrimeQnazwę zmiennej, można zmniejszyć liczbę bajtów. Oba następujące są 48 bajtów (54 - 6 bajtów):

Select[p=PrimeQ;Range@#,p@#&&(p[#+2]||p[#-2])&]&
Select[Range@#,(p=PrimeQ)@#&&(p[#+2]||p[#-2])&]&

2

Aby uzyskać rosnącą listę klucz-wartość, użyj SortzamiastSortBy

W przypadku list takich jak list = {{1, "world"}, {0, "universe"}, {2, "country"}}następujące trzy instrukcje są prawie równoważne.

SortBy[list,#[[1]]&]
list~SortBy~First
Sort@list

Połącz SelectiSortBy

Czasami musimy wybierać wpisy z większego zestawu i sortować je, aby znaleźć minimum / maksimum. W niektórych okolicznościach dwie operacje można połączyć w jedną.

Na przykład, co najmniej następujące dwie instrukcje są prawie równoważne.

SortBy[Select[l,SomeFormula==SomeConstant&],SortValue&]
SortBy[l,SortValue+99!(SomeFormula-SomeConstant)^2&]

i

SortBy[Select[l,SomeFormula!=SomeConstant&],SortValue&]
SortBy[l,SortValue+1/(SomeFormula-SomeConstant)&]

1/0jest ComplexInfinity„większy” niż wszystkie liczby rzeczywiste.

Na przykład lista klucz-wartość:

{SortValue,#}&/@SortBy[Select[l,SomeFormula==SomeConstant],SortValue&]
Sort[{SortValue+99!(SomeFormula-SomeConstant)^2,#})&/@l]

1

Spłaszczanie Arrayz##&

Gdy używasz macierzy wielowymiarowej do obliczania listy wyników, które należy spłaszczyć, użyj ##&jako czwartego argumentu. Zastępuje to głowy tablic ##&(odpowiednikiem Sequence) zamiast List, więc końcowy wynik będzie (płaski) Sequencewyników.

Porównaj w dwóch wymiarach

{Array[f,dims,origin,##&]}
Join@@Array[f,dims,origin]

Oczywiście Join@@Array[f,dims] nadal jest o 2 (lub 3, jeśli można zastosować notację infiksową) bajty krótsze niż {Array[f,dims,1,##&]}.

W co najmniej trzech wymiarach {Array[f,dims,origin,##&]}jest zawsze krótszy niż alternatywa, nawet jeśli początkiem jest 1.

{Array[f,dims,1,##&]}
f~Array~dims~Flatten~2

1

Wartości domyślne

Wartości domyślne radzą sobie z brakującymi argumentami wzorca w efektywny sposób. Na przykład, jeśli chcemy wzorować dopasowanie Exp[c_*x]w regule dla dowolnej wartości c, naiwny

Exp[x] + Exp[2x] /. {Exp[c_*x] -> f[c], Exp[x] -> f[1]}
(*    f[1] + f[2]    *)

używa o wiele więcej bajtów niż w przypadku użycia wartości domyślnej, cgdy tylko jej brakuje:

Exp[x] + Exp[2 x] /. Exp[c_.*x] -> f[c]
(*    f[1] + f[2]    *)

Zastosowanie domyślnie jest oznaczona kropką na wzór: c_..

Domyślne wartości wiąże się z czynności: w powyższym przykładzie, operacja jest Timesw c_.*xi Brak wartości c_w ten sposób pobierana z domyślnej wartości z Times, co 1. Plusdomyślna wartość od 0:

Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(*    f[0] + f[2]    *)

W przypadku Powerwykładników wartość domyślna to 1:

x + x^2 /. x^n_. -> p[n]
(*    p[1] + p[2]    *)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.