Apakah ada cara untuk memperpendek fungsi panah gemuk?

15

Dari apa yang saya lihat sepanjang waktu saya di PPCG, sebagian besar entri JavaScript yang melibatkan fungsi panah gemuk cenderung menjadi salah satu dari dua kubu:

  1. Yang sederhana yang mampu berjalan sebagai pernyataan tunggal dan mengembalikan jawaban, langsung dari kelelawar, seperti x=(a,b)=>a*a+b

  2. Yang lebih kompleks yang biasanya memiliki kurung kurawal karena penggunaan loop, dan akibatnya memerlukan penggunaan a return pernyataan .. sepertip=b=>{m=b;for(a=1;~-m;)--m,a*=m*m;return a%b}

Mengambil contoh di atas dari kategori 2 dengan konsep kurung kurawal sebagai bukti konsep ... Apakah akan ada cara untuk melakukan golf ulang kode ini (atau serupa) seperti ini untuk menghilangkan kurung kurawal dan juga return? Saya hanya menanyakan ini karena ini berpotensi (tidak mengatakan ini akan terjadi setiap saat) menghilangkan 8 byte dari kode pegolf JS. Apakah ada teknik yang bisa digunakan dalam hal ini? Saya sudah mencoba rekursi, tetapi m=bpernyataan itu telah terbukti sebagai momok, karena sepertinya saya tidak bisa mengguncangnya.

Untuk kode di atas, bagaimana satu golf lebih jauh sehingga menghilangkan returnpernyataan, terlepas dari apakah golf lebih pendek atau tidak?

WallyWest
sumber

Jawaban:

18

Gunakan Rekursi

Saya telah menemukan bahwa rekursi (hampir) selalu lebih pendek dari eval+ for. Cara umum untuk mengkonversi dari untuk ke eval adalah:

for(a=n;b;c);d
(f=a=>b?f(c):d)(n)

Jadi mari kita lihat contoh Anda:

b=>{m=b;for(a=1;~-m;)--m,a*=m*m;return a%b}

Pertama-tama kita dapat menyederhanakannya menjadi:

for(m=b,a=1;~-m;--m,a*=m*m)a%b;

Apa yang kami lakukan di sini? Yah kami hanya memindahkan semua yang ada di dalam forpernyataan, ini membantu kami mengurangi jumlah titik koma yang tidak secara langsung lebih baik tetapi hampir selalu mengarah ke beberapa golf.


Mari kita letakkan ini di eval dan bandingkan dengan versi rekursi:

b=>{m=b;for(a=1;~-m;)--m,a*=m*m;return a%b}
b=>eval('for(m=b,a=1;~-m;--m,a*=m*m)a%b')
b=>(f=a=>~-m?(--m,f(a*=m*m)):a%b)(1,m=b)

Bagian pertama dari for loop ( a=n), kita dapat memulainya dengan melewatkan variabel-variabel tersebut sebagai argumen. Syaratnya sederhana: di b?(c,f(a)):dmana dnilai kembali. Biasanya chanya memodifikasi asehingga bisa digabung ke dalamnya. Jadi kita bisa bermain golf lebih banyak menggunakan apa yang saya sebutkan:

b=>(f=a=>~-m?(--m,f(a*=m*m)):a%b)(1,m=b)
b=>(f=a=>~-m?f(a*=--m*m):a%b)(1,m=b) // --m moved into a*=
b=>(f=a=>--m?f(a*=m*m):a%b)(1,m=b) // --m moved to condition

Yang mengatakan, seperti dicatat oleh @Niel menyederhanakan algoritma Anda. Algoritma golf dalam satu bahasa mungkin tidak golf dalam bahasa lain jadi pastikan untuk mencoba algoritma yang berbeda dan membandingkannya.

Downgoat
sumber
1
Anda melewatkan penghematan besar dalam menyederhanakan kode asli. ~-madalah m-1, sehingga loop dapat for(m=b,a=1;--m;a*=m*m)a%b;dan versi rekursif dapat (belum diuji)b=>(f=a=>--m?f(a*=m*m):a%b)(1,m=b)
Peter Taylor
1
Kadang-kadang Anda hanya perlu menggunakan algoritma yang berbeda tetapi dalam hal ini yang terbaik yang bisa saya lakukan adalah sama panjangnya dengan jawaban @ PeterTaylor:b=>b>1&(f=a=>--a<2||b%a&&f(a))(b)
Neil
11

Eval penyalahgunaan.

Itu mudah. Dari pada:

f=n=>{for(i=c=0;i<n;i++)c+=n;return c}

Menggunakan

f=n=>eval("for(i=c=0;i<n;i++)c+=n;c")

Eval mengembalikan pernyataan yang terakhir dievaluasi. Dalam hal ini, karena pernyataan terakhir yang dievaluasi adalah c+=n, kita akan dibiarkan c, bagaimanapun, menghemat dua byte.

f=n=>eval("for(i=c=0;i<n;i++)c+=n")

Secara umum:

f=n=>eval("code;x")

lebih pendek dari ini, oleh byte:

f=n=>{code;return x}

Sebagai catatan, menggunakan kuburan untuk memanggil eval untuk menyimpan byte mungkin tidak berhasil, karena:

eval`string`

setara dengan

["string"]

Bermanfaat untuk kebingungan! Tidak terlalu banyak untuk kode golf.

Conor O'Brien
sumber
2
foo`string`selalu setara dengan foo(["string"]), hanya saja banyak fungsi kemudian membuang array kembali ke string yang diinginkan.
Neil
@Neil Oh, betapa menarik!
Conor O'Brien