Tips untuk bermain golf di Haskell


73

Tips umum apa yang Anda miliki untuk bermain golf di Haskell? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk Haskell. Harap kirim hanya satu tip per jawaban.


Jika Anda baru bermain golf di Haskell, silakan lihat Panduan Aturan Golf di Haskell . Ada juga ruang obrolan Haskell khusus: Of Monads and Men .


1
Melihat jumlah jawaban sampai sekarang, saya ragu apakah Haskell bahkan merupakan bahasa yang baik untuk bermain golf kode atau tidak?
Animesh 'the CODER'

10
Mengapa hanya satu tip per jawaban? Juga, setiap bahasa adalah bahasa yang baik untuk bermain golf. Hanya saja, jangan selalu berharap untuk menang.
hapus klasifikasi

6
@ intlemeat Dengan cara ini orang dapat memilih yang bagus ke atas tanpa memperbaiki yang buruk hanya karena ditulis oleh orang yang sama dalam jawaban yang sama.
MasterMastic

3
Permintaan khusus, kompresi string.
J Atkin

Ini mungkin tidak cocok sebagai anwer, tapi saya masih ingin menambahkannya di sini: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

Jawaban:


45

Tetapkan operator infiks alih-alih fungsi biner

Ini biasanya menghemat satu atau dua spasi per definisi atau panggilan.

0!(y:_)=y
x!(y:z)=(x-1)!z

vs.

f 0(y:_)=y
f x(y:z)=f(x-1)z

Simbol tersedia untuk operator 1-byte yang !, #, %, &, dan ?. Semua tanda baca ASCII lainnya sudah didefinisikan sebagai operator oleh Prelude (seperti $) atau memiliki arti khusus dalam sintaksis Haskell (seperti @).

Jika Anda membutuhkan lebih dari lima operator, Anda bisa menggunakan kombinasi di atas, seperti !#, atau karakter tanda baca Unicode tertentu, seperti ini (semua 2 byte di UTF-8):

¡ ¢ £ ¤ ¥ ¦ § ¨ © ¬ ® ¯ ° ± ´ ¶ · ¸ ¿ × ÷

11
Catatan: ini juga dapat digunakan untuk fungsi dengan tiga argumen atau lebih. (x!y)z=x+y*zdan (x#y)z u=x*z+y*ukeduanya berfungsi seperti yang diharapkan.
Zgarb

3
Ini juga dapat digunakan untuk argumen fungsi, misalnya \f g(!)x y->f g!x yalih-alih\f g j x y->j(f g)(x y)
Buah Esolanging

2
Terkadang menguntungkan untuk mengubah fungsi unary ke operator biner jika Anda harus menggunakan tanda kurung - g x=…;g(f x)lebih lama dari_?x=…;0!f x
Angs

29

Gunakan notasi pointless (atau -free) jika sesuai

Seringkali suatu fungsi dengan satu atau dua parameter dapat ditulis bebas titik.

Jadi pencarian untuk daftar tupel yang elemen-elemennya ditukar secara naif ditulis sebagai:

revlookup :: Eq b => b -> [(a, b)] -> Maybe a
revlookup e l=lookup e(map swap l)

(jenisnya ada hanya untuk membantu Anda memahami apa yang dilakukannya.)

untuk tujuan kita ini jauh lebih baik:

revlookup=(.map swap).lookup

28

Gunakan daftar monad

Ulasan singkat:

xs >> ys        =  concat $ replicate (length xs) ys
xs >>= f        =  concatMap f xs
mapM id[a,b,c]  =  cartesian product of lists: a × b × c
mapM f[a,b,c]   =  cartesian product of lists: f a × f b × f c

Contoh:

  • Mengulangi daftar dua kali

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Singkat concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • concatPemahaman daftar + lebih pendek

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • Produk Cartesian

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Daftar koordinat pada kisi

    Prelude> mapM(\x->[0..x])[3,2]
    [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]]
    

1
Penggunaan tambahan yang saya temukan bermanfaat [0..b]>>[a]bukan replicate a b.
Wheat Wizard

3
@WheatWizard a<$[1..b]bahkan lebih pendek, untuk replicate.
Lynn

Menggunakan =<<memaksa Anda untuk mengimpor Control.Monad. Jika Anda tidak membutuhkannya karena alasan lain, bertukar argumen dan menggunakan >>=tampaknya lebih ringkas.
dfeuer

OTOH, jika Anda memang perlu Data.Traversable, contoh produk Cartesian dapat disingkat menjadi for["Hh","io",".!"]id.
dfeuer

2
(=<<)ada di Prelude , sebenarnya! Saya sudah sering menggunakannya.
Lynn

28

Gunakan pelindung bukan kondisional:

f a=if a>0 then 3 else 7
g a|a>0=3|True=7

Gunakan titik koma bukan indentasi

f a=do
  this
  that
g a=do this;that

Gunakan ekspresi boolean untuk fungsi boolean

f a=if zzz then True else f yyy
g a=zzz||f yyy

(SO menjadi sakit karena membiarkan saya memposting ini secara terpisah)


2
juga, gunakan beberapa pelindung alih-alih &&saat berada dalam pemahaman daftar.
John Dvorak

Bagus Jan - Anda harus membuat itu menjadi jawaban, saya akan memilihnya
bazzargh

5
Contoh pertama dapat dipersingkat dengan True=>1>0
John Dvorak

1
dalam contoh pertama, saya berasumsi maksud Andaf a=if a>0 then 3 else 7
Cyoce

Penjaga bahkan berfungsi jika tidak ada argumen di dalamnya.
Akangka

24

interact :: (String → String) → IO ()

Orang sering lupa bahwa fungsi ini ada - ia mengambil semua stdin dan menerapkannya pada fungsi (murni). Saya sering melihat main-kode di sepanjang baris

main=getContents>>=print.foo

sementara

main=interact$show.foo

sedikit lebih pendek. Itu ada di Pendahuluan jadi tidak perlu impor!


24

Gunakan GHC 7.10

Versi pertama GHC yang berisi hal-hal ini dirilis pada 27 Maret 2015 .

Ini versi terbaru, dan Prelude mendapatkan beberapa tambahan baru yang berguna untuk bermain golf:

The (<$>)dan (<*>)operator

Operator yang bermanfaat ini Data.Applicativeberhasil masuk! <$>hanya fmap, sehingga Anda dapat mengganti map f xdan fmap f xdengan di f<$>xmana saja dan memenangkan kembali byte. Juga, <*>berguna dalam Applicativecontoh untuk daftar:

Prelude> (,)<$>[1..2]<*>"abcd"
[(1,'a'),(1,'b'),(1,'c'),(1,'d'),(2,'a'),(2,'b'),(2,'c'),(2,'d')]

The (<$)Operator

x<$asetara dengan fmap (const x) a; yaitu mengganti setiap elemen dalam wadah dengan x.

Ini sering merupakan alternatif yang bagus untuk replicate: 4<$[1..n]lebih pendek dari replicate n 4.

Proposal Lipat / Dapat Dilewati

Fungsi-fungsi berikut diangkat dari mengerjakan daftar [a]ke Foldabletipe umum t a:

fold*, null, length, elem, maximum, minimum, sum, product
and, or, any, all, concat, concatMap

Ini berarti mereka sekarang juga bekerja Maybe a, di mana mereka berperilaku seperti "daftar dengan paling banyak satu elemen". Misalnya null Nothing == True,, atau sum (Just 3) == 3. Demikian pula, lengthmengembalikan 0 untuk Nothingdan 1 untuk Justnilai. Alih-alih menulis, x==Just yAnda dapat menulis elem y x.

Anda juga dapat menerapkannya pada tupel, yang berfungsi seolah-olah Anda sudah menelepon \(a, b) -> [b]dulu. Ini hampir sama sekali tidak berguna, tetapi or :: (a, Bool) -> Boolsatu karakter lebih pendek dari snd, dan elem blebih pendek dari (==b).snd.

Fungsi Monoid memptydanmappend

Tidak sering penyelamat, tetapi jika Anda dapat menyimpulkan jenisnya, memptysatu byte lebih pendek dari itu Nothing, jadi itulah.


5
+1 Sangat menyenangkan mendengar tentang '<$> `dan <*>menjadikannya sebagai Prelude! Itu harus berguna bahkan ketika itu bukan kode golf (aplikatif adalah kata yang panjang).
ankh-morpork

Peringatan tentang penggantian rata: jika versi bahasa Anda lebih baru daripada tantangan, solusi Anda tidak dapat diabaikan untuk menang. Jika Anda ingin memperbarui jawaban atau jawaban yang ada, jangan timpa solusi yang ada. Tulis lampiran.
John Dvorak

4
Lucu ada [1..2]di sana. itu hanya[1,2]
haskeller bangga

2
Dalam versi yang sama kami juga dapatkan <*dari Applicative, yang untuk daftar adalah xs <* ys == concatMap (replicate (length ys)) xs. Ini berbeda dari xs >> ysatau xs *> ysyang ada concat (replicate (length ys)) xs. pureyang lebih pendek returndatang pada saat ini juga.
Angs

2
Anda sekarang dapat menggunakan <>sebagai ganti mappend, itu sekarang (dengan GHC 8.4.1) bagian dari Prelude.
ბიმო

22

Gunakan 1<2sebagai ganti Truedan 1>2bukan False.

g x|x<10=10|True=x
f x|x<10=10|1<2=x

3
Ini tidak benar-benar spesifik untuk Haskell: ini berlaku untuk hampir semua bahasa yang memiliki tipe Boolean yang bertentangan dengan nilai-nilai yang benar dan salah dari jenis lainnya.
Peter Taylor

Adakah yang bisa menjelaskan ini?
MasterMastic

2
ini bukan contoh yang baik, saya hanya akan golf ini f=max 10.
haskeller bangga

@MasterMastic ini hanya menulis if(true)dalam bahasa lain. di awal, sebaliknya sebenarnya adalah nilai boolean True.
haskeller bangga

2
@PeterTaylor Saya pikir ini masih berharga untuk haskellian baru (seperti saya) ketika saya pertama kali belajar menggunakannya otherwise.
flawr

20

Gunakan pemahaman daftar (dengan cara pintar)

Semua orang tahu itu sintaks yang berguna, seringkali lebih pendek dari map+ a lambda:

Prelude> [[1..x]>>show x|x<-[1..9]]
["1","22","333","4444","55555","666666","7777777","88888888","999999999"]

Atau filter(dan secara opsional a mappada saat yang sama):

Prelude> [show x|x<-[1..60],mod 60x<1]
["1","2","3","4","5","6","10","12","15","20","30","60"]

Tetapi ada beberapa kegunaan aneh yang berguna sekarang dan nanti. Pertama, pemahaman daftar tidak perlu mengandung <-panah sama sekali:

Prelude> [1|False]
[]
Prelude> [1|True]
[1]

Yang berarti alih-alih if p then[x]else[], Anda bisa menulis [x|p]. Juga, untuk menghitung jumlah elemen daftar yang memenuhi syarat, secara intuitif Anda akan menulis:

length$filter p x

Tapi ini lebih pendek:

sum[1|y<-x,p y]

Saya benar-benar menggunakan semua ini sebelumnya, tetapi saya tidak berpikir untuk meletakkannya di sini.
haskeller bangga

18

Ketahui milik Anda Prelude

Jalankan GHCi dan gulir melalui dokumentasi Prelude . Setiap kali Anda melewati suatu fungsi yang memiliki nama pendek, itu bisa bermanfaat untuk mencari beberapa kasus di mana itu mungkin berguna.

Misalnya, Anda ingin mengubah string s = "abc\ndef\nghi"menjadi string yang dipisahkan oleh ruang "abc def ghi",. Cara yang jelas adalah:

unwords$lines s

Tetapi Anda bisa berbuat lebih baik jika Anda menyalahgunakan max, dan fakta bahwa \n < space < printable ASCII:

max ' '<$>s

Contoh lain adalah lex :: String -> [(String, String)], yang melakukan sesuatu yang sangat misterius:

Prelude> lex "   some string of Haskell tokens  123  "
[("some"," string of Haskell tokens  123  ")]

Cobalah fst=<<lex suntuk mendapatkan token pertama dari sebuah string, melompati spasi putih. Berikut adalah solusi cerdas oleh henkma yang menggunakan lex.showpada Rationalnilai-nilai.


16

Cocokkan dengan nilai konstan

Pemahaman daftar dapat mencocokkan pola pada konstanta.


[0|0<-l]

Ini mengekstrak 0 dari daftar l, yaitu membuat daftar sebanyak 0 dalam l.


[1|[]<-f<$>l] 

Ini membuat daftar 1sebanyak yang ada elemen lyang fdibutuhkan untuk daftar kosong (menggunakan <$>sebagai infix map). Terapkan sumuntuk menghitung elemen-elemen ini.

Membandingkan:

[1|[]<-f<$>l]
[1|x<-l,f x==[]]

[x|(0,x)<-l]

Konstanta dapat digunakan sebagai bagian dari kecocokan pola. Ini mengekstrak entri kedua dari semua tupel yang entri pertamanya adalah 0.


Perhatikan bahwa semua ini membutuhkan konstanta literal aktual, bukan nilai variabel. Misalnya, let x=1 in [1|x<-[1,2,3]]akan menampilkan [1,1,1], bukan [1], karena xpengikatan luar ditimpa.


14

Gunakan wordssebagai ganti daftar panjang string. Ini tidak terlalu spesifik untuk Haskell, bahasa lain juga memiliki trik yang serupa.

["foo","bar"]
words"foo bar"  -- 1 byte longer
["foo","bar","baz"]
words"foo bar baz"  -- 1 byte shorter
["foo","bar","baz","qux"]
words"foo bar baz qux"    -- 3 bytes shorter

14

Ketahui fungsi monadik Anda

1)
mensimulasikan fungsi monadik menggunakan mapM.

banyak kali kode akan miliki sequence(map f xs), tetapi dapat diganti dengan mapM f xs. bahkan ketika hanya menggunakan sequenceitu saja sudah lebih lama mapM id.

2)
menggabungkan fungsi menggunakan (>>=)(atau (=<<))

versi fungsi monad (>>=)didefinisikan sebagai:

(f >>= g) x = g (f x) x 

ini bisa berguna untuk membuat fungsi yang tidak bisa diekspresikan sebagai pipeline. misalnya, \x->x==nub xlebih panjang dari nub>>=(==), dan \t->zip(tail t)tlebih panjang dari tail>>=zip.


+1 - Saya bahkan tidak menyadari bahwa ada instance monad untuk fungsi. itu bisa sangat berguna :)
Jules

2
Catatan: Meskipun ini bagian dari Applicativedan tidak Monadada implementasinya purejuga yang lebih pendek dari constdan benar-benar membantu saya sebelumnya.
ბიმო

14

Argumen bisa lebih pendek dari definisi

Saya baru saja dikalahkan dengan cara yang sangat aneh oleh henkma .

Jika fungsi bantu fdalam jawaban Anda menggunakan operator yang tidak digunakan di tempat lain dalam jawaban Anda, dan fdipanggil sekali, buat operator argumen f.

Ini:

(!)=take
f a=5!a++3!a
reverse.f

Apakah dua byte lebih panjang dari ini:

f(!)a=5!a++3!a
reverse.f take

12

Gunakan operator kontra (:)

saat menggabungkan daftar, jika yang pertama adalah panjang 1 kemudian gunakan :sebagai gantinya.

a++" "++b
a++' ':b  -- one character shorter

[3]++l
3:l    -- three characters shorter

4
Patut dicatat bahwa itu asosiatif yang tepat, sehingga Anda dapat terus menggunakannya untuk sejumlah item tunggal di awal daftar, mis. 1:2:3:xAlih-alih [1,2,3]++x.
Jules

11

Jangan gunakan backticks terlalu sering. Backticks adalah alat keren untuk membuat bagian fungsi awalan, tetapi kadang-kadang dapat disalahgunakan.

Pernah saya melihat seseorang menulis subekspresi ini:

(x`v`)

Meskipun sama dengan adil v x.

Contoh lain adalah menulis (x+1)`div`y sebagai lawan div(x+1)y.

Saya melihatnya terjadi sekitar divdan elemlebih sering karena fungsi-fungsi ini biasanya digunakan sebagai infix dalam kode reguler.


Bukankah maksud Anda membuat bagian dari fungsi awalan ?
Cyoce

@Cyoce Ya, tentu saja
haskeller bangga

11

Gunakan pelindung pola

Mereka lebih pendek dari a letatau lambda yang mendekonstruksi argumen fungsi yang Anda definisikan. Ini membantu ketika Anda membutuhkan sesuatu seperti fromJustdari Data.Maybe:

f x=let Just c=… in c

lebih panjang dari

f x=(\(Just c)->c)$…

lebih panjang dari

m(Just c)=c;f x=m$…

lebih panjang dari

f x|Just c<-…=c

Bahkan, mereka lebih pendek bahkan ketika mengikat nilai lama polos alih-alih mendekonstruksi: lihat tip xnor .


Yah, yang lambda tidak membutuhkan tanda dolar, dan sepertinya perubahan ini membuat panjang ini dan potongan berikutnya sama
haskeller bangga

1
Saya berasumsi esebenarnya bukan satu tanda tetapi ekspresi yang lebih lama yang diperlukan $sebelum itu, yang biasanya terjadi.
Lynn

11

Bersyarat lebih pendek

last$x:[y|b]

setara dengan

if b then y else x

Begini cara kerjanya:

             [y|b]   x:[y|b]   last$x:[y|b]
if...      +--------------------------------
b == False | []      [x]       x
b == True  | [y]     [x,y]     y

Haruskah begitu if b then y else x?
Akangka

@ChristianIrwan Tangkapan bagus, ya.
xnor

Tidak akan menggunakan boollebih pendek karena Anda tidak memerlukan pemahaman daftar
Potato44

@ Potato44 Itu ada di Data.Bool, yang membutuhkan byte untuk diimpor.
xnor

11

Bekerja dengan tanda minus

Tanda minus -adalah pengecualian yang menjengkelkan bagi banyak aturan sintaksis. Kiat ini mencantumkan beberapa cara singkat untuk mengekspresikan negasi dan pengurangan di Haskell. Tolong beri tahu saya jika saya melewatkan sesuatu.

Penyangkalan

  • Untuk meniadakan ekspresi e, lakukan saja -e. Misalnya, -length[1,2]memberi -2.
  • Jika ebahkan cukup kompleks, Anda akan membutuhkan tanda kurung e, tetapi Anda biasanya dapat menyimpan byte dengan memindahkannya: -length(take 3 x)lebih pendek dari -(length$take 3 x).
  • Jika edidahului oleh =atau operator infiks dari fixity kurang dari 6, Anda memerlukan ruang: f= -2mendefinisikan fdan k< -2menguji jika kkurang dari -2. Jika fixity 6 atau lebih besar, Anda perlu parens: 2^^(-2)memberi 0.25. Anda biasanya dapat mengatur ulang barang untuk menyingkirkan ini: misalnya, lakukan -k>2alih - alih k< -2.
  • Demikian pula, jika !adalah operator, maka -a!bdiurai seolah- (-a)!bolah fixity !paling banyak 6 (jadi -1<1memberi True), dan -(a!b)sebaliknya (jadi -[1,2]!!0memberi -1). Memperbaiki standar operator yang ditentukan pengguna dan fungsi backticked adalah 9, sehingga mereka mengikuti aturan kedua.
  • Untuk mengubah negasi menjadi fungsi (untuk digunakan dengan mapdll), gunakan bagian ini (0-).

Pengurangan

  • Untuk mendapatkan fungsi yang mengurangi k, gunakan bagian (-k+)yang menambahkan -k. kbahkan bisa menjadi ekspresi yang cukup rumit: (-2*length x+)berfungsi seperti yang diharapkan.
  • Untuk mengurangi 1, gunakan predsebaliknya, kecuali jika itu membutuhkan ruang di kedua sisi. Ini jarang terjadi dan biasanya terjadi dengan untilatau fungsi yang ditentukan pengguna, karena map pred xdapat digantikan oleh pred<$>xdan iterate pred xoleh [x,x-1..]. Dan jika Anda memiliki f pred xsuatu tempat, Anda mungkin harus mendefinisikan fsebagai fungsi infiks. Lihat tip ini .

11

Coba atur ulang definisi fungsi dan / atau argumen

Anda kadang-kadang dapat menyimpan beberapa byte dengan mengubah urutan kasus pencocokan pola dalam definisi fungsi. Tabungan ini murah, tetapi mudah untuk dilewatkan.

Sebagai contoh, pertimbangkan versi sebelumnya dari (bagian dari) jawaban ini :

(g?x)[]=x
(g?x)(a:b)=g(g?x$b)a

Ini adalah definisi rekursif ?, dengan kasus dasar menjadi daftar kosong. Karena []bukan nilai yang berguna, kita harus menukar definisi dan menggantinya dengan wildcard _atau argumen dummy y, menyimpan byte:

(g?x)(a:b)=g(g?x$b)a
(g?x)y=x

Dari jawaban yang sama, pertimbangkan definisi ini:

f#[]=[]
f#(a:b)=f a:f#b

Daftar kosong terjadi pada nilai kembali, sehingga kami dapat menyimpan dua byte dengan menukar kasus:

f#(a:b)=f a:f#b
f#x=x

Juga, urutan argumen fungsi terkadang dapat membuat perbedaan dengan memungkinkan Anda untuk menghapus spasi yang tidak perlu. Pertimbangkan versi sebelumnya dari jawaban ini :

h p q a|a>z=0:h p(q+2)(a-1%q)|1<2=1:h(p+2)q(a+1%p)

Ada ruang putih yang mengganggu antara hdan pdi cabang pertama. Kita dapat menyingkirkannya dengan mendefinisikan h a p qalih-alih h p q a:

h a p q|a>z=0:h(a-1%q)p(q+2)|1<2=1:h(a+1%p)(p+2)q

10

Jangan sia-siakan penjaga "jika tidak"

Pelindung terakhir yang merupakan catch-all True(lebih pendek 1>0) dapat digunakan untuk mengikat variabel. Membandingkan:

... |1>0=1/(x+y)
... |z<-x+y=1/z

... |1>0=sum l-sum m
... |s<-sum=s l-s m

Karena penjaga itu wajib dan jika tidak disia-siakan, sedikit yang diperlukan untuk membuat ini sepadan. Ini cukup untuk menyimpan sepasang paren atau mengikat ekspresi panjang-3 yang digunakan dua kali. Terkadang Anda bisa meniadakan penjaga untuk menjadikan kasus terakhir sebagai ekspresi yang paling baik menggunakan penjilidan.


10

Gunakan ,sebagai ganti &&penjaga

Berbagai kondisi dalam penjaga yang semua harus pegang dapat dikombinasikan dengan ,bukan &&.

f a b | a/=5 && b/=7 = ...
f a b | a/=5 ,  b/=7 = ...

2
Ini tidak harus menjadi kondisi juga, Anda dapat melakukan hal-hal seperti ini:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

Sintaks yang lebih pendek untuk deklarasi lokal

Kadang-kadang Anda perlu mendefinisikan fungsi atau operator lokal, tetapi membutuhkan banyak byte untuk menulis whereatau let…inmengangkatnya ke tingkat atas dengan menambahkan argumen tambahan.

g~(a:b)=2!g b where k!l=k:take(a-1)l++(k+1)!drop(a-1)l
g~(a:b)=let k!l=k:take(a-1)l++(k+1)!drop(a-1)l in 2!g b
g~(a:b)=2!g b$a;(k!l)a=k:take(a-1)l++((k+1)!drop(a-1)l)a

Untungnya, Haskell memiliki sintaks yang membingungkan dan jarang digunakan tetapi cukup singkat untuk deklarasi lokal :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

Pada kasus ini:

g~(a:b)|let k!l=k:take(a-1)l++(k+1)!drop(a-1)l=2!g b

Anda dapat menggunakan sintaks ini dengan deklarasi multi-pernyataan atau beberapa deklarasi, dan bahkan bersarang:

fun1 pattern1 | let fun2 pattern2 = expr2; fun2 pattern2' = expr2' = expr1
fun1 pattern1 | let fun2 pattern2 = expr2; fun3 pattern3 = expr3 = expr1
fun1 pattern1 | let fun2 pattern2 | let fun3 pattern3 = expr3 = expr2 = expr1

Ini juga berfungsi untuk variabel yang mengikat atau pola lain, meskipun penjaga pola cenderung lebih pendek untuk itu kecuali Anda juga fungsi yang mengikat.


3
Ini juga bekerja di dalam daftar comprehensions: [f 1|let f x=x+1].
Laikoni

10

Menghindari repeat n

-- 8 bytes, whitespace might be needed before and after
repeat n

-- 8 bytes, whitespace might be needed before
cycle[n]

-- 7 bytes, whitespace might be needed before and after, can be reused,
-- needs an assignment, n needs to be global
l=n:l;l

-- 7 bytes, never needs whitespace, n needs to derive from Enum,
-- n has to be short enough to be repeated twice
[n,n..]

Salah satu dari empat ekspresi itu akan menghasilkan daftar tak terhingga n.

Ini tip yang sangat spesifik tetapi bisa menghemat hingga 3 byte!


4
Jika nbersifat global, l=n:l;lpanjangnya sama dan berfungsi untuk (beberapa) ekspresi yang lebih panjang. (Mungkin perlu spasi putih.)
Ørjan Johansen

@ ØrjanJohansen Saya memasukkannya ke dalam pos. Terima kasih!
totallyhuman

10

Persyaratan lebih pendek ketika satu hasil adalah daftar kosong

Ketika Anda membutuhkan kondisional yang mengembalikan daftar Aatau daftar kosong []tergantung pada beberapa kondisi C, maka ada beberapa alternatif yang lebih pendek untuk konstruksi kondisional yang biasa:

if(C)then(A)else[] -- the normal conditional
last$[]:[A|C]      -- the golfy all-round-conditional
concat[A|C]        -- shorter and works when surrounded by infix operator
id=<<[A|C]         -- even shorter but might conflict with other infix operators
[x|C,x<-A]         -- same length and no-conflict-guarantee™
[0|C]>>A           -- shortest way, but needs surrounding parenthesis more often than not

Contoh: 1 , 2


Yang kedua telah Adan []beralih.
Ørjan Johansen

@ ØrjanJohansen Dikoreksi, terima kasih!
Laikoni

1
Aha! Tetapi *>memiliki fixity yang lebih tinggi daripada >>(masih agak rendah untuk kenyamanan.)
Ørjan Johansen

9

Aturan penguraian Lambda

Ekspresi lambda sebenarnya tidak membutuhkan tanda kurung di sekitarnya - itu hanya dengan rakus mengambil semuanya sehingga semuanya masih terurai, misalnya sampai

  • sebuah paren penutup - (foo$ \x -> succ x)
  • sebuah di - let a = \x -> succ x in a 4
  • akhir baris - main = getContents>>= \x -> head $ words x
  • dll ..

ditemui, dan ada beberapa kasus tepi aneh di mana ini dapat menghemat satu atau dua byte. Saya percaya \juga dapat digunakan untuk mendefinisikan operator, jadi ketika mengeksploitasi ini Anda akan memerlukan ruang ketika menulis lambda langsung setelah operator (seperti pada contoh ketiga).

Berikut adalah contoh di mana menggunakan lambda adalah hal tersingkat yang dapat saya pikirkan. Kode ini pada dasarnya terlihat seperti:

a%f=...
f t=sortBy(% \c->...)['A'..'Z']

9

Ganti letoleh lambda

Ini biasanya dapat mempersingkat definisi pembantu tunggal yang tidak dapat diikat dengan penjaga atau didefinisikan secara global karena beberapa alasan. Misalnya, ganti

let c=foo a in bar

oleh 3 byte lebih pendek

(\c->bar)$foo a

Untuk beberapa definisi tambahan, keuntungan mungkin lebih kecil, tergantung pada jumlah definisi.

let{c=foo a;n=bar a}in baz
(\c n->baz)(foo a)$bar a

let{c=foo a;n=bar a;m=baz a}in qux
(\c n m->qux)(foo a)(bar a)$baz a

let{c=foo a;n=bar a;m=baz a;l=qux a}in quux
(\c n m l->quux)(foo a)(bar a)(baz a)$qux a

Jika beberapa definisi merujuk ke yang lain, bahkan lebih sulit untuk menyimpan byte dengan cara ini:

let{c=foo a;n=bar c}in baz
(\c->(\n->baz)$bar c)$foo a

Peringatan utama dengan ini adalah yang letmemungkinkan Anda untuk mendefinisikan variabel polimorfik, tetapi lambdas tidak, seperti dicatat oleh @ChristianSievers. Sebagai contoh,

let f=length in(f["True"],f[True])

menghasilkan (1,1), tetapi

(\f->(f["True"],f[True]))length

memberikan kesalahan ketik.


1
Ini jarang penting, tetapi "setara secara semantik" terlalu banyak menjanjikan. Kami memiliki polymorpic let, jadi kami bisa melakukannya let f=id in (f 0,f True). Jika kami mencoba untuk menulis ulang ini dengan lambda itu tidak ketik cek.
Christian Sievers

@ChristianSievers Itu benar, terima kasih atas catatannya. Saya mengeditnya.
Zgarb

8

Ikat menggunakan penjaga

Saat mendefinisikan fungsi bernama, Anda bisa mengikat ekspresi ke variabel di penjaga. Sebagai contoh,

f s|w<-words s=...

melakukan hal yang sama dengan

f s=let w=words s in ...
f s=(\w->...)$words s

Gunakan ini untuk menghemat ekspresi berulang. Ketika ekspresi digunakan dua kali, itu memecah bahkan pada panjang 6, meskipun masalah spasi dan prioritas dapat mengubah itu.

(Dalam contoh, jika variabel asli stidak digunakan, ini lebih pendek untuk dilakukan

g w=...
f=g.words

tapi itu tidak benar untuk mengikat ekspresi yang lebih kompleks.)


Bukankah ini duplikat / kasus khusus dari jawaban ini ?
Lynn

@ Lynn Melihat ke belakang, ini adalah kasus khusus, tetapi ketika saya membaca jawaban itu, Justcontohnya membuat saya berpikir itu adalah pencocokan pola untuk mengekstrak dari sebuah wadah, daripada untuk menyimpan pada ekspresi.
xnor

8

Gunakan (0<$)sebagai ganti lengthuntuk perbandingan

Saat menguji apakah daftar alebih panjang dari daftar b, biasanya orang akan menulis

length a>length b

Namun mengganti setiap elemen dari kedua daftar dengan nilai yang sama, misalnya 0, dan kemudian membandingkan kedua daftar itu bisa lebih pendek:

(0<$a)>(0<$b)

Cobalah online!

Kurung diperlukan karena <$dan operator perbandingan ( ==, >, <=, ...) memiliki hak tingkat yang sama 4, meskipun dalam beberapa kasus lain mereka mungkin tidak diperlukan, menghemat bahkan lebih byte.


8

Singkat transpose

Untuk menggunakan transposefungsi Data.Listharus diimpor. Jika ini adalah satu-satunya fungsi yang membutuhkan impor, seseorang dapat menyimpan byte menggunakan foldrdefinisi berikut transpose:

import Data.List;transpose
e=[]:e;foldr(zipWith(:))e

Perhatikan bahwa perilaku ini hanya identik untuk daftar daftar dengan panjang yang sama.

Saya berhasil menggunakan ini di sini .


8

Dapatkan sufiks

Gunakan scanr(:)[]untuk mendapatkan sufiks dari daftar:

λ scanr(:)[] "abc"
["abc","bc","c",""]

Ini jauh lebih pendek daripada tailssesudahnya import Data.List. Anda dapat melakukan awalan dengan scanr(\_->init)=<<id(ditemukan oleh Ørjan Johansen).

λ  scanr(\_->init)=<<id $ "abc"
["","a","ab","abc"]

Ini menghemat lebih dari satu byte

scanl(\s c->s++[c])[]

Mungkin scanl(flip(:))[] "abc"= ["","a","ba","cba"]juga layak disebutkan - terkadang awalan yang mundur tidak masalah.
Lynn

3
Ørjan Johansen menemukan alternatif satu byte yang lebih pendek untuk awalan:scanr(\_->init)=<<id
Laikoni
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.