เคล็ดลับสำหรับการเล่นกอล์ฟใน Haskell


73

คุณมีเคล็ดลับอะไรสำหรับการเล่นกอล์ฟใน Haskell ฉันกำลังมองหาความคิดที่สามารถนำไปใช้กับปัญหารหัสกอล์ฟโดยทั่วไปซึ่งอย่างน้อยค่อนข้างเฉพาะกับ Haskell กรุณาโพสต์เพียงหนึ่งเคล็ดลับต่อคำตอบ


ถ้าคุณยังใหม่กับการเล่นกอล์ฟใน Haskell, โปรดดูได้ที่เป็นคู่มือกฎกอล์ฟใน Haskell นอกจากนี้ยังมีเฉพาะ Haskell ห้องแชท: ของ Monads และผู้ชาย


1
เมื่อเห็นจำนวนคำตอบจนถึงตอนนี้ฉันสงสัยว่า Haskell เป็นภาษาที่ดีสำหรับการตีกอล์ฟหรือไม่?
Animesh 'the CODER'

10
ทำไมเพียงหนึ่งเคล็ดลับต่อคำตอบ? นอกจากนี้ทุกภาษาเป็นภาษาที่ดีสำหรับการเล่นกอล์ฟ อย่าคาดหวังว่าจะชนะเสมอไป
ลุง

6
@unclemeat ด้วยวิธีนี้ผู้คนสามารถโหวตคนที่ดี ๆ ขึ้นไปด้านบนได้โดยไม่ต้องอัปเดตคนที่ไม่ดีเท่านั้นเพราะพวกเขาเขียนโดยคนเดียวกันในคำตอบเดียวกัน
MasterMastic

3
คำขอพิเศษ, การบีบอัดสตริง
J Atkin

นี่อาจไม่เหมาะกับ anwer แต่ฉันยังต้องการเพิ่มที่นี่: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

คำตอบ:


45

กำหนดตัวดำเนินการมัดแทนฟังก์ชั่นไบนารี

โดยปกติจะบันทึกหนึ่งหรือสองช่องว่างต่อการกำหนดหรือการโทร

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

เมื่อเทียบกับ

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

สัญลักษณ์พร้อมใช้งานสำหรับผู้ประกอบการ 1 ไบต์มี!, #, %, และ& ?เครื่องหมายวรรคตอน ASCII อื่นทั้งหมดถูกกำหนดเป็นโอเปอเรเตอร์โดยโหมโรง (เช่น$) หรือมีความหมายพิเศษในไวยากรณ์ของ Haskell (เช่น@)

หากคุณต้องการโอเปอเรเตอร์มากกว่าห้าตัวคุณสามารถใช้การรวมกันของ!#อักขระด้านบนเช่นหรืออักขระเครื่องหมายวรรคตอน Unicode บางตัวเช่นอักขระเหล่านี้ (ทั้ง 2 ไบต์ใน UTF-8):

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

11
หมายเหตุ: สามารถใช้กับฟังก์ชันที่มีอาร์กิวเมนต์ตั้งแต่สามข้อขึ้นไป (x!y)z=x+y*zและ(x#y)z u=x*z+y*uทำงานได้ตามที่คาดไว้
Zgarb

3
นอกจากนี้ยังสามารถใช้สำหรับการขัดแย้งฟังก์ชันเช่น\f g(!)x y->f g!x yแทน\f g j x y->j(f g)(x y)
Esolanging Fruit

2
บางครั้งมันมีประโยชน์ในการเปลี่ยนฟังก์ชัน unary เป็นตัวดำเนินการไบนารีหากคุณจำเป็นต้องใช้วงเล็บ - g x=…;g(f x)นานกว่า_?x=…;0!f x
Angs

29

ใช้สัญกรณ์ไร้จุดหมาย (หรือ - ฟรี) ตามความเหมาะสม

บ่อยครั้งที่ฟังก์ชั่นที่มีพารามิเตอร์หนึ่งหรือสองพารามิเตอร์สามารถเขียนได้อย่างอิสระ

ดังนั้นการค้นหารายการของสิ่งอันดับที่มีการสลับองค์ประกอบถูกเขียนอย่างไร้เดียงสาว่า:

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

(ประเภทมีเพื่อช่วยให้คุณเข้าใจสิ่งที่กำลังทำอยู่)

สำหรับจุดประสงค์ของเรานี้ดีกว่ามาก:

revlookup=(.map swap).lookup

28

ใช้รายการ monad

ตรวจสอบอย่างรวดเร็ว:

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

ตัวอย่าง:

  • ทำซ้ำรายการสองครั้ง

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • สั้น concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • concatความเข้าใจสั้นกว่า+ รายการ

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • ผลิตภัณฑ์คาร์ทีเซียน

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • รายการพิกัดบนขัดแตะ

    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
ใช้ Annother ผมพบว่ามีประโยชน์แทน[0..b]>>[a] replicate a b
ข้าวสาลีตัวช่วยสร้าง

3
@WheatWizard แม้จะสั้นลงa<$[1..b] replicate
ลินน์

การใช้กองกำลังที่คุณจะนำเข้า=<< Control.Monadหากคุณไม่ต้องการเหตุผลดังกล่าวการแลกเปลี่ยนข้อโต้แย้งและการใช้>>=ดูเหมือนจะกระชับกว่า
dfeuer

OTOH หากคุณต้องการData.Traversableอย่างไรก็ตามตัวอย่างผลิตภัณฑ์คาร์ทีเซียนสามารถย่อให้เล็กลงfor["Hh","io",".!"]idได้
dfeuer

2
(=<<)อยู่ในพรีลูดจริง ๆ ! ฉันใช้มันมาก
Lynn

28

ใช้ยามไม่เงื่อนไข:

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

ใช้เครื่องหมายอัฒภาคไม่เยื้อง

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

ใช้นิพจน์บูลีนสำหรับฟังก์ชั่นบูลีน

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

(ดังนั้นจึงเป็นความเจ็บปวดเกี่ยวกับการให้ฉันโพสต์เหล่านี้แยกต่างหาก)


2
อีกทั้งใช้การ์ดหลายยามแทน&&เมื่ออยู่ในรายการความเข้าใจ
John Dvorak

Good one Jan - คุณควรตอบคำถามนี้ฉันจะลงคะแนนให้
bazzargh

5
ตัวอย่างแรกสามารถทำให้สั้นลงได้อีกโดยTrue=>1>0
John Dvorak

1
ในตัวอย่างแรกฉันถือว่าคุณหมายความว่าf a=if a>0 then 3 else 7
Cyoce

ยามยังใช้งานได้หากไม่มีข้อโต้แย้ง
Akangka

24

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

ผู้คนมักลืมว่ามีฟังก์ชั่นนี้อยู่ - มันคว้า stdin ทั้งหมดและนำไปใช้กับฟังก์ชั่น (บริสุทธิ์) ฉันมักจะเห็นmainรหัสในบรรทัดของ

main=getContents>>=print.foo

ในขณะที่

main=interact$show.foo

ค่อนข้างสั้น มันอยู่ในโหมโรงดังนั้นไม่จำเป็นต้องนำเข้า!


24

ใช้ GHC 7.10

รุ่นแรกของ GHC ที่มีสิ่งนี้ได้รับการปล่อยตัวเมื่อวันที่27 มีนาคม 2015

เป็นรุ่นล่าสุดและโหมโรงได้รับการเพิ่มใหม่บางอย่างที่มีประโยชน์สำหรับการเล่นกอล์ฟ:

(<$>)และ(<*>)ผู้ประกอบการ

ผู้ประกอบการที่มีประโยชน์เหล่านี้มาจากData.Applicativeทำใน! <$>เป็นเพียงfmapเพื่อให้คุณสามารถแทนที่map f xและfmap f xด้วยf<$>xทุกที่และชนะกลับไบต์ นอกจากนี้ยัง<*>มีประโยชน์ในApplicativeกรณีสำหรับรายการ:

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

(<$)ผู้ประกอบการ

x<$aเทียบเท่ากับfmap (const x) a; xคือแทนที่ทุกองค์ประกอบในภาชนะโดย

นี้มักจะเป็นทางเลือกที่ดีที่จะreplicate: สั้นกว่า4<$[1..n]replicate n 4

ข้อเสนอแบบพับได้ / ผ่านได้

ฟังก์ชั่นต่อไปนี้ถูกยกขึ้นจากการทำงานกับรายการ[a]เป็นFoldableประเภททั่วไปt a:

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

ซึ่งหมายความว่าตอนนี้พวกเขายังทำงานMaybe aที่พวกเขาทำงานเหมือน "รายการที่มีองค์ประกอบมากที่สุด" ยกตัวอย่างเช่นหรือnull Nothing == True sum (Just 3) == 3ในทำนองเดียวกันlengthผลตอบแทน 0 สำหรับNothingและ 1 สำหรับJustค่า แทนการเขียนคุณสามารถเขียนx==Just yelem y x

คุณสามารถนำไปใช้กับสิ่งอันดับ (tuples) ซึ่งทำงานเหมือนกับว่าคุณโทรมา\(a, b) -> [b]ก่อน มันเกือบจะไร้ประโยชน์อย่างสมบูรณ์ แต่or :: (a, Bool) -> Boolเป็นหนึ่งในตัวละครที่สั้นกว่าsndและสั้นกว่าelem b(==b).snd

ฟังก์ชั่น Monoid memptyและmappend

ไม่บ่อยนักที่ช่วยชีวิต แต่ถ้าคุณสามารถอนุมานประเภทmemptyเป็นหนึ่งไบต์สั้นกว่าNothingดังนั้นจึงมี


5
+1 เป็นเรื่องที่ดีมากที่ได้ยินเกี่ยวกับ '<$>' และ<*>ทำให้เป็นการนำเสนอ! ที่ควรจะมีประโยชน์แม้ในขณะที่ไม่ใช่รหัสกอล์ฟ (การใช้งานเป็นคำที่ยาวเกินไป)
ankh-morpork

คำเตือนเกี่ยวกับการแทนที่แบบแบน: หากเวอร์ชันภาษาของคุณใหม่กว่าความท้าทายโซลูชันของคุณจะไม่มีสิทธิ์ได้รับรางวัล หากคุณต้องการอัปเดตคำตอบหรือคำตอบที่มีอยู่อย่าเขียนทับโซลูชันที่มีอยู่ของคุณ เขียนภาคผนวก
John Dvorak

4
ตลกมีอยู่[1..2]ในนั้น นั่นเป็นเพียง[1,2]
ภูมิใจ haskeller

2
ในรุ่นเดียวกันกับที่เรายังมี<*จากซึ่งสำหรับรายการคือApplicative xs <* ys == concatMap (replicate (length ys)) xsซึ่งแตกต่างจากxs >> ysหรือซึ่งเป็นxs *> ys ซึ่งสั้นกว่ามาที่จุดนี้ด้วย concat (replicate (length ys)) xspurereturn
Angs

2
ตอนนี้คุณสามารถใช้<>แทนmappendก็ตอนนี้ (กับ GHC 8.4.1) Preludeเป็นส่วนหนึ่งของ
ंმო

22

ใช้1<2แทนTrueและแทน1>2False

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

3
สิ่งนี้ไม่เฉพาะเจาะจงกับ Haskell: ใช้ได้กับทุกภาษาที่มีประเภท Boolean ซึ่งตรงข้ามกับความจริงและค่าเท็จประเภทอื่น ๆ
Peter Taylor

มีใครอธิบายเรื่องนี้ได้บ้าง
MasterMastic

2
นี่ไม่ใช่ตัวอย่างที่ดีฉันจะลองเล่นกอล์ฟนี้f=max 10ดู
ภูมิใจ haskeller

@MasterMastic นี่เป็นเพียงการเขียนif(true)ในภาษาอื่น Trueในโหมโรงมิฉะนั้นเป็นจริงค่าบูลีน
ภูมิใจ haskeller

2
@PeterTaylor ผมคิดว่านี้ยังคงเป็นที่มีคุณค่าสำหรับ haskellians ใหม่ (เช่นฉัน) otherwiseเป็นครั้งแรกที่ผมได้เรียนรู้ที่จะใช้
ข้อบกพร่อง

20

ใช้รายการความเข้าใจ (ในวิธีที่ฉลาด)

ทุกคนรู้ว่าพวกเขามีประโยชน์ไวยากรณ์มักจะสั้นกว่าmap+ แลมบ์ดา:

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

หรือfilter(และเป็นทางเลือกmapในเวลาเดียวกัน):

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

แต่มีผู้ใช้บางคนที่ใช้ประโยชน์ได้ง่ายในตอนนี้ สำหรับหนึ่งรายการความเข้าใจไม่จำเป็นต้องมี<-ลูกศรใด ๆเลย:

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

ซึ่งหมายความว่าif p then[x]else[]คุณสามารถเขียน[x|p]แทน นอกจากนี้หากต้องการนับจำนวนองค์ประกอบของรายการที่ตรงตามเงื่อนไขคุณต้องเขียนโดยสังหรณ์ใจ:

length$filter p x

แต่นี่จะสั้นกว่า:

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

ก่อนหน้านี้ฉันเคยใช้สิ่งเหล่านี้มาก่อน แต่ฉันไม่คิดว่าจะใส่ไว้ที่นี่
ภูมิใจ haskeller

18

รู้จักคุณ Prelude

ไฟขึ้น GHCi และเลื่อนดูเอกสารโหมโรง เมื่อใดก็ตามที่คุณข้ามฟังก์ชั่นที่มีชื่อสั้น ๆ ก็สามารถชำระเพื่อดูบางกรณีที่มันอาจจะมีประโยชน์

ตัวอย่างเช่นสมมติว่าคุณต้องการที่จะเปลี่ยนสตริงเป็นหนึ่งที่เป็นพื้นที่ที่แยกออกจากกันs = "abc\ndef\nghi" "abc def ghi"วิธีที่ชัดเจนคือ:

unwords$lines s

แต่คุณสามารถทำได้ดีกว่าถ้าคุณใช้วิธีการที่ผิดmaxและความจริงที่\n < space < printable ASCII:

max ' '<$>s

อีกตัวอย่างหนึ่งคือlex :: String -> [(String, String)]ซึ่งทำสิ่งที่ค่อนข้างลึกลับ:

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

ลองfst=<<lex sรับโทเค็นแรกจากสตริงโดยข้ามช่องว่าง นี่คือทางออกที่ฉลาดโดย henkma ที่ใช้lex.showกับRationalค่า


16

จับคู่กับค่าคงที่

รายการความเข้าใจสามารถจับคู่รูปแบบกับค่าคงที่


[0|0<-l]

นี้สารสกัดจาก 0 ของรายการlคือทำให้รายการของ 0 lมากที่สุดเท่าที่เป็นอยู่ใน


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

สิ่งนี้ทำให้ลิสต์ของมีจำนวนมาก1เท่ากับที่มีอิลิเมนต์lที่fจะนำไปสู่รายการที่ว่างเปล่า (ใช้<$>เป็นมัดmap) ใช้sumเพื่อนับองค์ประกอบเหล่านี้

เปรียบเทียบ:

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

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

ค่าคงที่สามารถใช้เป็นส่วนหนึ่งของการจับคู่รูปแบบ นี้แยกรายการที่สองของ tuples 0ทั้งหมดซึ่งเป็นรายการแรก


โปรดทราบว่าสิ่งเหล่านี้ทั้งหมดต้องการตัวอักษรคงที่จริงไม่ใช่ค่าของตัวแปร ตัวอย่างเช่นlet x=1 in [1|x<-[1,2,3]]จะส่งออก[1,1,1]ไม่ใช่[1]เพราะการxรวมภายนอกถูกเขียนทับ


14

ใช้wordsแทนรายการสตริงที่มีความยาว นี่ไม่เฉพาะเจาะจงกับ Haskell แต่ภาษาอื่นก็มีกลอุบายที่คล้ายกันเช่นกัน

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

รู้จักฟังก์ชั่น monadic ของคุณ

1) การจำลองฟังก์ชั่นการใช้เอก
mapM

จำนวนมากของรหัสครั้งจะมีแต่ก็สามารถถูกแทนที่ด้วยsequence(map f xs) mapM f xsแม้เมื่อใช้sequenceเพียงอย่างเดียวมันก็จะนานmapM idขึ้น

2)
รวมฟังก์ชั่นการใช้(>>=)(หรือ(=<<))

ฟังก์ชั่นเวอร์ชั่น monad of (>>=)ถูกกำหนดไว้ดังนี้:

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

มันจะมีประโยชน์สำหรับการสร้างฟังก์ชั่นที่ไม่สามารถแสดงเป็นไปป์ไลน์ ตัวอย่างเช่น\x->x==nub xมีความยาวมากกว่าnub>>=(==)และมีความยาวมากกว่า\t->zip(tail t)ttail>>=zip


+1 - ฉันไม่ได้ตระหนักว่ามีอินสแตนซ์ monad สำหรับฟังก์ชั่น ที่อาจจะมีประโยชน์มาก :)
จูลส์

2
ด้านหมายเหตุ: แม้ว่ามันจะเป็นส่วนหนึ่งของApplicativeและไม่มีMonadการใช้งานสำหรับpureเช่นกันซึ่งสั้นกว่าconstและจริงช่วยฉันก่อน
ბიმო

14

อาร์กิวเมนต์อาจสั้นกว่าคำจำกัดความ

ฉันเพียงแค่ outgolfed ในทางที่อยากรู้อยากเห็นมากโดยhenkma

หากฟังก์ชั่นเสริมfในคำตอบของคุณใช้ประกอบการที่ไม่ได้ใช้ที่อื่น ๆ ในคำตอบของคุณและจะเรียกว่าครั้งทำให้ผู้ประกอบการโต้แย้งของff

นี้:

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

สองไบต์ยาวกว่านี้:

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

12

ใช้โอเปอเรเตอร์ cons (:)

เมื่อทำการเชื่อมต่อรายการถ้าสิ่งแรกคือความยาว 1 ให้ใช้:แทน

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

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

4
เป็นที่น่าสังเกตว่ามันเป็นแบบเชื่อมโยงที่เหมาะสมเพื่อให้คุณสามารถใช้มันสำหรับจำนวนของรายการใด ๆ เดียวที่จุดเริ่มต้นของรายการเช่นมากกว่า1:2:3:x [1,2,3]++x
จูลส์

11

อย่าใช้ backticks บ่อยเกินไป Backticks เป็นเครื่องมือที่ยอดเยี่ยมสำหรับการทำส่วนของฟังก์ชั่นคำนำหน้า แต่บางครั้งสามารถนำไปใช้ในทางที่ผิด

เมื่อฉันเห็นมีคนเขียนข้อความย่อยนี้:

(x`v`)

v xแม้ว่ามันจะเป็นเช่นเดียวกับเพียง

อีกตัวอย่างหนึ่งคือการเขียนเมื่อเทียบกับ(x+1)`div`y div(x+1)y

ฉันเห็นมันเกิดขึ้นรอบ ๆdivและelemบ่อยขึ้นเพราะฟังก์ชั่นเหล่านี้มักจะถูกใช้เป็นมัดในรหัสประจำ


คุณไม่ได้ตั้งใจทำส่วนของฟังก์ชั่นนำหน้าใช่ไหม
Cyoce

@Cyoce ใช่แน่นอน
ภูมิใจ Haskeller

11

ใช้ยามรูปแบบ

มันสั้นกว่าletหรือแลมบ์ดาที่แยกส่วนของข้อโต้แย้งของฟังก์ชั่นที่คุณกำหนด สิ่งนี้จะช่วยเมื่อคุณต้องการอะไรfromJustจากData.Maybe:

f x=let Just c=… in c

นานกว่า

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

นานกว่า

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

นานกว่า

f x|Just c<-…=c

ในความเป็นจริงพวกเขากำลังสั้นลงแม้ในขณะที่มีผลผูกพันค่าเก่าธรรมดาแทนการถอนราก: ดูเคล็ดลับของ XNOR


แลมบ์ดาไม่ต้องการเครื่องหมายดอลลาร์และดูเหมือนว่าการเปลี่ยนแปลงนี้ทำให้ความยาวของมันและตัวอย่างต่อไปเหมือนกัน
ภูมิใจ haskeller

1
ฉันสมมติว่าeไม่ใช่โทเค็นเดียว แต่เป็นการแสดงออกที่ยาวกว่าที่ต้องการ$ก่อนซึ่งมักเป็นกรณี
ลินน์

11

เงื่อนไขที่สั้นกว่า

last$x:[y|b]

เทียบเท่ากับ

if b then y else x

นี่คือวิธีการทำงาน:

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

มันควรจะเป็นif b then y else xอย่างไร
Akangka

@ CristianIrwan จับได้ดีใช่
xnor

boolจะไม่ใช้ให้สั้นลงเพราะคุณไม่ต้องการความเข้าใจในรายการ
Potato44

@ Potato44 นั่นคือใน Data.Bool ซึ่งมีค่าใช้จ่ายเป็นไบต์ที่จะนำเข้า
xnor

11

การทำงานกับเครื่องหมายลบ

เครื่องหมายลบ-เป็นข้อยกเว้นที่น่ารำคาญสำหรับกฎไวยากรณ์หลายข้อ เคล็ดลับนี้แสดงวิธีสั้น ๆ ในการแสดงการปฏิเสธและการลบใน Haskell โปรดแจ้งให้เราทราบหากฉันพลาดอะไรบางอย่าง

การปฏิเสธ

  • จะลบล้างการแสดงออกเพียงแค่ทำe -eยกตัวอย่างเช่นให้-length[1,2]-2
  • หากeจะยิ่งซับซ้อนปานกลางคุณจะต้องวงเล็บรอบeแต่คุณจะสามารถบันทึกไบต์โดยการย้ายไปรอบ ๆ : จะสั้นกว่า-length(take 3 x)-(length$take 3 x)
  • หากeจะนำหน้าด้วย=หรือผู้ประกอบการมัดของfixityน้อยกว่า 6 คุณจะต้องพื้นที่: f= -2กำหนดfและk< -2การทดสอบถ้าน้อยกว่าk -2หาก fixity คือ 6 หรือสูงกว่าคุณจะต้อง parens: ช่วยให้2^^(-2) 0.25คุณมักจะสามารถจัดเรียงสิ่งที่จะได้รับการกำจัดเหล่านี้: ยกตัวอย่างเช่นทำแทน-k>2k< -2
  • ในทำนองเดียวกันถ้า!เป็นโอเปอเรเตอร์แล้ว-a!bจะถูกแยกวิเคราะห์ราวกับ(-a)!bว่า fixity ของ!มีค่าได้สูงสุด 6 (ดังนั้น-1<1ให้True) และ-(a!b)อื่น ๆ (เช่นนั้น-[1,2]!!0ให้-1) fixity เริ่มต้นของตัวดำเนินการที่ผู้ใช้กำหนดและฟังก์ชัน backticked คือ 9 ดังนั้นพวกเขาจึงปฏิบัติตามกฎข้อที่สอง
  • ปฏิเสธที่จะเปิดเข้าไปในฟังก์ชั่น (ที่จะใช้กับmapฯลฯ ) (0-)ใช้ส่วน

การลบ

  • เพื่อให้ได้ฟังก์ชั่นที่หักkใช้ส่วนที่เพิ่ม(-k+) อาจเป็นนิพจน์ที่ซับซ้อนได้ด้วย: ทำงานตามที่คาดไว้-kk(-2*length x+)
  • หากต้องการลบ 1 ให้ใช้predแทนเว้นเสียแต่ว่าจะต้องใช้ช่องว่างทั้งสองด้าน นี้เป็นของหายากและมักจะเกิดขึ้นกับuntilหรือฟังก์ชั่นที่ผู้ใช้กำหนดเนื่องจากmap pred xสามารถถูกแทนที่ด้วยpred<$>xและโดยiterate pred x [x,x-1..]และถ้าคุณมีที่f pred xใดที่หนึ่งคุณก็ควรนิยามfฟังก์ชัน infix ต่อไป ดูเคล็ดลับนี้

11

ลองจัดเรียงนิยามฟังก์ชันและ / หรืออาร์กิวเมนต์ใหม่

บางครั้งคุณสามารถบันทึกสองสามไบต์โดยการเปลี่ยนลำดับของกรณีการจับคู่รูปแบบในการกำหนดฟังก์ชัน การประหยัดเหล่านี้มีราคาถูก แต่ง่ายต่อการมองข้าม

เป็นตัวอย่างให้พิจารณาคำตอบ (ส่วนหนึ่งของ) เวอร์ชั่นก่อนหน้านี้ :

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

นี่เป็นนิยามแบบเรียกซ้ำ?โดยที่กรณีฐานเป็นรายการว่าง เนื่องจาก[]ไม่ใช่ค่าที่มีประโยชน์เราควรสลับนิยามและแทนที่ด้วย wildcard _หรืออาร์กิวเมนต์ดัมมี่yบันทึกไบต์:

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

จากคำตอบเดียวกันให้พิจารณาคำจำกัดความนี้:

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

รายการที่ว่างเปล่าเกิดขึ้นในค่าส่งคืนดังนั้นเราจึงสามารถบันทึกสองไบต์ด้วยการสลับเคส:

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

นอกจากนี้ลำดับของฟังก์ชันอาร์กิวเมนต์บางครั้งสามารถสร้างความแตกต่างด้วยการอนุญาตให้คุณลบช่องว่างที่ไม่จำเป็น ลองพิจารณาคำตอบนี้ก่อนหน้า:

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

มีช่องว่างที่น่ารำคาญระหว่างhและpในสาขาแรก เราสามารถกำจัดมันได้โดยกำหนดh a p qแทน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

อย่าเสียยาม "อย่างอื่น"

ยามสุดท้ายที่เป็น catch-all True(short as 1>0) สามารถใช้เพื่อผูกตัวแปร เปรียบเทียบ:

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

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

เนื่องจากผู้พิทักษ์มีความจำเป็นและไม่ได้รับความเสียหายจึงจำเป็นต้องมีค่าเล็กน้อยในการทำให้คุ้มค่า ก็เพียงพอแล้วที่จะบันทึกคู่ของ parens หรือผูกความยาว 3 นิพจน์ที่ใช้สองครั้ง บางครั้งคุณสามารถลบล้างยามเพื่อให้กรณีสุดท้ายเป็นนิพจน์ที่ใช้การรวมที่ดีที่สุด


10

ใช้,แทน&&ในยาม

หลายเงื่อนไขในยามที่ทุกคนต้องถือสามารถใช้ร่วมกับแทน,&&

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

2
ไม่จำเป็นต้องมีเงื่อนไขเช่นกันคุณสามารถทำสิ่งนี้:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

ไวยากรณ์ที่สั้นกว่าสำหรับการประกาศในเครื่อง

บางครั้งคุณจำเป็นต้องกำหนดฟังก์ชั่นท้องถิ่นหรือผู้ประกอบการ แต่มันมีค่าใช้จ่ายจำนวนมากไบต์เพื่อเขียนwhereหรือlet…inหรือยกระดับขึ้นไปด้านบนโดยการเพิ่มข้อโต้แย้งพิเศษ

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

โชคดีที่ Haskell มีไวยากรณ์ที่สับสนและไม่ค่อยมีใครใช้ แต่มีเหตุผลสำหรับการประกาศในท้องถิ่น :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

ในกรณีนี้:

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

คุณสามารถใช้ไวยากรณ์นี้กับการประกาศหลายคำสั่งหรือการประกาศหลายและมันยังทำรัง:

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

นอกจากนี้ยังใช้งานได้กับตัวแปรการรวมหรือรูปแบบอื่น ๆ แม้ว่ายามรูปแบบมักจะสั้นกว่าสำหรับสิ่งนั้นเว้นแต่ว่าคุณจะทำหน้าที่เชื่อม


3
นอกจากนี้ยังทำงานอยู่ภายใน comprehensions [f 1|let f x=x+1]รายการ:
Laikoni

10

หลีกเลี่ยง 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..]

อย่างใดอย่างหนึ่งในบรรดาสี่สำนวนที่จะผลิตรายการที่ไม่มีที่สิ้นสุดของn's

มันเป็นทิปที่เฉพาะเจาะจงมาก แต่สามารถบันทึกได้ถึง3ไบต์!


4
ถ้าnเป็นโกลบอลl=n:l;lความยาวเท่ากันและใช้ได้กับนิพจน์ที่ยาวกว่า (บางส่วน) (อาจต้องใช้ช่องว่าง)
Ørjan Johansen

@ ØrjanJohansenฉันรวมไว้ในโพสต์ ขอบคุณ!
สิ้นเชิงมนุษย์

10

เงื่อนไขที่สั้นกว่าเมื่อผลลัพธ์หนึ่งรายการคือรายการที่ว่างเปล่า

เมื่อคุณต้องการเงื่อนไขที่ส่งคืนรายการAหรือรายการว่างเปล่า[]ขึ้นอยู่กับเงื่อนไขบางอย่าง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

ตัวอย่าง: 1 , 2


คนที่สองมีAและ[]เปลี่ยน
Ørjan Johansen

@ ØrjanJohansenแก้ไขแล้วขอบคุณ!
Laikoni

1
Aha! แต่*>มีความตรึงใจสูงกว่า>>(ยังค่อนข้างต่ำเพื่อความสะดวกสบาย)
Ørjan Johansen

9

แลมบ์ดาแยกกฎ

การแสดงออกแลมบ์ดาไม่จำเป็นต้องใช้วงเล็บโดยรอบ - มันค่อนข้างโลภมากที่จะคว้าทุกสิ่งดังนั้นสิ่งทั้งยังแยกวิเคราะห์เช่นจนกระทั่ง

  • paren ปิด - (foo$ \x -> succ x)
  • ใน - let a = \x -> succ x in a 4
  • จุดสิ้นสุดของบรรทัด - main = getContents>>= \x -> head $ words x
  • ฯลฯ ..

พบและมีบางกรณีที่แปลกขอบที่นี้สามารถช่วยคุณไบต์หรือสอง ฉันเชื่อว่า\สามารถใช้เพื่อกำหนดโอเปอเรเตอร์ได้ดังนั้นเมื่อใช้ประโยชน์จากสิ่งนี้คุณจะต้องใช้พื้นที่เมื่อเขียนแลมบ์ดาโดยตรงจากผู้ให้บริการ (เช่นในตัวอย่างที่สาม)

นี่คือตัวอย่างของการใช้แลมบ์ดาซึ่งเป็นสิ่งที่สั้นที่สุดที่ฉันสามารถทราบได้ รหัสโดยทั่วไปดูเหมือนว่า:

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

9

แทนที่letด้วยแลมบ์ดา

สิ่งนี้สามารถทำให้คำจำกัดความเสริมโดด ๆ สั้นลงซึ่งไม่สามารถผูกมัดกับการ์ดรักษาความปลอดภัยหรือกำหนดไว้ด้วยกันทั่วโลกด้วยเหตุผลบางประการ ตัวอย่างเช่นแทนที่

let c=foo a in bar

สั้นลง 3 ไบต์

(\c->bar)$foo a

สำหรับคำจำกัดความเสริมหลายรายการกำไรอาจน้อยกว่าทั้งนี้ขึ้นอยู่กับจำนวนคำจำกัดความ

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

ถ้าคำจำกัดความบางคำอ้างถึงตัวอื่นมันเป็นการยากที่จะบันทึกไบต์ด้วยวิธีนี้:

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

ข้อแม้หลักที่มีสิ่งนี้คือletช่วยให้คุณสามารถกำหนดตัวแปร polymorphic แต่ lambdas ไม่ได้ดังที่ระบุไว้โดย @ChristianSievers ตัวอย่างเช่น,

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

ผลลัพธ์ใน(1,1)แต่

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

ให้ข้อผิดพลาดประเภท


1
มันไม่ค่อยสำคัญ แต่ "ความหมายเทียบเท่ากัน" หมายถึงสัญญามากเกินไป เรามี polymorpic เพื่อให้เราสามารถทำlet let f=id in (f 0,f True)หากเราพยายามเขียนใหม่ด้วยแลมบ์ดามันจะไม่พิมพ์ข้อความตรวจสอบ
Christian Sievers

@ChristianSievers นั่นเป็นเรื่องจริงขอบคุณสำหรับการบันทึก ฉันแก้ไขมันค่ะ
Zgarb

8

ผูกใช้ยาม

เมื่อกำหนดฟังก์ชั่นที่มีชื่อคุณสามารถผูกนิพจน์กับตัวแปรในการ์ด ตัวอย่างเช่น,

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

ทำเช่นเดียวกัน

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

ใช้สิ่งนี้เพื่อบันทึกการแสดงออกซ้ำ เมื่อนิพจน์ถูกใช้สองครั้งมันจะหยุดพักที่ความยาว 6 แม้ว่าปัญหาระยะห่างและความสำคัญจะเปลี่ยนไป

(ในตัวอย่างถ้าsไม่ใช้ตัวแปรดั้งเดิมมันสั้นกว่าที่จะทำ

g w=...
f=g.words

แต่นั่นไม่เป็นความจริงสำหรับการรวมนิพจน์ที่ซับซ้อนมากขึ้น)


ไม่ใช่คำตอบนี้ / ซ้ำกันหรือไม่?
ลินน์

@ ลินน์มองย้อนกลับไปมันเป็นกรณีพิเศษ แต่เมื่อฉันอ่านคำตอบนั้นJustทำให้ฉันคิดว่ามันเหมาะสำหรับการจับคู่รูปแบบเพื่อแยกจากคอนเทนเนอร์แทนที่จะเก็บไว้ในนิพจน์
xnor

8

ใช้(0<$)แทนlengthการเปรียบเทียบ

เมื่อทำการทดสอบว่ารายการaนั้นยาวกว่ารายการหรือไม่รายการbก็มักจะเขียน

length a>length b

อย่างไรก็ตามการแทนที่แต่ละองค์ประกอบของทั้งสองรายการด้วยค่าเดียวกันเช่น0จากนั้นการเปรียบเทียบทั้งสองรายการจะสั้นกว่า:

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

ลองออนไลน์!

วงเล็บมีความจำเป็นเพราะ<$และดำเนินการเปรียบเทียบ ( ==, >, <=, ... ) มีความสำคัญในระดับเดียวกัน 4 แต่ในกรณีอื่น ๆ ที่พวกเขาอาจจะไม่จำเป็นต้องประหยัดไบต์มากยิ่งขึ้น


8

สั้น transpose

ในการใช้transposeฟังก์ชั่นData.Listจะต้องมีการนำเข้า หากนี่เป็นเพียงฟังก์ชันเดียวที่ต้องการนำเข้าหนึ่งสามารถบันทึกไบต์โดยใช้foldrคำจำกัดความต่อไปนี้transpose:

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

โปรดทราบว่าพฤติกรรมนั้นเหมือนกันสำหรับรายการที่มีความยาวเท่ากัน

ฉันใช้ประสบความสำเร็จนี้ที่นี่


8

รับส่วนต่อท้าย

ใช้scanr(:)[]เพื่อรับส่วนต่อท้ายของรายการ:

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

นี่คือสั้นกว่าหลังtails import Data.Listคุณสามารถนำหน้าด้วยscanr(\_->init)=<<id(พบโดยØrjan Johansen)

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

วิธีนี้จะช่วยประหยัดไบต์

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

บางทีscanl(flip(:))[] "abc"= ["","a","ba","cba"]ก็เป็นสิ่งที่ควรค่าแก่การกล่าวถึงด้วยเช่นกัน - บางครั้งคำนำหน้าอาจถอยหลังไม่สำคัญ
Lynn

3
Ørjan Johansen พบทางเลือกที่สั้นกว่าหนึ่งไบต์สำหรับคำนำหน้า:scanr(\_->init)=<<id
Laikoni
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.