Haskell'de golf için ipuçları


73

Haskell'de golf oynamak için hangi genel ipuçlarınız var? Genel olarak golf problemlerini kodlamak için uygulanabilecek fikirleri arıyorum, en azından Haskell'e özgü. Lütfen cevap başına sadece bir ipucu gönderin.


Haskell'de golf oynamaya yeniyseniz, lütfen Haskell'deki Golf Kuralları Kılavuzuna bakın . Ayrıca bir Haskell sohbet odası var: Monads and Men .


1
Şu ana kadarki cevapları görünce, Haskell'in kod golf oynamak için iyi bir dil olup olmadığı konusunda şüphem var mı?
Animesh 'CODER'

10
Neden cevap başına sadece bir ipucu? Ayrıca, her dil golf oynamak için iyi bir dildir. Sadece her zaman kazanmayı beklemeyin.
unclemeat

6
@unclemeat Bu yolla, insanlar sadece kötü cevapları kızdırmadan en iyileri, sadece aynı adam tarafından aynı cevabı yazdıkları için yükseltebilirler.
MasterMastic

3
Özel istek, String sıkıştırma.
J Atkin,

Bu muhtemelen bir anwer olarak uygun değil, ama yine de buraya eklemek istiyorum: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
kusur

Yanıtlar:


45

İkili işlevler yerine infix işleçlerini tanımlayın

Bu, tanım veya çağrı başına genellikle bir veya iki boşluk kazandırır.

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

vs.

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

1 baytlık operatörler için semboller !, #, %, &, ve ?. Diğer tüm ASCII noktalama işaretleri, zaten Prelude (örneğin $) tarafından operatör olarak tanımlanmıştır veya Haskin'in sözdiziminde (örneğin @) özel bir anlamı vardır .

Beşten fazla operatöre ihtiyacınız varsa, yukarıdakilerin kombinasyonlarını (örneğin !#, Unicode noktalama karakterleri gibi) kullanabilirsiniz (bunlar UTF-8'deki 2 baytın tümü):

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

11
Not: Bu, üç veya daha fazla argüman içeren işlevler için de kullanılabilir. (x!y)z=x+y*zve (x#y)z u=x*z+y*uher ikisi de beklendiği gibi çalışıyor.
Zgarb

3
Bu aynı zamanda fonksiyon argümanları, örneğin kullanılabilir \f g(!)x y->f g!x yyerine\f g j x y->j(f g)(x y)
Esolanging Meyve

2
Bazen parantez kullanmak zorunda kalırsanız, unary fonksiyonlarını ikili operatörlere g x=…;g(f x)_?x=…;0!f x
çevirmek faydalı olabilir

29

Uygun olduğunda, anlamsız (veya-ücretsiz) gösterim kullanın

Genellikle bir veya iki parametreli bir fonksiyon noktadan serbest yazılabilir.

Öyleyse, elemanları değiştirilen bir tuna listesinin aranması naif bir şekilde şöyle yazılır:

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

(tür ne yaptığını anlamanıza yardımcı olmak için oradadır.)

amaçlarımız için bu çok daha iyi:

revlookup=(.map swap).lookup

28

Liste monadini kullan

Hızlı bir inceleme:

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

Örnekler:

  • Bir listeyi iki kez tekrarlamak

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Daha kısa concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • Daha kısa concat+ liste anlama

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • Kartezyen ürün

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Bir kafes üzerindeki koordinatların listesi

    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
Yararlı bulduğum başka bir kullanım [0..b]>>[a]yerine replicate a b.
Buğday Sihirbazı,

3
@WheatWizard a<$[1..b]için daha kısa replicate.
Lynn

Kullanılması =<<Almak kuvvetleri Control.Monad. Başka bir nedenden ötürü buna ihtiyacınız yoksa, argümanları değiştirmek ve kullanmak >>=daha özlü görünmektedir.
dfeuer

OTOH, Data.Traversableyine de ihtiyacınız olursa , Kartezyen ürün örneğine kısaltılabilir for["Hh","io",".!"]id.
dfeuer

2
(=<<)aslında Prelude'de ! Çok kullandım.
Lynn

28

Şartlı olmayan koruyucuları kullanın:

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

Girintileri değil noktalı virgül kullan

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

Boole işlevleri için boole ifadeleri kullanın

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

(SO bunları ayrı olarak göndermeme izin verme konusunda acı veriyor)


2
ayrıca, &&liste içindeyken yapmak yerine çoklu koruma kullanın .
John Dvorak

İyi bir Jan - bunu bir cevap haline getirmelisin, buna oy vereceğim
bazzargh

5
İlk örnek True=>1>0
John Dvorak

1
ilk örnekte, demek istediğinizi varsayıyorumf a=if a>0 then 3 else 7
Cyoce

Muhafızlar, eğer tartışılmazsa bile çalışır.
Akangka

24

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

İnsanlar genellikle bu işlevin var olduğunu unutur - tüm stdinleri alır ve onu (saf) bir işleve uygular. Ben sık sık mainsatırları boyunca -code görüyorum

main=getContents>>=print.foo

süre

main=interact$show.foo

biraz daha kısa. Prelüdde olduğundan ithalat yapmaya gerek yok!


24

GHC 7.10 kullanın

Bunları içeren GHC'nin ilk versiyonu 27 Mart 2015 tarihinde yayınlandı .

Bu en son sürüm ve Prelude golf oynamak için yararlı olan bazı yeni eklentiler aldı:

(<$>)ve (<*>)operatörler

Bu kullanışlı operatörler Data.Applicativebunu başardı! <$>adildir fmapdeğiştirmek, böylece map f xve fmap f xile f<$>xher yerde ve bayt geri kazanmak. Ayrıca, listelerde <*>bu Applicativeörnekte kullanışlıdır :

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

(<$)operatör

x<$aeşittir fmap (const x) a; yani bir kabın içindeki her elemanı değiştiriniz x.

Bu genellikle güzel bir alternatiftir replicate: 4<$[1..n]şundan kısa replicate n 4.

Katlanabilir / Gezilebilir Teklif

Aşağıdaki işlevler listelerde [a]çalışmaktan genel Foldabletürlere yükseltildi t a:

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

Bu, şimdi "aynı anda Maybe aen fazla bir öğeye sahip listeler" gibi davrandıkları için üzerinde çalıştıkları anlamına gelir . Örneğin, null Nothing == Trueya da sum (Just 3) == 3. Benzer şekilde, değerler için length0 Nothing, Justdeğerler için 1 döndürür . x==Just yYazmak yerine yazabilirsiniz elem y x.

Bunları, \(a, b) -> [b]ilk önce aramışsınız gibi çalışan tülelere de uygulayabilirsiniz . Neredeyse tamamen yararsız, ama or :: (a, Bool) -> Boolbirden karakter kısadır sndve elem bdaha kısadır (==b).snd.

Monoid fonksiyonları memptyvemappend

Çoğu zaman bir hayat kurtarıcı değildir, ancak türünü çıkartabilirseniz mempty, bir bayttan daha kısadır Nothing, yani bu var.


5
+1 '<$> `hakkında bir şeyler duymak ve <*>Prelude! Kod golf olmadığında bile faydalı olmalı (uygulayıcı çok uzun bir kelimedir).
ankh-morpork,

Düz değiştirme konusunda uyarı: Eğer dil sürümünüz zorluktan daha yeni ise, çözümünüz kazanmak için uygun değildir. Mevcut cevaplarınızı veya cevaplarınızı güncellemek istiyorsanız, mevcut çözümünüzün üzerine yazmayın. Bir ek yaz.
John Dvorak

4
Komik orada [1..2]var. bu sadece[1,2]
gururlu haskeller

2
Aynı versiyonda da var <*gelen Applicativelisteler için hangi xs <* ys == concatMap (replicate (length ys)) xs. Bu, hangisinden farklı xs >> ysveya xs *> yshangisidir concat (replicate (length ys)) xs? puredaha kısa returnolan bu noktada da geldi.
Angs,

2
Şimdi <>yerine kullanabilirsiniz mappend, şimdi (GHC 8.4.1 ile) 'nin bir parçası Prelude.
ბიმო

22

Kullanım 1<2yerine Trueve 1>2yerine False.

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

3
Bu gerçekten Haskell'e özgü değil: hemen hemen Boole türüne sahip her dilin, diğer türlerin gerçek ve sahte değerlerinin aksine geçerlidir.
Peter Taylor

Bunu kimse açıklayabilir mi?
MasterMastic

2
Bu iyi bir örnek değil, sadece golf gibi f=max 10.
Gurur haskeller 17:14

@MasterMastic bu sadece if(true)diğer dillerde yazıyor . prelüdde, aksi halde aslında boole değeridir True.
Gurur haskeller

2
@PeterTaylor Sanırım bu, ilk kullanmayı öğrendiğim gibi, yeni haskellianlar (benim gibi) için hala değerli otherwise.
kusur

20

Liste anlamalarını kullanın (akıllıca)

Herkes yararlı bir sözdizimi olduğunu bilir, genellikle map+ bir lambdadan daha kısadır :

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

Veya filter(ve isteğe bağlı mapolarak aynı anda):

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

Ama şimdi ve sonra kullanışlı olan bazı garip kullanımları var. Birincisi, bir liste kavramasının hiç <-ok içermesi gerekmez :

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

Bunun yerine if p then[x]else[], yazabilirsiniz [x|p]. Ayrıca, bir koşulu sağlayan bir listedeki öğelerin sayısını saymak için, sezgisel olarak şunu yazacaksınız:

length$filter p x

Ancak bu daha kısa:

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

Bunların hepsini daha önce kullanmıştım, ancak onları buraya koymayı düşünmedim.
Gurur haskeller

18

Bilin ki Prelude

GHCi'yi ateşleyin ve Prelude belgelerine göz atın . Ne zaman kısa bir isme sahip bir işlevi geçtiğinizde, yararlı olabileceği bazı durumlara bakmak için ödeme yapabilir.

Örneğin, bir dizgiyi s = "abc\ndef\nghi"boşlukla ayrılmış bir dizgeye dönüştürmek istediğinizi varsayalım "abc def ghi". Bariz yolu:

unwords$lines s

Ama kötüye kullanırsan daha iyisini yapabilirsin maxve gerçeği \n < space < printable ASCII:

max ' '<$>s

Başka bir örnek, lex :: String -> [(String, String)]oldukça gizemli bir şey yapan:

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

fst=<<lex sBoşluğu atlayarak, ilk dizgiyi bir dizgeden almaya çalışın . İşte kullanan henkma verdiği zekice bir çözümdür lex.showüzerinde Rationaldeğerler.


16

Sabit bir değer eşleştir

Bir liste anlama, sabit bir şekilde eşleşmeyi düzenleyebilir.


[0|0<-l]

Bu, bir listenin 0'larını çıkarır l, yani içinde olduğu kadar 0'ın bir listesini yapar l.


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

Bu , boş listeye giren 1öğelerin olduğu kadar çoğunun listesini yapar ( ek olarak kullanılır ). Bu elemanları saymak için uygulayın .lf<$>mapsum

Karşılaştırmak:

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

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

Desen eşleşmesinin bir parçası olarak bir sabit kullanılabilir. Bu, ilk girişi olan tüm dosyaların ikinci girişlerini ayıklar 0.


Bunların hepsinin bir değişkenin değerini değil, gerçek bir değişmezi gerektirdiğini unutmayın. Örneğin, let x=1 in [1|x<-[1,2,3]]çıktısı [1,1,1]değil [1], dış için xbağlanma yazılır.


14

wordsUzun bir dize listesi yerine kullanın . Bu gerçekten Haskell'e özgü değil, diğer dillerin de benzer hileleri var.

["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

Monadik fonksiyonlarınızı bilin

1)
monadik fonksiyonları kullanarak simüle eder mapM.

çoğu zaman kod sahip olacak sequence(map f xs), fakat bununla değiştirilebilir mapM f xs. Sadece sequenceyalnız kullanırken bile o zaman daha uzun mapM id.

2)
kullanarak işlevleri birleştirmek (>>=)(veya(=<<) )

işlev monad sürümü (>>=)şöyle tanımlanır:

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

Bir boru hattı olarak ifade edilemeyen fonksiyonlar oluşturmak için faydalı olabilir. örneğin, \x->x==nub xdaha uzun nub>>=(==)ve \t->zip(tail t)tdaha uzun tail>>=zip.


+1 - İşlevler için bir monad örneği olduğunu bile bilmiyordum. bu çok kullanışlı olabilir :)
Jules

2
Yan Not: parçası olduğu olsa Applicativedeğil Monadyönelik uygulama var puredaha kısadır hem de constaslında daha önce bana yardımcı oldu.
ბიმო

14

Bağımsız değişkenler tanımlardan daha kısa olabilir

Az önce henkma tarafından çok merak uyandırıldım .

Cevabınızdaki bir yardımcı işlev f, cevabınızda başka bir yerde kullanılmayan ve fbir kez çağrılan bir işleci kullanıyorsa, işleci bir argüman haline getirin f.

Bu:

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

Bundan iki bayt daha uzun:

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

12

Eksileri operatörünü kullanın (:)

listeleri birleştirirken, ilk uzunluk 1 ise, :bunun yerine kullanın.

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

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

4
Eğer örneğin listenin başında tek öğelerin herhangi bir sayı için kullanmaya devam edebilir bu yüzden, önce sağ taraf olduğunu belirtmek gerekir 1:2:3:xziyade [1,2,3]++x.
Jules,

11

Geri tepme düğmelerini çok sık kullanmayın. Geri tepmeler önek işlevlerinin bölümlerini yapmak için harika bir araçtır, ancak bazen yanlış kullanılabilir.

Bir zamanlar birisinin bu alt ifadeyi yazdığını gördüm:

(x`v`)

Her ne kadar aynı olsa da v x.

Başka bir örnek, (x+1)`div`y karşıt olarak yazmaktır div(x+1)y.

Etrafında divve elemdaha sık olduğunu görüyorum, çünkü bu işlevler genellikle normal kodda ek olarak kullanılır.


Önek fonksiyonlarının bölümlerini yapmak mı demek istiyorsun ?
Cyoce

@Cyoce Evet, elbette
gururlu haskeller

11

Desen koruyucuları kullanın

letTanımladığınız bir fonksiyonun argümanlarını yok eden bir veya lambdadan daha kısadırlar . Eğer böyle bir şey gerektiğinde, bu yardımcı olur fromJustden Data.Maybe:

f x=let Just c=… in c

daha uzun

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

daha uzun

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

daha uzun

f x|Just c<-…=c

Aslında, yapısızlaştırma yerine düz eski bir değeri bağlarken bile daha kısadırlar: xnor'ın ipucuna bakınız .


Eh, lambda bir dolar işareti ihtiyacı yok ve bu değişim bu uzunlukları ve bir sonraki snippet aynı kılan görünüyor
gururlu haskeller 26:15

1
Sanırım easlında bir belirteç değil $, ondan önce ihtiyaç duyan daha uzun bir ifade , genellikle durum böyledir.
Lynn,

11

Daha kısa koşullu

last$x:[y|b]

eşittir

if b then y else x

İşte nasıl çalışıyor:

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

Olmalı mı if b then y else x?
Akangka

@Hristiyan Irlanda İyi yakaladınız, evet.
xnor

boolBir liste kavrayışına ihtiyacınız olmadığından kullanmak daha kısa olmaz
mıydı

@ Patates44 Bu Data.Bool'da, hangi byte'lar almak istiyor?
xnor

11

Eksi işareti ile çalışmak

Eksi işareti -birçok sözdizimi kurallarına sinir bozucu bir istisnadır. Bu ipucu Haskell'de olumsuzlama ve çıkarma ifade etmenin bazı kısa yollarını listeler. Bir şeyi kaçırdıysam lütfen bana bildirin.

olumsuzluk

  • Bir ifadeyi olumsuzlamak için e, sadece yapın -e. Mesela -length[1,2]verir -2.
  • Eğer ehatta orta derecede karmaşık, sen etrafında parantez gerekir e, ancak genellikle etrafında taşıyarak byte kaydedebilirsiniz: -length(take 3 x)daha kısadır-(length$take 3 x) .
  • Eğer eöncesinde =veya bir infix operatörü fixity 6'dan küçük bir boşluğa ihtiyacınız vardır: daha küçük olup olmadığını f= -2tanımlar fve k< -2test eder . Sertlik 6 veya daha büyükse, parens gerekir: verir . Bunlardan kurtulmak için genellikle işleri yeniden düzenleyebilirsiniz: örneğin, yerine .k-22^^(-2)0.25-k>2k< -2
  • Benzer şekilde, eğer ! bir operatör ise, kesinliği en fazla 6 (yani verir ) ve aksi halde (yani verir ) -a!bgibi ayrıştırılır . Kullanıcı tanımlı operatörlerin ve backticked işlevlerin varsayılan sabitliği 9'dur, bu nedenle ikinci kurala uyuyorlar.(-a)!b!-1<1True-(a!b)-[1,2]!!0-1
  • Olumsuz bir işleve dönüştürmek ( mapvb ile kullanmak için) bölümünü kullanın (0-).

Çıkarma

  • Çıkartılan bir işlevi elde etmek için , eklenen kbölümü kullanın . oldukça karmaşık bir ifade bile olabilir: beklendiği gibi çalışır.(-k+)-kk(-2*length x+)
  • 1'i çıkarmak için pred, her iki tarafta bir boşluk gerektirmediği sürece kullanın . Bu nadir görülür ve genellikle ile olur until, çünkü ya bir kullanıcı tanımlı işlevi map pred xile değiştirilebilir pred<$>xve iterate pred xtarafından [x,x-1..]. Ve bir yeriniz varsa f pred x, fyine de bir ek işlevi olarak tanımlamanız gerekir . Bu ipucuna bakın .

11

İşlev tanımlarını ve / veya argümanları yeniden düzenlemeyi deneyin

Bir işlev tanımındaki desen eşleştirme durumlarının sırasını değiştirerek bazen birkaç bayttan tasarruf edebilirsiniz. Bu tasarruflar ucuzdur, ancak gözden kaçırılması kolaydır.

Örnek olarak, bu cevabın (bir kısmının) önceki bir versiyonunu düşünün :

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

Bu özyinelemeli bir tanımdır ?, temel durum boş listedir. Yana []yararlı bir değer değil, biz tanımlarını takas ve joker ile değiştirmek gerekir _ya da kukla argüman ybir byte tasarrufu:

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

Aynı cevaptan şu tanımı düşünün:

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

Boş liste dönüş değerinde gerçekleşir, bu nedenle vakaları değiştirerek iki bayt kaydedebiliriz:

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

Ayrıca, işlev bağımsız değişkenlerinin sırası, gereksiz beyaz boşlukları kaldırmanıza izin vererek bazen bir fark yaratabilir. Bu cevabın daha önceki bir versiyonunu düşünün :

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

İlk dalda hve parasında sinir bozucu bir boşluk var . Biz tanımlayarak ondan kurtulmak yapabilirsiniz h a p qyerine 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

"Aksi" korumayı boşa harcamayın

Bir değişkeni bağlamak için her şeyi yakalayabilen True(en kısa olan 1>0) bir son koruma kullanılabilir. Karşılaştırmak:

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

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

Güvenlik görevlisi zorunlu ve boşa harcanmış olduğundan, buna değecek kadar az şey gerekir. Bir çift ebeveyni kurtarmak veya iki kez kullanılan bir uzunluk-3 ifadesini bağlamak yeterlidir. Bazen, son davayı bir bağlayıcıyı en iyi kullanan ifade haline getirmek için koruyucuları ihmal edebilirsiniz.


10

Muhafızlar ,yerine kullanın&&

Bir bekçi içindeki herkesin tutması gereken çok sayıda koşul ,yerine birleştirilebilir &&.

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

2
Koşullar da olması gerekmiyor, böyle şeyler yapabilirsiniz:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

Yerel bildirimler için daha kısa sözdizimi

Bazen yerel bir işlev veya işleç tanımlamanız gerekir, ancak fazladan argümanlar ekleyerek yazmak whereveya let…inveya en üst düzeye çıkarmak çok bayt tutar .

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

Neyse ki, Haskell'in yerel bildirimler için kafa karıştırıcı ve nadiren kullanılan ancak makul derecede kısa sözdizimi vardır :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

Bu durumda:

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

Bu sözdizimini multi-ifade bildirimleri veya çoklu bildirimler ile kullanabilirsiniz, hatta yuvalar:

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

Aynı zamanda, ciltleme değişkenleri veya diğer kalıplar için de çalışsa da, desen korumaları da bağlama işlevleri olmadıkça bunun için daha kısa olma eğilimindedir.


3
Bu aynı zamanda liste comprehensions içinde çalışır: [f 1|let f x=x+1].
Laikoni,

10

Önlemek 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..]

Bu dört ifadeden herhangi biri sonsuz bir liste üretecek n .

Çok özel bir ipucu ama 3 byte'a kadar tasarruf sağlayabilir !


4
Eğer ngeneldir, l=n:l;laynı uzunlukta ve (bazı) uzun ifadeler için çalışır. (Yine de boşluklara ihtiyaç duyabilir.)
Ørjan Johansen

@ ØrjanJohansen Ben yazıya dahil ettim. Teşekkürler!
tamamen insan

10

Bir sonuç boş liste olduğunda daha kısa koşullamalar

Bazı koşullara bağlı olarak listeyi Aveya boş listeyi döndüren bir koşulluya ihtiyacınız olduğunda, normal koşullu yapılara bazı daha kısa alternatifler vardır:[]C

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

Örnekler: 1 , 2


İkincisi değişti Ave []değişti.
Ørjan Johansen

@ ØrjanJohansen Düzeltildi, teşekkürler!
Laikoni

1
Aha! Ancak *>daha yüksek bir sabitliğe sahiptir >>(konfor için hala biraz düşük).
Ørjan Johansen

9

Lambda ayrıştırma kuralları

Bir lambda ifadesi aslında etrafındaki parantezlere ihtiyaç duymaz - sadece açgözlülükle her şeyi alır böylece her şey hala ayrışır, örneğin

  • kapanış paren - (foo$ \x -> succ x)
  • bir - let a = \x -> succ x in a 4
  • Hattın sonu - main = getContents>>= \x -> head $ words x
  • vb..

ile karşılaşılır ve bunun size bir veya iki bayt kazandırabileceği bazı garip kenar durumları vardır. inanıyorum\Operatörleri tanımlamak için de kullanılabileceğine , dolayısıyla bundan faydalanırken, doğrudan bir operatörden sonra bir lambda yazarken bir boşluğa ihtiyacınız olacak (üçüncü örnekte olduğu gibi).

İşte lambda kullanmanın, bulabildiğim en kısa şey olduğu bir örnek . Kod temelde şöyle görünür:

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

9

letLambda ile değiştirin

Bu genellikle bir bekçi ile bağlanamayan veya bir nedenden dolayı global olarak tanımlanamayan yalnız bir yardımcı tanımı kısaltabilir . Örneğin, değiştirin

let c=foo a in bar

3 byte kısa

(\c->bar)$foo a

Birden fazla yardımcı tanım için, tanım sayısına bağlı olarak kazanç muhtemelen daha küçüktür.

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

Tanımların bazıları diğerlerine atıfta bulunuyorsa, baytları bu şekilde kaydetmek daha zordur:

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

Bununla ilgili ana uyarı letpolimorfik değişkenleri tanımlamanıza izin verir, ancak lambdalar @HristianSievers tarafından belirtildiği gibi değildir. Örneğin,

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

sonuçlanır (1,1), ancak

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

bir tür hata veriyor.


1
Nadiren önemlidir, ancak "anlamsal olarak eşdeğer" biraz fazla vaat ediyor. Polimorpik letolduk, böylece yapabiliriz let f=id in (f 0,f True). Bunu lambda ile yeniden yazmaya çalışırsak check yazmaz.
Christian Sievers

@ ChristianSievers Bu doğru, not için teşekkürler. Düzenledim.
Zgarb

8

Korumaları kullanarak bağlama

Adlandırılmış bir işlevi tanımlarken, bir ifadeyi bir korumadaki bir değişkene bağlayabilirsiniz. Örneğin,

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

aynısını yapar

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

Tekrarlanan ifadelerden tasarruf etmek için bunu kullanın. İfade iki kez kullanıldığında, uzunluk 6'da bile kopar, aralık ve öncelikli konular bunu değiştirebilir.

(Örnekte, orijinal değişken skullanılmıyorsa, yapılması daha kısadır

g w=...
f=g.words

ama bu daha karmaşık ifadeleri bağlamak için doğru değil.)


Bu, bu cevabın bir kopyası / özel durumu değil midir?
Lynn,

@Lynn Geriye dönüp baktığımda, özel bir durum, ancak bu cevabı okuduğumda, Justörnek, bir ifadede depolamak yerine, bir kaptan ayıklanmanın desen eşleşmesi olduğunu düşündürdü.
xnor

8

Karşılaştırmalar (0<$)yerine kullanınlength

Bir listenin bir listeden auzun olup olmadığını test ederken b, genellikle

length a>length b

Bununla birlikte, her iki listenin her bir elemanını aynı değerle değiştirmek, örneğin 0, ve bu iki listeyi karşılaştırmak daha kısa olabilir:

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

Çevrimiçi deneyin!

Parantez çünkü ihtiyaç vardır <$ve karşılaştırma operatörleri ( ==, >, <=diğer bazı durumlarda da gerekli olabilir gerçi, ...) daha bayt tasarrufu, aynı öncelik seviyesini 4 var.


8

Daha kısa transpose

Kullanmak için transposeişlevi Data.Listithal edilmesi gerekir. İçe aktarma gerektiren tek işlev buysa, aşağıdaki foldrtanımları kullanarak bir bayt kaydedebilir transpose:

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

Davranışın yalnızca aynı uzunluktaki listelerin bir listesi için aynı olduğunu unutmayın.

Bunu burada başarıyla kullandım .


8

Sonekleri al

scanr(:)[]Bir listenin son eklerini almak için kullanın :

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

Bu tailsondan çok daha kısa import Data.List. Önekleri scanr(\_->init)=<<id(Ørjan Johansen tarafından bulunan) ile yapabilirsiniz.

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

Bu bir bayttan tasarruf sağlar

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

Belki de scanl(flip(:))[] "abc"= ["","a","ba","cba"]söylemeye değer - bazen öneklerin geriye doğru olması önemli değil.
Lynn,

3
Ørjan Johansen, önekler için bir bayt kısa alternatif buldu:scanr(\_->init)=<<id
Laikoni
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.