Apa tips umum yang Anda miliki untuk bermain golf di Mathematica? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk Mathematica (mis. "Hapus komentar" bukan jawaban).
Apa tips umum yang Anda miliki untuk bermain golf di Mathematica? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk Mathematica (mis. "Hapus komentar" bukan jawaban).
Jawaban:
Kiat di bawah ini bervariasi dari yang paling ekonomis hingga yang paling sering digunakan:
Gunakan perintah tingkat tinggi Mathematica jika memungkinkan, bahkan yang besar:
MorphologicalComponents
: Code-Golf: Count Islands
Kemampuan manipulasi gambar: mis. Hari ini (24 September) adalah ulang tahun HONDA
Subsets
IntegerPartitions
Ukuran Jarak dan Kesamaan: misalnya EuclideanDistance
bisa menjadi byte saver. Namun perlu dicatat, bahwa biasanya lebih pendek untuk menulis Total@Abs[a-b]
daripada a~ManhattanDistance~b
dan Max@Abs[a-b]
bukan a~ChessboardDistance~b
.
Gunakan Graphics and
Text
untuk seni Ascii: mis. Pemrograman bintang!
dan Bangun jam analog
Simbol khusus:
logika dan tetapkan simbol operasi alih-alih nama panjangnya: ⋂, ⋃, ∧, ∨
Map
dan Apply
: /@
, //@
. @@
,@@@
Awalan dan infiks notasi:
Print@"hello"
di tempat Print["hello"]
a~f~b
di tempat f[a,b]
Ketika suatu fungsi hanya digunakan sekali, fungsi murni dapat menghemat satu atau dua karakter.
Menggabungkan string dalam daftar. ""<>{"a","b","c"}
dari padaStringJoin@{"a","b","c"}
Memanfaatkan fungsi yang dapat didaftarkan. Semakin lama daftar semakin baik.
{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}}
Beberapa fungsi bawaan dengan nama panjang dapat diganti dengan ekspresi yang lebih pendek.
Sebagai contoh:
Total
=> Tr
Transpose
=> Thread
atau\[Transpose]
True
=> 1<2
False
=> 1>2
Times
=> 1##&
Alternatives
=> $|##&
IntegerQ
=> ⌊#⌋==#&
a[[1]]
=> #&@@a
a[[All,1]]
=> #&@@@a
ConstantArray[a,n]
=> Array[a&,n]
atauTable[a,{n}]
Union@a
=> {}⋃a
ataua⋃a
ToExpression@n
=> FromDigits@n
jika n
adalah angkaDivisible[n,m]
=> m∣n
FromDigits[n,2]
=> Fold[#+##&,n]
jika n
daftar 0
s dan 1
sComplex@z
=> di {1,I}.z
mana z
daftar formulir{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
trik Anda untuk FromDigits
juga berfungsi untuk pangkalan lain kecuali 10
. Misalnya FromDigits[n,5]
-> Fold[4#+##&,n]
(dengan bonus menyimpan byte tambahan untuk basis 100
dan 1000
).
U+F3C7
.
Echo
itu pilihan, karena ia mencetak >>
(dan spasi) untuk STDOUT sebelum mencetak string yang sebenarnya.
Complex[x,y] => {1,I}.{x,y}
, saya pikir x+y*I
jauh lebih pendek dengan efek yang sama?
Ini adalah vektor yang cukup umum untuk digunakan:
{0,0}
Ternyata ini dapat disingkat dengan byte:
0{,}
Bahkan lebih banyak byte disimpan jika vektor lebih panjang dari dua nol. Ini juga dapat digunakan untuk menginisialisasi nol matriks, misalnya yang berikut ini memberikan matriks 2x2 nol:
0{{,},{,}}
Ini juga dapat digunakan untuk nilai-nilai non-nol jika cukup besar atau cukup banyak atau negatif. Bandingkan pasangan berikut:
{100,100}
0{,}+100
{-1,-1}
0{,}-1
{3,3,3,3}
0{,,,}+3
Tetapi ingat bahwa mulai dari 6 nilai, Anda lebih baik 1~Table~6
dalam hal ini (berpotensi lebih awal, tergantung pada persyaratan diutamakan).
Alasan ini berhasil adalah yang ,
memperkenalkan dua argumen ke daftar, tetapi argumen yang dihilangkan (di mana saja di Mathematica) adalah Null
s implisit . Selanjutnya, perkalian Listable
, dan 0*x
merupakan 0
untuk hampir semua x
(kecuali untuk hal-hal seperti Infinity
dan Indeterminate
), jadi di sini adalah apa yang terjadi:
0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}
Untuk daftar 1
s, Anda dapat menggunakan trik serupa dengan memanfaatkan aturan eksponensial. Ada dua cara berbeda untuk menghemat byte jika Anda memiliki setidaknya tiga 1
s dalam daftar:
{1,1,1}
1^{,,}
{,,}^0
1^{,,,}
satu byte lebih kecil dari 0{,,,}+1
.
{,,}^0
. Saya akan mengedit posting.
Saat kode golf, Anda akan sering menggunakan pendekatan fungsional, di mana Anda menggunakan fungsi anonim (murni) dengan &
sintaks steno. Ada banyak cara berbeda untuk mengakses argumen dari fungsi seperti itu, dan Anda sering dapat memotong beberapa byte dengan memiliki pemahaman yang baik tentang kemungkinan.
Anda mungkin tahu ini jika Anda pernah menggunakan fungsi murni sebelumnya. The n Argumen th disebut sebagai #n
, dan #
bertindak sebagai alias untuk #1
. Jadi jika, katakanlah, Anda ingin menulis fungsi yang mengambil parameter fungsi lain dan argumennya (untuk meneruskan argumen ke fungsi itu), gunakan
#@#2&
Ini tidak berfungsi dengan angka negatif (seperti yang mungkin Anda gunakan saat mengakses daftar).
Salah satu fitur utama bahasa baru di Mathematica 10 adalah Association
s, yang pada dasarnya adalah peta nilai-kunci dengan jenis kunci acak, ditulis seperti
<| x -> 1, "abc" -> 2, 5 -> 3 |>
Jika asosiasi seperti itu diteruskan sebagai argumen pertama ke fungsi murni, Anda dapat mengakses beberapa jika argumennya sebagai parameter bernama:
{#, #2, #3, #abc, #xyz} & [<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th"]
(* {<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th", "1st", "2nd"} *)
Perhatikan bahwa #
masih merujuk ke seluruh asosiasi seperti yang diharapkan. Agar parameter yang dinamai berfungsi, kunci harus berupa string (tidak akan berfungsi jika Anda menggunakan variabel yang tidak ditentukan misalnya), dan string tersebut harus dimulai dengan huruf dan hanya berisi huruf dan angka.
#0
Fitur yang kurang dikenal adalah yang #0
juga ada, dan memberi Anda objek fungsi itu sendiri. Ini bisa sangat berguna dalam quines dan quines umum. Bahkan, Quine Mathematica terpendek (saya tahu) adalah
ToString[#0][] & []
Yang sedikit menjengkelkan adalah bahwa itu tidak akan memberi Anda karakter tepat yang Anda masukkan. Misalnya jika digunakan @
untuk aplikasi fungsi, masih akan di-render [...]
dan spasi akan dimasukkan di beberapa tempat. Ini biasanya akan membuat quine sedikit lebih lama dari yang Anda inginkan, tetapi akan selalu berhasil, dengan mem-golf quine terlebih dahulu, dan kemudian hanya menyalin outputnya - yang sekarang seharusnya menjadi quine nyata.
Terlepas dari quines, ini juga berarti bahwa Anda dapat menulis kode rekursif tanpa harus menyebutkan nama fungsi Anda. Bandingkan tiga implementasi Fibonacci (naif tapi golf) ini:
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]]&
Sekarang di sinilah keajaiban yang sebenarnya dimulai. Urutan tidak sering digunakan dalam bermain golf, karena nama Sequence
itu terlalu panjang untuk menjadi layak untuk sebagian besar waktu. Tetapi dalam fungsi murni adalah di mana mereka bersinar. Jika Anda tidak terbiasa dengan urutan, mereka pada dasarnya seperti percikan dalam beberapa bahasa lain, jika Anda menggunakan urutan dalam List
atau daftar argumen fungsi, elemen-elemennya akan secara otomatis diperluas ke slot yang terpisah. Begitu
{1, Sequence[2, 3, 4], 5} == {1, 2, 3, 4, 5}
f["a", Sequence[0, {}], "b"] == f["a", 0, {}, "b"]
Sekarang, dalam fungsi murni ##
atau ##1
merupakan urutan dari semua argumen. Demikian juga, ##2
merupakan urutan semua argumen mulai dari kedua, ##3
semua argumen mulai dari dll Jadi ketiga untuk memulai, kita hanya bisa reimplement Sequence
sebagai ##&
, tabungan 5 byte. Sebagai contoh penggunaan, ini memberi kami alternatif untuk Join@@list
(lihat tip ini ), yang tidak menyimpan byte apa pun, tetapi bagaimanapun juga baik untuk diketahui:
##&@@@list
Ini secara efektif meratakan tingkat pertama dari daftar bersarang. Apa lagi yang bisa kita lakukan dengan ini? Berikut adalah alternatif 2 byte lebih pendek untuk RotateLeft
:
RotateLeft@list
{##2,#}&@list
Untuk hal-hal ini saja, ada baiknya mengingat fitur ini. Namun, kita bisa berbuat lebih baik! Urutan menjadi sangat menarik ketika mempertimbangkan bahwa operator sebenarnya diimplementasikan sebagai fungsi di bawah kap. Misalnya a+b
sebenarnya mengevaluasi Plus[a,b]
. Jadi jika kita memberi urutan ...
1+##&[1,2,3]
=> Plus[1,##]
=> Plus[1,1,2,3]
=> 7
Trik ini telah digunakan dalam tip ini untuk menghemat byte Times
, karena penjajaran secara teknis juga hanya operator:
1##&[1,2,3]
=> Times[1,##]
=> Times[1,1,2,3]
=> 6
Anda juga dapat menggunakannya untuk menyimpan byte Unequal
jika Anda memiliki nilai karakter tunggal atau variabel yang Anda tahu tidak ada dalam argumen Anda ( N
mungkin akan bekerja di 99% kasus):
Unequal[a,b,c]
N!=##&[a,b,c]
Ini menjadi lebih menarik dengan operator yang tidak dikenal dan -
dan /
- dua yang terakhir sebenarnya diimplementasikan dalam hal penggandaan dan eksponensial. Berikut adalah daftar hal-hal yang dapat Anda lakukan, di mana kolom terakhir mengasumsikan bahwa fungsi telah melewati argumen 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
Operator umum lainnya adalah !=
, ==
, &&
, ||
. Yang kurang umum untuk diingat adalah |
, @*
, /*
. Untuk menyimpulkan, berikut adalah trik bonus kecil:
#### Times[##,##] Times[a,b,c,a,b,c] (a*b*c)^2
Terus bereksperimen dengan ini, dan beri tahu saya jika Anda menemukan aplikasi lain yang bermanfaat atau sangat menarik!
Sqrt@2
atau 2^.5
=>√2
a[[1]]
=>a〚1〛
#+#2&
=>+##&
Flatten@a
=> Join@@a
(terkadang)
Function[x,x^2]
=> xx^2
atau#^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
〚
dan 〛
mengambil masing-masing 3 byte (asumsikan UTF8)
Terinspirasi oleh penemuan Dennis untuk Julia baru-baru ini, saya pikir saya akan mencari di dalam untuk Mathematica. Saya sadar bahwa Mathematica mendefinisikan sejumlah besar operator yang tidak digunakan, tetapi tidak pernah memperhatikannya.
Untuk referensi, daftar semua operator dapat ditemukan di sini dalam bentuk tabel prioritas. Segitiga pada kolom terakhir menunjukkan apakah operator memiliki makna bawaan atau tidak. Meskipun tidak semua dari mereka yang tidak dapat didefinisikan dengan mudah, kebanyakan dari mereka bisa.
Mudahnya, ada dua operator yang tidak digunakan dengan codepoint kurang dari 256, sehingga mereka dapat digunakan sebagai byte tunggal dalam file sumber yang disandikan ISO 8859-1:
±
(0xB1) dapat digunakan sebagai operator awalan unary atau operator infiks biner.·
(0xB7) dapat digunakan sebagai operator infiks variadic atau n-ary, untuk n> 2.Namun ada satu lagi tangkapan: untuk beberapa alasan aneh ketika mendefinisikan operator ini Anda perlu satu ruang di depan mereka, atau Mathematica mencoba mengurai perkalian. Saat menggunakannya, Anda tidak memerlukan spasi:
±x_:=2x
x_ ±y_:=x+y
x_ ·y_ ·z_:=x*y+z
Print[±5] (* 10 *)
Print[3±4] (* 7 *)
Print[3·4·5] (* 17 *)
Bandingkan ini dengan:
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 *)
Jadi ini menghemat satu byte saat mendefinisikan fungsi dan dua byte saat menggunakannya. Perhatikan bahwa definisi ·
tidak akan menghemat byte untuk empat operan dan akan mulai menentukan biaya byte untuk lebih banyak operan, tetapi penggunaannya mungkin masih menyimpan byte, tergantung pada prioritas operator yang digunakan dalam argumen. Ini juga baik untuk dicatat bahwa Anda dapat dengan murah mendefinisikan fungsi variadic yang kemudian dapat disebut lebih efisien:
x_ ·y__:={y}
Print[1·2·3·4·5] (* {2, 3, 4, 5} *)
Tetapi perhatikan bahwa tidak mudah untuk memanggil fungsi variadic ini dengan satu argumen. (Anda bisa melakukannya CenterDot[x]
atau ##&[]·x
tetapi jika Anda benar-benar membutuhkannya, ada peluang bagus Anda lebih baik dengan solusi yang berbeda.)
Tentu saja, ini tidak menyimpan apa pun untuk solusi di mana fungsi yang tidak disebutkan namanya mencukupi, tetapi kadang-kadang Anda perlu mendefinisikan fungsi pembantu untuk digunakan nanti, dan kadang-kadang lebih pendek untuk menentukan fungsi yang disebutkan misalnya untuk mengatur definisi yang berbeda untuk parameter yang berbeda. Dalam kasus tersebut, menggunakan operator sebagai gantinya dapat menghemat jumlah byte yang layak.
Perhatikan bahwa menggunakan file yang disandikan ISO 8859-1 ini $CharacterEncoding
harus diatur ke nilai yang kompatibel, seperti standar Windows WindowsANSI
. Pada beberapa sistem, default ini UTF-8
tidak dapat membaca poin kode ini dari satu byte.
Pendekatan naif untuk memilih antara y
dan z
, tergantung pada apakah x
adalah 0
atau 1
merupakan
If[x<1,y,z]
Namun, ada cara yang lebih singkat:
y[z][[x]]
Ini berfungsi karena [[0]]
memberikan Head
ekspresi, dalam hal ini y
, sedangkan [[1]]
hanya memberikan elemen pertama - dalam hal ini argumen pertama z
,.
Anda bahkan dapat menggunakan ini untuk memilih antara lebih dari dua nilai:
u[v,w][[x]]
Perhatikan bahwa ini tidak akan berfungsi jika u
fungsi yang sebenarnya mengevaluasi sesuatu. Penting agar Mathematica tetap u[v,w]
seperti itu. Namun, ini berfungsi dalam kebanyakan kasus, termasuk jika u
adalah angka, string atau daftar.
Penghargaan untuk trik ini diberikan ke alephalpha - Saya menemukan ini dalam salah satu jawabannya.
Jika x
berbasis 1 dan bukan berbasis nol, gunakan saja
{y,z}[[x]]
atau
{u,v,w}[[x]]
Dalam beberapa kasus yang jarang terjadi, Anda bahkan dapat menggunakan fakta bahwa perkalian tidak dievaluasi untuk beberapa nilai:
{"abc","def"}[[x]]
("abc""def")[[x]]
Catat bahwa Mathematica sebenarnya akan menyusun ulang argumen, dari perkalian jika tetap tidak dievaluasi, sehingga hal di atas identik dengan
("def""abc")[[x]]
Length
Ini sepenuhnya ditulis ulang dengan beberapa saran dari LegionMammal978 dan Misha Lavrov. Terima kasih banyak untuk mereka berdua.
Dalam banyak kasus, Length
dapat dipersingkat sedikit dengan memanfaatkan Tr
. Ide dasarnya adalah mengubah input menjadi daftar 1
s, sehingga Tr
meringkasnya yang kemudian akan sama dengan panjang daftar.
Cara paling umum untuk melakukan ini adalah menggunakan 1^x
(untuk daftar x
). Ini bekerja karena Power
merupakan Listable
dan 1^n
untuk sebagian besar nilai-nilai atom n
hanya 1
(termasuk semua nomor, string dan simbol). Jadi kita sudah bisa menyimpan satu byte dengan ini:
Length@x
Tr[1^x]
Tentu saja, ini mengasumsikan bahwa x
ini adalah ekspresi dengan prioritas lebih tinggi daripada ^
.
Jika x
hanya berisi 0
s dan 1
s, kita dapat menyimpan byte lain menggunakan Factorial
(dengan asumsi x
memiliki prioritas lebih tinggi daripada !
):
Length@x
Tr[x!]
Dalam beberapa kasus yang jarang terjadi, x
mungkin memiliki prioritas lebih rendah daripada ^
tetapi masih lebih tinggi daripada multiplikasi. Dalam hal ini juga akan memiliki prioritas lebih rendah daripada @
, jadi kita benar-benar perlu membandingkan Length[x]
. Contoh dari operator tersebut adalah .
. Dalam kasus tersebut, Anda masih dapat menyimpan byte dengan formulir ini:
Length[x.y]
Tr[0x.y+1]
Akhirnya, beberapa komentar tentang jenis daftar ini berfungsi:
Seperti disebutkan di atas, ini berfungsi pada daftar datar yang hanya berisi angka, string, dan simbol. Namun, ini juga akan bekerja pada beberapa daftar yang lebih dalam, meskipun sebenarnya menghitung sesuatu yang sedikit berbeda. Untuk array n -D persegi panjang, menggunakan Tr
memberi Anda dimensi terpendek (bukan yang pertama). Jika Anda tahu bahwa dimensi terluar adalah yang terpendek, atau Anda tahu mereka semua sama, daripada Tr
-ekspresi masih setara Length
.
Length@x == Tr[1^x]
. Harus bekerja dengan sebagian besar daftar.
Tr[x!]
alih-alih Tr[1^x]
menyimpan satu byte dalam kasus khusus di mana x
hanya berisi nol dan satu.
Jelajahi solusi rekursif - Mathematica adalah multi-paradigma, tetapi pendekatan fungsional sering kali paling ekonomis. NestWhile
bisa menjadi solusi yang sangat kompak untuk mencari masalah, NestWhileList
dan FoldList
sangat kuat ketika Anda perlu mengembalikan atau memproses hasil iterasi menengah. Map (/@)
, Apply (@@, @@@)
, MapThread
, Dan benar-benar segala sesuatu di Wolfram Pemrograman Fungsional halaman dokumentasi adalah hal yang ampuh.
Formulir singkat untuk kenaikan / penurunan - Misalnya, alih-alih While[i<1,*code*;i++]
yang dapat Anda lakukanWhile[i++<1,*code*]
Jangan lupa Anda bisa melakukan pra-kenaikan / penurunan - Misalnya, --i
alih-alih i--
. Ini kadang-kadang dapat menghemat beberapa byte dalam kode di sekitarnya dengan menghilangkan operasi persiapan.
Akibat wajar terhadap David Carraher's # 5: Ketika fungsi yang sama digunakan berkali-kali, menetapkan simbol untuk itu dapat menghemat byte. Misalnya, jika Anda menggunakan ToExpression
4 kali dalam suatu solusi, t=ToExpression
memungkinkan Anda untuk menggunakannya t@*expression*
setelahnya. Namun, sebelum Anda melakukan ini pertimbangkan apakah aplikasi berulang dari fungsi yang sama menunjukkan peluang untuk pendekatan rekursif yang lebih ekonomis.
{}
jika Anda menggunakan @@@
.Dalam beberapa kasus, Anda mungkin menemukan ekspresi seperti:
f@@@{{a,b},{c,d}}
Dimungkinkan untuk mengurangi byte dengan menulis:
f@@@{a|b,c|d}
Alternatives
memiliki prioritas yang sangat rendah, jadi biasanya tidak apa-apa untuk menulis ekspresi (pengecualian penting adalah fungsi murni; Anda dapat menggunakannya hanya di elemen paling kiri Alternatives
).
f@@@{f@a|b~g~1,#^2&@c|d@2}
Perhatikan bahwa f@@a|b|c
(bukannya f@@{a,b,c}
) tidak berfungsi karena Apply
memiliki prioritas lebih tinggi daripada Alternative
.
Dalam hal ini, Anda cukup menggunakan f@@{a,b,c}
.
Formulir operator
Mathematica 10 mendukung apa yang disebut "bentuk-bentuk operator", yang pada dasarnya berarti beberapa fungsi dapat dijelajahi. Currying suatu fungsi adalah membuat fungsi baru dengan memperbaiki salah satu operatornya. Katakanlah, Anda menggunakan SortBy[list, somereallylongfunction&]
banyak list
s berbeda . Sebelumnya, Anda mungkin akan ditugaskan SortBy
untuk s
dan fungsi murni untuk f
jadi
s=SortBy;
f=somereallylongfunction&;
list1~s~f;
list2~s~f;
list3~s~f;
Sekarang Anda bisa menjilat SortBy
, yang artinya sekarang bisa Anda lakukan
s=SortBy[somereallylongfunction&];
s@list1;
s@list2;
s@list3;
Karya-karya yang sama untuk banyak fungsi lainnya, yang mengambil daftar atau fungsi argumen, termasuk (namun tidak terbatas pada) Select
, Map
, Nearest
, dll
ybeltukov ke atas di Mathematica.SE mampu menghasilkan daftar lengkap ini :
{"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"}
Komposisi dan Komposisi Kanan
Ada singkatan baru untuk Composition
( @*
) dan RightComposition
( /*
). Contoh yang jelas dibuat di mana ini dapat menyimpan karakter terlihat dalam tiga baris setara berikut
Last@Range@# & /@ Range[5]
Last@*Range /@ Range[5]
Range /* Last /@ Range[5]
Tidak perlu kode seperti ini:
f[]:=DoSomething[1,2]
(*...*)
f[]
(*...*)
f[]
Anda cukup menggunakan variabel dengan :=
untuk memaksa evaluasi ulang dari sisi kanan:
f:=DoSomething[1,2]
(*...*)
f
(*...*)
f
Ini juga berarti bahwa Anda dapat alias tindakan apa pun yang sering Anda lakukan (bahkan jika itu hanya sesuatu seperti n++
) ke satu karakter dengan biaya 5 byte. Jadi dalam kasus n++
itu membayar kembali setelah penggunaan keempat:
n++;n++;n++;n++
f:=n++;f;f;f;f
%
untuk mendapatkan variabel gratisTip ini hanya berlaku jika lingkungan REPL Mathematica dapat diasumsikan. %
tidak ditentukan ketika kode dijalankan sebagai skrip.
Saat Anda dapat menggunakan fitur REPL, jangan lakukan ini:
a=someLongExpression;some[other*a,expression@a,using^a]
Sebagai gantinya, ingatlah bahwa Mathematica menyimpan ekspresi yang terakhir dievaluasi (diakhiri baris) di %
:
someLongExpression;
some[other*%,expression@%,using^%]
Baris baru yang ditambahkan biaya satu byte, tetapi Anda menyimpan dua dengan menghapus a=
, jadi secara keseluruhan ini menghemat satu byte.
Dalam beberapa kasus (mis. Ketika Anda ingin mencetak nilai a
toh), Anda bahkan dapat mengabaikannya ;
, menghemat dua byte:
someLongExpression
some[other*%,expression@%,using^%]
Satu atau dua byte mungkin terlihat agak kecil, tetapi ini merupakan kasus penting, karena itu membuat ekstraksi ekspresi berulang (yang merupakan teknik yang sangat umum) jauh lebih berguna ketika bermain golf:
Teknik normal mengekstraksi ekspresi berulang menghabiskan empat byte overhead, yang perlu dihemat dengan penggunaan ekspresi lebih lanjut. Berikut adalah tabel pendek dari jumlah minimum penggunaan ekspresi (dengan panjang ekspresi) untuk ekstraksi ke variabel bernama untuk menyimpan apa pun:
Length Min. Uses
2 6
3 4
4 3
5 3
6 2
... 2
Dengan menggunakan variabel yang tidak disebutkan namanya, akan lebih mungkin untuk menyimpan beberapa byte lebih sering:
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
Saya tidak berpikir %%
atau tidak %n
bisa digunakan untuk bermain golf, karena jika Anda tidak menggunakannya setidaknya dua kali, Anda bisa meletakkan ekspresi tepat di tempat yang dibutuhkan. Dan jika Anda menggunakannya dua kali, karakter tambahan dalam nama variabel membatalkan penghematan dari menghilangkan beberapa x=
.
Ini pada dasarnya adalah akibat wajar dari tip ini, tetapi ini adalah tugas yang cukup umum yang saya pikir itu menjamin jawabannya sendiri.
Cara naif untuk memeriksa apakah suatu daftar harus digunakan
OrderedQ@a
Kita dapat melakukan satu byte dengan lebih baik
Sort@a==a
Namun, ini tidak berfungsi jika kita belum memiliki hal yang ingin kita periksa di variabel. (Kami membutuhkan sesuatu Sort[a=...]==a
yang panjangnya tidak perlu.) Namun, ada opsi lain:
#<=##&@@a
Yang terbaik adalah ini dapat digunakan untuk memeriksa apakah input diurutkan terbalik untuk jumlah byte yang sama:
#>=##&@@a
Satu byte lagi dapat disimpan jika a) kita tahu bahwa elemen daftar berbeda dan b) kita tahu batas bawah antara 0 dan 9 (termasuk; atau batas atas untuk urutan diurutkan terbalik):
0<##&@@a
5>##&@@a
Untuk mengetahui mengapa ini berhasil, lihat "Urutan argumen" di ujung yang ditautkan di atas.
##>0&@@a
. Mirip untuk batas atas untuk diurutkan.
Alih-alih StringRepeat[str,n]
digunakan (0Range[n]+str)<>""
. Atau jika str
tidak bergantung pada argumen slot mana pun, bahkan lebih baik Array[str&,n]<>""
sesuai tip ini .
StringRepeat[s,n+1]
digunakan Array[s&,n]<>s
(bahkan saat Anda sudah memiliki n+1
variabel juga).
Table[str,n]<>""
Jika Anda membutuhkan daftar angka yang diurutkan secara terbalik, jangan gunakan
Reverse@Sort@x
tapi
-Sort@-x
untuk menghemat enam byte. Menyortir berdasarkan nilai negatif juga berguna untuk SortBy
skenario:
Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]
-Sort@-x
?
Anda dapat menempel ekspresi di Break
mana dapat menyimpan satu atau dua karakter. Contoh ( detail lain yang tidak di-golf untuk kejelasan ):
result = False;
Break[]
bisa diubah menjadi
Break[result = False]
untuk menyimpan satu karakter. Jika ekspresi yang dipermasalahkan tidak memiliki prioritas lebih rendah dari aplikasi fungsi, Anda bahkan dapat menyimpan karakter lain:
Print@x;
Break[]
bisa diubah menjadi
Break@Print@x
Meskipun tidak berdokumen, argumen untuk Break
tampaknya dikembalikan oleh lingkaran di sekitarnya, yang berpotensi dapat menyebabkan penghematan lebih banyak lagi.
Untuk menghapus semua spasi putih dari string s
, gunakan
StringSplit@s<>""
Artinya, gunakan StringSplit
default (dipecah menjadi komponen non-spasi putih) dan cukup bergabung kembali. Hal yang sama mungkin masih terpendek jika Anda ingin menghilangkan karakter atau substring lain:
s~StringSplit~"x"<>""
Range
Tugas yang sangat umum adalah menerapkan semacam fungsi pada semua angka dari 1 hingga a n
(biasanya diberikan sebagai input). Pada dasarnya ada 3 cara untuk melakukan ini (menggunakan fungsi identitas yang tidak disebutkan namanya sebagai contoh):
#&/@Range@n
Array[#&,n]
Table[i,{i,n}]
Saya cenderung memilih yang pertama (untuk alasan apa pun), tetapi ini jarang merupakan pilihan terbaik.
Array
sebagai gantinyaContoh di atas menunjukkan bahwa menggunakan Array
memiliki jumlah byte yang sama. Namun, itu memiliki keuntungan bahwa itu adalah ekspresi tunggal. Secara khusus, jika Anda ingin memproses lebih lanjut hasil dengan fungsi f
Anda dapat menggunakan notasi awalan, yang menyimpan byte lebih dari Range
:
f[#&/@Range@n]
f@Array[#&,n]
Selain itu, Anda mungkin dapat menghilangkan tanda kurung di sekitar fungsi yang tidak disebutkan namanya yang mungkin Anda perlukan Range
, misalnya
15/(#&)/@Range@n
15/Array[#&,n]
Jika Anda tidak ingin menggunakannya lebih lanjut (atau dengan operator yang memiliki prioritas lebih rendah), Anda dapat menulis Array
sendiri dalam notasi infiks dan juga menyimpan byte:
#&/@Range@n
#&~Array~n
Karenanya, Array
hampir pasti lebih baik daripadaRange
.
Table
sebagai gantinyaSekarang tabel harus menebus 3 byte, atau setidaknya 2 ketika notasi infiks adalah pilihan:
#&/@Range@n
i~Table~{i,n}
Saat tidak menggunakan notasi infiks, Table
mungkin Anda dapat menghilangkan tanda kurung jika fungsi Anda terdiri dari beberapa pernyataan:
(#;#)&/@Range@n
Table[i;i,{i,n}]
Ini masih lebih lama, tetapi memberikan penghematan ekstra dengan dalam kasus yang disebutkan di bawah ini.
Tabungan nyata berasal dari fakta yang Table
memberi nama variabel berjalan tidak boleh diabaikan. Seringkali, Anda akan memiliki fungsi yang tidak disebutkan namanya yang bersarang di mana Anda ingin menggunakan variabel luar di dalam salah satu fungsi bagian dalam. Ketika itu terjadi, Table
lebih pendek dari Range
:
(i=#;i&[])&/@Range@n
Table[i&[],{i,n}]
i&[]~Table~{i,n}
Anda tidak hanya menyimpan karakter untuk ditugaskan i
, Anda mungkin juga dapat mengurangi fungsi menjadi satu pernyataan dalam proses, yang memungkinkan Anda untuk menggunakan notasi infiks di atasnya. Sebagai referensi, Array
juga lebih lama dalam hal ini, tetapi masih lebih pendek dari Range
:
(i=#;i&[])&~Array~n
Range
?Kapan pun Anda tidak membutuhkan pemanggilan fungsi untuk memproses nilai-nilai, misalnya saat pemetaan dapat dilakukan melalui operasi yang di-vektor. Contohnya:
5#&~Array~n
5Range@n
#^2&~Array~n
Range@n^2
Tentu saja, ini juga lebih pendek jika Anda tidak ingin memetakan fungsi apa pun, misalnya
Mean@Array[#&,n]
Mean@Range@n
f/@Range[x]
secara teratur ...
Beberapa konstruk like i=1;While[cond[i],i++]
tidak apa-apa, tetapi ada alternatif yang lebih pendek dua byte:
1//.i_/;cond[i]:>i+1
Kode di atas berulang kali menggantikan nomor i
dengan i+1
sementara itu memenuhi kondisi cond[i]
. Dalam hal ini, i
mulai dari 1
.
Perhatikan bahwa jumlah iterasi maksimum default adalah 2 ^ 16 (= 65536). Jika Anda membutuhkan lebih banyak iterasi daripada itu, While
akan lebih baik. ( MaxIterations->∞
terlalu panjang)
Anda terkadang dapat mengganti If
dengan operator yang logis.
Misalnya, katakanlah Anda ingin membuat fungsi yang memeriksa apakah suatu bilangan prima, dan cetak 2*(number) - 1
adalah jika benar:
If[PrimeQ@#,Print[2#-1]]&
Lebih pendek jika Anda menggunakan &&
sebagai gantinya:
PrimeQ@#&&Print[2#-1]&
Bahkan ketika Anda memiliki banyak ekspresi, Anda masih menyimpan byte:
If[PrimeQ@#,a++;Print[2#-1]]&
PrimeQ@#&&a++&&Print[2#-1]&
(* or *)
PrimeQ@#&&(a++;Print[2#-1])&
Anda dapat menggunakan ||
kasus-kasus ketika Anda menginginkan kondisinya False
:
If[!PrimeQ@#,Print[2#-1]]&
(* or *)
If[PrimeQ@#,,Print[2#-1]]&
(* can become *)
PrimeQ@#||Print[2#-1]&
Trik ini berfungsi karena operator logis dapat mengalami hubungan pendek ; argumen kedua dan sesudahnya bahkan tidak perlu menjadi ekspresi boolean yang valid.
Tentu saja, ini tidak berfungsi jika Anda membutuhkan nilai balik If
atau ketika Anda membutuhkan argumen yang benar dan salah If
.
Berikut adalah daftar dengan banyak formulir input operator yang dapat mempersingkat banyak hal. Beberapa di antaranya telah disebutkan di posting lain, tetapi daftarnya panjang dan saya selalu terkejut menemukan beberapa hal baru di sana:
Optional (:)
Optional (:)
dapat digunakan untuk memperluas daftar dalam penggantian, tanpa harus menetapkan aturan terpisah untuk ekspansi.
Jawaban ini oleh saya dan jawaban ini oleh @ngenisis adalah contoh.
Pemakaian
... /. {p___, a_: 0, b_, q___} /; cond[b] :> ...
Penggantian di atas pertama menggunakan pola {p___, a_, b_, q___}
dan menemukan kecocokan sedemikian sehingga b
memenuhi kondisi tertentu.
Ketika tidak ada kecocokan seperti itu ditemukan, itu menghilangkan a_
dan malah mencari {p___, b_, q___}
. a
tidak termasuk dalam pencarian dan dianggap memiliki nilai 0
.
Perhatikan bahwa pencarian pola kedua hanya akan berfungsi untuk b
itu terjadi pada awal daftar; jika b
nilai yang memuaskan suatu kondisi berada di tengah, maka {p___, a_, b_, q___}
(yang memiliki prioritas lebih tinggi) akan cocok dengan itu sebagai gantinya.
Penggantiannya sama dengan mengawali a 0
ketika kondisi yang b
memuaskan terjadi di awal daftar. (yaitu tidak perlu mendefinisikan aturan yang terpisah, {b_, q___} /; cond[b] :> ...
)
Untuk golf kode, Function
argumen murni paling sering dirujuk menggunakan Slot
s; misalnya #
untuk argumen pertama, #2
untuk argumen kedua, dll. (lihat jawaban ini untuk lebih jelasnya).
Dalam banyak kasus, Anda ingin bersarang Function
. Sebagai contoh, 1##&@@#&
adalah Function
yang mengambil daftar sebagai argumen pertama dan mengeluarkan produk dari elemen-elemennya. Inilah fungsi di TreeForm
:
Argumen yang diteruskan ke tingkat atas Function
hanya dapat mengisi hadir Slot
dan SlotSequence
s di tingkat atas, yang dalam hal ini berarti bahwa SlotSequence
di bagian dalam Function
tidak akan ada cara mengakses argumen ke tingkat atas Function
.
Dalam beberapa kasus, meskipun, Anda mungkin ingin Function
bersarang di dalam yang lain Function
untuk dapat referensi argumen ke luar Function
. Misalnya, Anda mungkin menginginkan sesuatu seperti Array[fun,...]&
, di mana fungsinya fun
tergantung pada argumen ke tingkat atas Function
. Untuk konkret, katakanlah yang fun
seharusnya memberikan sisa kuadrat dari input modulo input ke level atas Function
. Salah satu cara untuk mencapai ini adalah dengan menetapkan argumen tingkat atas ke variabel:
(x=#;Array[Mod[#^2,x]&,...])&
Di mana pun x
muncul di dalam Function
Mod[#^2,x]&
, itu akan merujuk pada argumen pertama ke luar Function
, sedangkan #
akan merujuk pada argumen pertama ke dalam Function
. Pendekatan yang lebih baik adalah dengan menggunakan fakta yang Function
memiliki bentuk dua argumen di mana argumen pertama adalah simbol atau daftar simbol yang akan mewakili argumen bernama ke Function
(sebagai lawan dari Slot
s tanpa nama ). Ini akhirnya menghemat kita tiga byte dalam kasus ini:
xArray[Mod[#^2,x]&,...]
adalah karakter penggunaan pribadi tiga byte yang U+F4A1
mewakili operator infus biner \[Function]
. Anda juga dapat menggunakan bentuk biner dari yang Function
lain Function
:
Array[xMod[x^2,#],...]&
Ini sama dengan di atas. Alasannya adalah, jika Anda menggunakan argumen bernama, maka Slot
s dan SlotSequences
dianggap milik berikutnya di Function
atas yang tidak menggunakan argumen bernama.
Sekarang hanya karena kita dapat bersarang Function
dengan cara ini, tidak berarti kita harus selalu melakukannya. Misalnya, jika kami ingin memilih elemen-elemen daftar yang kurang dari input, kami mungkin tergoda untuk melakukan sesuatu seperti berikut:
Select[...,xx<#]&
Ini sebenarnya akan lebih pendek untuk digunakan Cases
dan menghindari kebutuhan untuk bersarang Function
sepenuhnya:
Cases[...,x_/;x<#]&
Anda dapat menyimpan byte dengan bekerja di sekitar Prepend
atau PrependTo
:
l~Prepend~x
{x}~Join~l
{x,##}&@@l
atau
l~PrependTo~x
l={x}~Join~l
l={x,##}&@@l
Sayangnya, ini tidak membantu untuk yang lebih umum Append
, yang tampaknya merupakan padanan terpendek Array.push()
dalam bahasa lain.
BlockMap
adalah Partition
+Map
Tip ini juga bisa berjudul, "Baca catatan rilis, semuanya". (Untuk referensi, berikut adalah catatan rilis untuk 10.2 dan di sini rilis 10.3 hari ini .)
Bagaimanapun, bahkan rilis kecil mengandung banyak fitur baru, dan salah satu yang lebih berguna (untuk bermain golf) dari 10.2 adalah BlockMap
fungsi baru . Ini pada dasarnya menggabungkan Partition
dan Map
, yang bagus untuk pegolf, karena Partition
digunakan cukup sering, dan itu nama fungsi yang sangat panjang. Fungsi baru tidak akan memendek Partition
dengan sendirinya, tetapi setiap kali Anda ingin memetakan fungsi ke partisi (yang mungkin terjadi lebih sering daripada tidak), Anda sekarang dapat menyimpan satu atau dua byte:
#&/@l~Partition~2
BlockMap[#&,l,2]
#&/@Partition[l,3,1]
BlockMap[#&,l,3,1]
Penghematan semakin besar ketika posisi baru dari fungsi yang tidak disebutkan namanya memungkinkan Anda untuk menyelamatkan diri beberapa tanda kurung:
#&@@(#&/@Partition[l,3,1])
#&@@BlockMap[#&,l,3,1]
Sayangnya, saya tidak tahu mengapa mereka tidak menambahkannya BlockApply
sementara mereka melakukannya ...
Perhatikan juga bahwa BlockMap
tidak mendukung parameter ke-4 yang dapat Anda gunakan Partition
untuk mendapatkan daftar siklik:
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... *)
Jika jawaban Anda akhirnya menggunakan fungsi atau ekspresi yang sama beberapa kali, Anda mungkin ingin mempertimbangkan untuk menyimpannya dalam variabel.
Jika ekspresi Anda panjang l
dan Anda menggunakannya n
kali, biasanya akan menggunakan l * n
byte.
Namun, jika Anda menyimpannya dalam variabel panjang-1, itu hanya akan membutuhkan 3 + l + n
byte (atau 2 + l + n
byte, jika Anda menetapkan variabel di mana Anda tidak perlu CompoundExpression (;)
atau tanda kurung).
Sebagai contoh, mari kita pertimbangkan masalah sederhana, menemukan bilangan prima kembar kurang dari N.
Seseorang dapat menulis solusi 54 byte ini:
Select[Range@#,PrimeQ@#&&(PrimeQ[#+2]||PrimeQ[#-2])&]&
Dalam contoh ini, fungsi PrimeQ
ini digunakan tiga kali.
Dengan menetapkan PrimeQ
nama variabel, jumlah byte dapat dikurangi. Keduanya adalah 48 byte (54 - 6 byte):
Select[p=PrimeQ;Range@#,p@#&&(p[#+2]||p[#-2])&]&
Select[Range@#,(p=PrimeQ)@#&&(p[#+2]||p[#-2])&]&
Sort
sebagai gantiSortBy
Untuk daftar seperti list = {{1, "world"}, {0, "universe"}, {2, "country"}}
, tiga pernyataan berikut ini hampir setara.
SortBy[list,#[[1]]&]
list~SortBy~First
Sort@list
Select
danSortBy
Kadang-kadang kita perlu mengambil entri dari set yang lebih besar dan mengurutkannya untuk menemukan minimum / maksimum. Dalam beberapa keadaan , dua operasi dapat digabungkan menjadi satu.
Misalnya, untuk minimum, dua pernyataan berikut ini hampir setara.
SortBy[Select[l,SomeFormula==SomeConstant&],SortValue&]
SortBy[l,SortValue+99!(SomeFormula-SomeConstant)^2&]
dan
SortBy[Select[l,SomeFormula!=SomeConstant&],SortValue&]
SortBy[l,SortValue+1/(SomeFormula-SomeConstant)&]
1/0
adalah ComplexInfinity
, yang "lebih besar" dari semua bilangan real.
Untuk daftar nilai kunci, misalnya:
{SortValue,#}&/@SortBy[Select[l,SomeFormula==SomeConstant],SortValue&]
Sort[{SortValue+99!(SomeFormula-SomeConstant)^2,#})&/@l]
Array
dengan##&
Saat menggunakan Array multidimensi untuk menghitung daftar hasil yang perlu diratakan, gunakan ##&
sebagai argumen keempat. Ini menggantikan kepala Array dengan ##&
(setara dengan Sequence
), bukan List
, sehingga hasil akhir akan menjadi (datar) Sequence
dari hasil.
Dalam dua dimensi, bandingkan
{Array[f,dims,origin,##&]}
Join@@Array[f,dims,origin]
Tentu saja,
Join@@Array[f,dims]
masih 2 (atau 3, jika notasi infiks dapat digunakan) byte lebih pendek dari
{Array[f,dims,1,##&]}
.
Dalam tiga atau lebih dimensi, {Array[f,dims,origin,##&]}
selalu lebih pendek daripada alternatifnya, meskipun asalnya adalah 1.
{Array[f,dims,1,##&]}
f~Array~dims~Flatten~2
Nilai default menangani argumen pola yang hilang dengan cara yang efisien. Misalnya, jika kita ingin mencocokkan pola Exp[c_*x]
dalam aturan untuk nilai apa pun c
, naif
Exp[x] + Exp[2x] /. {Exp[c_*x] -> f[c], Exp[x] -> f[1]}
(* f[1] + f[2] *)
menggunakan lebih banyak byte daripada jika kita menggunakan nilai default untuk c
setiap kali hilang:
Exp[x] + Exp[2 x] /. Exp[c_.*x] -> f[c]
(* f[1] + f[2] *)
Penggunaan default ditunjukkan dengan titik setelah pola: c_.
.
Nilai default dikaitkan dengan operasi: dalam contoh di atas, operasi Times
dalam c_.*x
, dan nilai yang hilang untuk c_
dengan demikian diambil dari nilai default yang terkait dengan Times
, yaitu 1. Untuk Plus
, nilai default adalah 0:
Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(* f[0] + f[2] *)
Untuk Power
eksponen, standarnya adalah 1:
x + x^2 /. x^n_. -> p[n]
(* p[1] + p[2] *)
(Norm[#-#2]&)
daripada menulisEuclideanDistance
.