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ą).
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:
Poniższe wskazówki różnią się od najbardziej ekonomicznych do najczęściej używanych:
Jeśli to możliwe, używaj poleceń wysokiego poziomu Mathematiki, nawet nieporęcznych:
MorphologicalComponents
: Code-Golf: Count Islands
Możliwości manipulacji obrazem: np. Dzisiaj (24 września) są urodziny HONDA
Subsets
IntegerPartitions
Miary odległości i podobieństwa: np. EuclideanDistance
Mogą być oszczędzaniem bajtów. Zauważ jednak, że zwykle krótsze jest pisanie Total@Abs[a-b]
zamiast a~ManhattanDistance~b
i Max@Abs[a-b]
zamiast a~ChessboardDistance~b
.
Użyj Graphics and
Text
do sztuki Ascii: np. Programowanie gwiazd!
i zbuduj zegar analogowy
Dedykowane symbole:
logiczne i ustawiaj symbole operacji zamiast ich długich nazw postaci: ⋂, ⋃, ∧, ∨
Map
i Apply
: /@
, //@
. @@
,@@@
Notacja przedrostka i przyrostka:
Print@"hello"
zamiast Print["hello"]
a~f~b
zamiast f[a,b]
Gdy funkcja jest używana tylko raz, czysta funkcja może oszczędzić jeden lub dwa znaki.
Łączenie ciągów na liście. ""<>{"a","b","c"}
zamiastStringJoin@{"a","b","c"}
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}}
Niektóre wbudowane funkcje o długich nazwach można zastąpić krótszymi wyrażeniami.
Na przykład:
Total
=> Tr
Transpose
=> Thread
lub\[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
=> {}⋃a
luba⋃a
ToExpression@n
=> FromDigits@n
jeśli n
jest liczbąDivisible[n,m]
=> m∣n
FromDigits[n,2]
=> Fold[#+##&,n]
if n
jest listą 0
s i 1
sComplex@z
=> {1,I}.z
gdzie z
jest lista formularza{x,y}
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}}]
Fold
sztuczka FromDigits
dział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 100
i 1000
).
U+F3C7
.
Echo
jest to opcja, ponieważ drukuje >>
(i spację) na STDOUT przed wydrukowaniem rzeczywistego ciągu.
Complex[x,y] => {1,I}.{x,y}
myślę, że x+y*I
jest znacznie krótszy z tym samym efektem?
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~6
w 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 Listable
i 0*x
jest 0
do prawie każdego x
(z wyjątkiem rzeczy takich jak Infinity
i Indeterminate
), więc oto co się dzieje:
0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}
W przypadku list 1
s można użyć podobnej sztuczki, korzystając z reguł potęgowania. Istnieją dwa różne sposoby zapisywania bajtów, jeśli 1
na liście są co najmniej trzy s:
{1,1,1}
1^{,,}
{,,}^0
1^{,,,}
jest o jeden bajt mniejszy niż 0{,,,}+1
.
{,,}^0
. Zmienię post.
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.
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).
Jedną z głównych nowych funkcji językowych w Mathematica 10 są Association
s, 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.
#0
Istnieje mniej znana funkcja, która #0
ró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]]&
Teraz zaczyna się prawdziwa magia. Sekwencje nie są często używane w golfie, ponieważ nazwa Sequence
jest 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 List
na 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 ##1
jest sekwencją wszystkich argumentów. Podobnie ##2
jest sekwencją wszystkich argumentów rozpoczynających się od drugiego, ##3
wszystkich argumentów rozpoczynających się od trzeciego itd. Tak więc na początek możemy po prostu zaimplementować Sequence
jako ##&
, 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+b
Faktycznie 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, Unequal
jeśli masz wartość jednoznakową lub zmienną, o której wiesz, że nie ma jej w argumentach ( N
prawdopodobnie 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!
Sqrt@2
lub 2^.5
=>√2
a[[1]]
=>a〚1〛
#+#2&
=>+##&
Flatten@a
=> Join@@a
(czasami)
Function[x,x^2]
=> xx^2
lub#^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
〚
i 〛
zajmuje 3 bajty każda (zakładamy UTF8)
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 ##&[]·x
jeś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 $CharacterEncoding
ustawienia zgodnej wartości, takiej jak wartość domyślna systemu Windows WindowsANSI
. W niektórych systemach domyślnie UTF-8
nie będzie w stanie odczytać tych punktów kodu z pojedynczych bajtów.
Naiwne podejście do wyboru pomiędzy y
i z
, w zależności od tego, czy x
jest, 0
czy 1
jest
If[x<1,y,z]
Istnieje jednak krótsza droga:
y[z][[x]]
To działa, ponieważ [[0]]
daje Head
wyraż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 u
jest 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 u
a jest liczbą, łańcuchem lub listą.
Kredyty za tę sztuczkę trafiają do alephalpha - odkryłem to w jednej z jego odpowiedzi.
Jeśli x
jest 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]]
Length
Zostało to całkowicie przepisane z kilkoma sugestiami LegionMammal978 i Miszy Ławrow. Wielkie dzięki dla obu z nich.
W wielu przypadkach Length
można go nieco skrócić, korzystając z Tr
. Podstawową ideą jest przekształcenie danych wejściowych w listę 1
s, tak że Tr
sumuje 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 Power
to, ponieważ jest Listable
i 1^n
dla większości wartości atomowych n
jest 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 x
jest to wyrażenie o wyższym priorytecie niż ^
.
Jeśli x
zawiera tylko 0
si 1
, możemy zapisać kolejny bajt przy użyciu Factorial
(zakładając, że x
ma wyższy priorytet niż !
):
Length@x
Tr[x!]
W niektórych rzadkich przypadkach x
moż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 Tr
daje 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
.
Length@x == Tr[1^x]
. Powinien działać z większością list.
Tr[x!]
zamiast Tr[1^x]
zapisywać jeden bajt w specjalnym przypadku, w którym x
zawiera tylko zera i jedynki.
Poznaj rozwiązania rekurencyjne - Mathematica to paradygmat, ale podejście funkcjonalne jest często najbardziej ekonomiczne. NestWhile
może być bardzo zwartym rozwiązaniem problemów z wyszukiwaniem NestWhileList
i FoldList
ma 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.
Skrócona forma inkrementacji / dekrementacji - Na przykład zamiast tego While[i<1,*code*;i++]
możesz zrobićWhile[i++<1,*code*]
Nie zapomnij, że możesz wstępnie zwiększyć / zmniejszyć - na przykład --i
zamiast i--
. Może to czasem zaoszczędzić kilka bajtów w otaczającym kodzie, eliminując operację przygotowawczą.
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 ToExpression
4 razy w roztworze, t=ToExpression
umoż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.
{}
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}
Alternatives
ma 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ż Apply
ma wyższy priorytet niż Alternative
.
W takim przypadku powinieneś po prostu użyć f@@{a,b,c}
.
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 list
s. Przed, prawdopodobnie byłby przypisany SortBy
do s
i czysta funkcja do f
zarzutó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]
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
%
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 %n
moż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=
.
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=...]==a
co 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.
##>0&@@a
. Podobne dla górnej granicy sortowania.
Zamiast StringRepeat[str,n]
używać (0Range[n]+str)<>""
. Lub jeśli str
nie zależy od argumentów dotyczących slotów, jeszcze lepiej jest Array[str&,n]<>""
według tej wskazówki.
StringRepeat[s,n+1]
używać Array[s&,n]<>s
(nawet jeśli masz już n+1
zmienną).
Table[str,n]<>""
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 SortBy
scenariuszach:
Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]
-Sort@-x
?
Możesz wstawić wyrażenie, w Break
któ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 Break
wydaje się być zwracany przez otaczającą pętlę, może potencjalnie prowadzić do jeszcze większej oszczędności.
Aby usunąć wszystkie białe znaki z ciągu s
, użyj
StringSplit@s<>""
To znaczy użyj StringSplit
domyś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"<>""
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.
Array
zamiast tegoPowyższy przykład pokazuje, że użycie Array
ma 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ć Range
np
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ć Array
się w notacji infiksowej, a także zapisać bajt:
#&/@Range@n
#&~Array~n
Dlatego Array
prawie na pewno jest lepszy niż Range
.
Table
zamiast tegoTeraz 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, Table
moż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 Table
nazwa 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, Table
jest 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, Array
w tym przypadku jest również dłuższy, ale wciąż krótszy niż Range
:
(i=#;i&[])&~Array~n
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
f/@Range[x]
regularnie używa ...
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 i
ze i+1
podczas gdy spełnia warunek cond[i]
. W takim przypadku i
zaczyna się od 1
.
Zauważ, że domyślna maksymalna liczba iteracji wynosi 2 ^ 16 (= 65536). Jeśli potrzebujesz więcej iteracji, While
byłoby lepiej. ( MaxIterations->∞
jest za długi)
Czasami możesz zastąpić If
operatorem logicznym.
Załóżmy na przykład, że chcesz utworzyć funkcję, która sprawdza, czy liczba jest liczbą pierwszą, a wypisuje, 2*(number) - 1
jeś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 If
lub gdy potrzebujesz zarówno prawdziwych, jak i fałszywych argumentów If
.
Oto lista z mnóstwem formularzy wejściowych operatora, które mogą skrócić wiele rzeczy. Niektóre z nich zostały wymienione w innych postach, ale lista jest długa i zawsze jestem zaskoczony, gdy znalazłem tam kilka nowych rzeczy:
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 b
spełnia określony warunek.
Gdy nie znaleziono takiego dopasowania, pomija a_
i zamiast tego szuka {p___, b_, q___}
. a
nie jest uwzględniany podczas wyszukiwania i zakłada się, że ma wartość 0
.
Zauważ, że drugie wyszukiwanie wzorca działałoby tylko dla b
tego, które występuje na początku listy; jeśli b
wartość 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, 0
gdy b
spełniony jest warunek na początku listy. (tzn. nie ma potrzeby definiowania oddzielnej reguły {b_, q___} /; cond[b] :> ...
)
W przypadku golfa kodowego czysto Function
argumenty są najczęściej przy użyciu Slot
s; np. #
dla pierwszego argumentu, #2
dla drugiego itd. ( więcej szczegółów znajdziesz w tej odpowiedzi ).
W wielu przypadkach będziesz chciał zagnieździć Function
s. Na przykład 1##&@@#&
jest to, Function
który bierze listę jako swój pierwszy argument i wyprowadza iloczyn swoich elementów. Oto ta funkcja w TreeForm
:
Argumenty przekazywane do poziomu górnej Function
może jedynie wypełnienie Slot
s i SlotSequence
s obecni na najwyższym poziomie, co w tym przypadku oznacza, że SlotSequence
w wewnętrzny Function
nie będzie miał żadnych argumentów sposób dostępu do poziomu górnego Function
.
Jednak w niektórych przypadkach możesz chcieć, aby Function
zagnieżdżony w innym Function
mógł odwoływać się do argumentów zewnętrznych Function
. Na przykład możesz chcieć czegoś takiego Array[fun,...]&
, gdzie funkcja fun
zależy od argumentu na najwyższym poziomie Function
. Dla konkretności, powiedzmy, że fun
resztę 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 x
pojawia 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 Function
ma 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:
xArray[Mod[#^2,x]&,...]
to trzy bajtowy prywatny znak U+F4A1
reprezentujący binarny operator infix \[Function]
. Możesz również użyć postaci binarnej Function
w innym Function
:
Array[xMod[x^2,#],...]&
Jest to równoważne z powyższym. Powodem jest to, że jeśli używasz nazwanych argumentów, to Slot
s i SlotSequences
zakłada się, że należą do następnego Function
powyżej, który nie używa nazwanych argumentów.
Teraz tylko dlatego, że możemy zagnieżdżać Function
w 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[...,xx<#]&
W rzeczywistości byłby krótszy w użyciu Cases
i unikałby potrzeby Function
całkowicie zagnieżdżonego :
Cases[...,x_/;x<#]&
Możesz zapisać bajt, obejście Prepend
lub 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.
BlockMap
jest 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 BlockMap
funkcja. Zasadniczo łączy Partition
i Map
, co jest świetne dla golfistów, ponieważ Partition
jest używany dość często i jest to naprawdę denerwująco długa nazwa funkcji. Nowa funkcja Partition
sama 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 BlockMap
nie obsługuje czwartego parametru, którego można użyć Partition
do 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... *)
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ść l
i używasz go n
razy, zwykle l * n
zużywa bajty.
Jeśli jednak przechowujesz ją w zmiennej o długości 1, zajmie to tylko 3 + l + n
bajty (lub 2 + l + n
bajty, 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 PrimeQ
jest używana trzy razy.
Przypisując PrimeQ
nazwę 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])&]&
Sort
zamiastSortBy
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
Select
iSortBy
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/0
jest 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]
Array
z##&
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) Sequence
wynikó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
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, c
gdy 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 Times
w c_.*x
i Brak wartości c_
w ten sposób pobierana z domyślnej wartości z Times
, co 1. Plus
domyślna wartość od 0:
Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(* f[0] + f[2] *)
W przypadku Power
wykładników wartość domyślna to 1:
x + x^2 /. x^n_. -> p[n]
(* p[1] + p[2] *)
(Norm[#-#2]&)
niżEuclideanDistance
.