Kiat untuk bermain golf di Clean

17

Apa tips umum yang Anda miliki untuk bermain golf di Clean? Harap posting hanya ide yang dapat diterapkan pada masalah kode golf secara umum, dan setidaknya agak spesifik untuk Bersihkan.

Jika Anda belum pernah mendengar tentang Clean, Anda dapat mencari tahu lebih lanjut di sini .
Atau, Anda dapat bergabung dengan ruang obrolan .

Suram
sumber

Jawaban:

10

Hindari import StdEnvjika memungkinkan

Untuk mengakses fungsi bawaan, bahkan yang tampaknya mendasar seperti (==)atau map, pernyataan impor diperlukan, biasanya import StdEnvkarena mengimpor modul yang paling umum seperti StdInt, StdBooldan sebagainya (lihat di sini untuk info lebih lanjut tentang StdEnv).

Namun dimungkinkan untuk menghindari impor ini untuk beberapa tantangan dan cukup gunakan fitur bahasa inti seperti pemahaman daftar dan pencocokan pola.

Misalnya, bukannya

import StdEnv 
map f list

orang bisa menulis

[f x\\x<-list]

Daftar alternatif:

Beberapa fungsi atau fungsi pemanggilan yang perlu import StdEnv, alternatif yang tidak perlu impor dan perkiraan kasar byte yang disimpan.

  • hd-> (\[h:_]=h), ~ 6 byte
  • tl-> (\[_:t]=t), ~ 6 byte
  • map f list-> [f x\\x<-list], ~ 10 byte
  • filter p list-> [x\\x<-list|p x], ~ 11 byte
  • (&&)-> %a b|a=b=a;%, ~ 6 byte
  • (||)-> %a b|a=a=b;%, ~ 6 byte
  • not-> %a|a=False=True;%, ~ 1 byte
  • and-> %[a:r]|a= %r=a;%_=True, ~ 0 byte
  • or-> %[a:r]|a=a= %r;%_=False, ~ 0 byte

Beberapa yang terakhir tidak mungkin benar-benar menghemat byte, karena penggantian langsung menghasilkan lebih banyak byte daripada impor, tetapi mungkin dalam kasus-kasus di mana rekursi pada daftar tetap diperlukan.

Tip ini telah berhasil digunakan di sini .

Laikoni
sumber
Bukankah import StdEnv+ a and b(21 byte) lebih kecil dari %[a:r]|a= %r=a;%_=True(22 byte)? Atau apakah itu import StdEnv+ a=True and b=True(31 byte), dalam hal ini memang pasti lebih pendek? (Saya belum pernah memprogram dalam Bersihkan, btw.)
Kevin Cruijssen
@KevinCruijssen Kami baru saja mendiskusikan hal itu dalam obrolan . Memang benar bahwa itu tidak mungkin untuk menyimpan byte, kecuali mungkin ketika program perlu mengulang lagi daftar.
Laikoni
4
Ah baiklah. Mungkin juga berguna untuk menyatakan berapa banyak byte yang disimpan dengan alternatif (yaitu map f list -> [f x\\x<-list] (11 bytes saved)(atau yang serupa)).
Kevin Cruijssen
@KevinCruijssen Done.
Laikoni
5

Tahu cara belajar bahasa

Lagi pula, bagaimana orang bisa bermain golf dalam bahasa yang tidak bisa mereka gunakan!

On line

Bersih bukanlah bahasa yang terkenal atau terdokumentasi dengan baik, dan namanya tentu tidak membuatnya mudah untuk menemukan sumber daya yang sangat dibutuhkan untuk memperbaiki masalah ini ... atau apakah itu?

Clean pada awalnya disebut Concurrent Clean , yang masih digunakan dalam kata pengantar hampir setiap dokumen yang berhubungan dengan Clean - jadi jika Anda mencari Clean, cari Concurrent Clean.

Salah satu kemiripan Clean yang lebih luar biasa dengan Haskell (yang banyak terdapat) adalah keberadaan Cloogle , yang merupakan mesin pencari fungsi yang mencakup perpustakaan yang dikirimkan oleh Clean.

Secara lokal

Perpustakaan yang Bersihkan kapal adalah dalam bentuk file sumber Bersih yang cukup komentar, didokumentasikan sendiri, yang dapat diakses melalui menggunakan IDE.
(Itu juga datang dengan program contoh lengkap, di bawah $INSTALL/Examples.)

Omong-omong, versi Windows Clean hadir dengan IDE - walaupun cukup terbatas oleh standar modern, dunia lebih baik daripada menggunakan editor teks dan baris perintah.
Dua fitur yang paling berguna (dalam konteks pembelajaran) adalah:

  • Anda dapat mengklik dua kali kesalahan untuk melihat baris mana yang aktif
  • Anda dapat menyorot nama modul dan tekan [Ctrl]+[D]untuk membuka file definisi (atau gunakan [Ctrl]+[I]untuk file implementasi), dan beralih antara file definisi dan implementasi dengan[Ctrl]+[/]
Suram
sumber
4

Lupakan pengkodean karakter

Compiler Clean tidak peduli dengan pengkodean apa yang Anda pikir Anda telah menyimpan file sumber, hanya tentang nilai byte dalam file. Ini memiliki beberapa konsekuensi yang rapi.

Di dalam tubuh kode sumber, hanya byte dengan titik kode yang sesuai dengan karakter ASCII yang dapat dicetak yang diizinkan, selain itu byte untuk \t\r\n.

Literal:

Dalam Stringdan [Char]literal ( "stuff"dan ['stuff']masing - masing), setiap byte kecuali 0 diizinkan, dengan peringatan bahwa "dan 'harus melarikan diri (untuk Stringdan [Char]masing - masing), dan bahwa baris baru dan pengembalian carraige harus diganti dengan \ndan \r(juga masing-masing).

Dalam Charliteral, byte apa pun kecuali 0 diizinkan, artinya:

'\n'

'
'

Apakah sama, tetapi yang kedua lebih pendek satu byte.

Melarikan diri:

Selain dari huruf standar yang lolos \t\r\n(dll.), Semua urutan pelarian non-numerik dalam Bersihkan adalah untuk slash, atau untuk kutipan yang digunakan untuk membatasi literal yang ada di dalamnya.

Untuk urutan pelepasan numerik, angka diperlakukan sebagai nilai oktal yang diakhiri setelah tiga digit. Ini berarti bahwa jika Anda ingin null diikuti oleh karakter 1dalam String, Anda harus menggunakan "\0001"(atau "\0\61") dan tidak "\01" . Namun, jika Anda mengikuti pelarian dengan apa pun selain angka, Anda dapat menghilangkan nol di depan.

Konsekuensi:

Keunikan ini dengan cara Clean menangani file sumbernya memungkinkan Stringdan ['Char']secara efektif menjadi urutan nomor basis-256 satu digit - yang memiliki banyak kegunaan untuk kode-golf, seperti menyimpan indeks (hingga 255, tentu saja).

Suram
sumber
3

Nama berfungsi dengan simbol

Ketika mendefinisikan suatu fungsi, seringkali lebih pendek untuk menggunakan beberapa kombinasi !@$%^&*~-+=<:|>.?/\daripada menggunakan karakter alfanumerik, karena memungkinkan Anda untuk menghilangkan spasi putih di antara pengidentifikasi.

Misalnya: ?a=a^2lebih pendek dari f a=a^2, dan memohon itu juga lebih pendek.

Namun :

Jika pengidentifikasi fungsi digunakan berdekatan dengan simbol lain , yang dapat bergabung untuk membentuk pengidentifikasi yang berbeda, tetapi valid , mereka semua akan diuraikan sebagai satu pengidentifikasi dan Anda akan melihat kesalahan.

Sebagai contoh: ?a+?bparses as? a +? b

Selain itu:

Dimungkinkan untuk menimpa pengidentifikasi yang diimpor di Clean, dan satu-satunya pengidentifikasi simbol karakter tunggal yang belum digunakan StdEnvadalah @$?. Timpa ^-+(dll.) Dapat berguna jika Anda membutuhkan lebih banyak pengidentifikasi simbolis, tetapi berhati-hatilah agar Anda tidak menimpa yang Anda gunakan.

Suram
sumber
3

Ketahui simpul K Anda

Beberapa konstruksi terkuat (untuk bermain golf) dalam bahasa fungsional adalah let ... in ....
Bersih tentu saja, memiliki ini, dan sesuatu yang lebih baik - itu #.

Apa itu simpul?

Clean's #, dan ubiquitous |(pattern guard) keduanya dikenal sebagai 'ekspresi simpul'.
Khususnya, mereka memungkinkan Anda untuk program imperatively- ish di Clean (yang benar-benar baik di sini!).

The #(let-before):

Keduanya menghitung nilai integer yang diberikan sebagai string, dikalikan dengan jumlah karakternya

f s=let i=toInt s;n=sum[toInt c\\c<-:s]in n*i

f s#i=toInt s
#s=sum[toInt c\\c<-:s]
=s*i

Perhatikan bagaimana versi dengan #lebih pendek, dan bagaimana kita dapat mendefinisikan ulang s. Ini berguna jika kita tidak membutuhkan nilai yang dimiliki variabel ketika kita menerimanya, jadi kita bisa menggunakan kembali namanya. ( letdapat mengalami masalah saat Anda melakukannya)

Tetapi menggunakan letlebih mudah ketika Anda membutuhkan sesuatu sepertiflip f = let g x y = f y x in g

The |(penjaga pola):

Pelindung pola Clean dapat digunakan seperti yang ada di banyak bahasa fungsional lainnya - namun juga dapat digunakan seperti keharusan if ... else .... Dan versi yang lebih pendek dari ekspresi ternary.

Misalnya, ini semua mengembalikan tanda integer:

s n|n<>0|n>0=1= -1
=0

s n=if(n<>0)if(n>0)1(-1)0

s n|n>0=1|n<0= -1=0

Tentu saja, yang terakhir yang menggunakan penjaga lebih tradisional adalah yang terpendek, tetapi yang pertama menunjukkan bahwa Anda dapat membuat sarangnya (tetapi hanya dua klausa pengembalian tanpa syarat dapat muncul pada baris yang sama dalam aturan tata letak), dan yang kedua menunjukkan apa yang yang pertama tidak secara logis.

Sebuah catatan:

Anda dapat menggunakan ekspresi ini pada dasarnya di mana saja. Dalam lambdas, case ... of, let ... in, dll

Suram
sumber
1

Jika Anda menggunakan StringAnda harus menggunakanText

Konversi ke string, dan manipulasi string ( jenis {#Char}/ Stringbukan [Char]jenis) cukup panjang dan buruk untuk bermain golf. The Textmodul obat ini.

Konversi:

Textmendefinisikan operator <+untuk dua jenis yang telah toStringditentukan.
Operator ini, digunakan a<+bsama dengan toString a+++toString b- menghemat setidaknya 19 byte . Bahkan jika Anda memasukkan impor ekstra ,Text,, dan menggunakannya hanya sekali, itu masih menghemat 14 byte!

Manipulasi:

Textmendefinisikan beberapa staples manipulasi-string yang hilang dari StdEnv:

  • Operator +untuk string, yang jauh lebih pendek dari +++(dari StdEnv)
  • indexOf, dengan perilaku seperti-C kembali -1bukannya Nothingpada kegagalan
  • concat, yang merangkai daftar string
  • join, yang menggabungkan daftar string menggunakan string pemisah
  • split, yang membagi string menjadi daftar string pada substring
Suram
sumber
1

Gunakan lambda yang lebih pendek

Terkadang Anda menemukan diri Anda menggunakan ekspresi lambda (untuk beralih ke map, atau sortBy, dll.). Ketika Anda melakukan ini (menulis lambdas), ada banyak cara untuk melakukannya.

Jalan yang benar:

Ini sortBy, dengan daftar penyortiran lambda idiomatik dari terpanjang ke terpendek

sortBy (\a b = length a > length b)

Cara lain yang benar:

Jika Anda menggunakan Data.Func, Anda juga bisa melakukannya

sortBy (on (>) length)

Jalan pendek:

Ini adalah hal yang sama, tetapi dengan sintaksis golf

sortBy(\a b=length a>length b)

Jalan lain:

Menggunakan komposisi tidak lebih pendek kali ini, tetapi terkadang bisa lebih pendek

sortBy(\a=(>)(length a)o length)

Cara lain yang lain:

Meskipun sedikit dibuat-buat di sini, Anda dapat menggunakan penjaga di lambdas

sortBy(\a b|length a>length b=True=False)

Dan juga ekspresi simpul let-before

sortBy(\a b#l=length
=l a>l b)

Sebuah catatan:

Ada dua bentuk lambda lagi, (\a b . ...)dan (\a b -> ...)yang terakhir identik dengan =varian, dan yang pertama ada karena beberapa alasan dan sering terlihat seperti Anda mencoba mengakses properti dari sesuatu alih-alih mendefinisikan lambda jadi jangan gunakan. bisa menggunakannya.

Suram
sumber
1
Setelah melihat beberapa program golf Anda, saya mendapat kesan \a=... adalah sintaks lambda yang biasa dari Clean: P
Ørjan Johansen
Anda juga bisa menambahkan penjaga di lambda, seperti yang digunakan di sini . Ini tidak berdokumen (bahkan bertentangan dengan laporan bahasa), tetapi berfungsi. Juga, ->dan =untuk lambdas identik sejauh menyangkut kompiler ( ->adalah sintaks lama). Hanya .berbeda (tapi saya tidak tahu persis bagaimana).
Dan dalam contoh khusus ini Anda dapat mempertimbangkan menggunakan on(<)length, meskipun Data.Funcimpor akan memecah Anda kecuali Anda sudah membutuhkannya.
@Keelan Cool. Saya akan memperbarui ini hari ini. Saya pikir Anda juga dapat menggunakan let-before ( #) di lambdas.
Οurous
Ya, Anda dapat :-)
0

Gunakan Daftar Karakter Literal

Daftar karakter literal adalah cara singkat menulis sesuatu seperti ['h','e','l','l','o']sebagai ['hello'].

Ini bukan batas notasi, misalnya:

  • repeat'c'menjadi ['c','c'..]menjadi['cc'..]
  • ['z','y'..'a'] menjadi ['zy'..'a']
  • ['beginning']++[a,b,c]++['end'] menjadi ['beginning',a,b,c,'end']
  • ['prefix']++suffix menjadi ['prefix':suffix]

Ini juga berfungsi dalam pencocokan:

  • ['I don't care about the next character',_,'but I do care about these ones!']
Suram
sumber
0

Terkadang codelebih pendek

Bersih memiliki banyak fungsi yang sangat berguna di perpustakaan standar, beberapa di antaranya sangat verbose untuk digunakan tanpa akses *World, dan menggunakan *Worlddalam kode-golf umumnya merupakan ide yang buruk pula.

Untuk mengatasi masalah ini, seringkali ada yang ccallbisa Anda gunakan di dalam codeblok sebagai gantinya.

Beberapa contoh:

Waktu sistem

import System.Time,System._Unsafe
t=toInt(accUnsafe(time))

Di atas adalah 58 byte, tetapi Anda dapat menyimpan 17 byte (hingga 40 +1) dengan:

t::!Int->Int
t _=code{ccall time "I:I"
}

Angka acak

Yang ini tidak menyimpan byte sendiri, tetapi menghindari keharusan membagikan daftar genRandInt

s::!Int->Int
s _=code{ccall time "I:I"ccall srand "I:I"
}
r::!Int->Int
r _=code{ccall rand "I:I"
}

Penggunaan lainnya

Selain kedua ini, yang mungkin merupakan kegunaan utama untuk ini dalam kode-golf, Anda dapat memanggil fungsi apa pun yang disebutkan (termasuk tetapi tidak terbatas pada setiap syscall), menanamkan perakitan sewenang-wenang dengan instruction <byte>, dan menanamkan kode untuk mesin ABC.

Suram
sumber