เคล็ดลับการตีกอล์ฟในคลีน


17

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

ถ้าคุณไม่เคยได้ยินสะอาดคุณสามารถหาข้อมูลเพิ่มเติมได้ที่นี่
หรือคุณสามารถเข้าร่วมห้องสนทนา

คำตอบ:


10

หลีกเลี่ยงimport StdEnvเมื่อเป็นไปได้

ในการเข้าถึงฟังก์ชั่นแม้กระทั่งขั้นพื้นฐานดูเหมือนจะชอบ(==)หรือmapคำสั่งนำเข้าเป็นสิ่งจำเป็นมักจะimport StdEnvเพราะมันนำเข้าโมดูลที่พบมากที่สุดเช่นStdInt, StdBoolและอื่น ๆ (ดูที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวStdEnv)

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

ตัวอย่างเช่นแทนที่จะเป็น

import StdEnv 
map f list

หนึ่งสามารถเขียน

[f x\\x<-list]

รายการทางเลือก:

ฟังก์ชันหรือการเรียกใช้ฟังก์ชันบางอย่างที่ต้องการimport StdEnvทางเลือกที่ไม่ต้องการการนำเข้าและการประมาณคร่าวๆของไบต์ที่บันทึกไว้

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

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

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


ไม่ใช่import StdEnv+ a and b(21 ไบต์) เล็กกว่า%[a:r]|a= %r=a;%_=True(22 ไบต์) ใช่ไหม หรือจะเป็นimport StdEnv+ a=True and b=True(31 ไบต์) ซึ่งในกรณีนี้มันสั้นกว่านี้อย่างแน่นอน (ผมไม่เคยโปรแกรมในสะอาดครับ.)
เควิน Cruijssen

@KevinCruijssen เราเป็นเพียงแค่การพูดคุยว่าในการแชท มันเป็นความจริงที่ว่าผู้ที่ไม่น่าจะบันทึกไบต์ยกเว้นอาจจะเมื่อโปรแกรมจำเป็นต้องเรียกคืนรายการ
Laikoni

4
อาโอเค. อาจเป็นประโยชน์ในการระบุจำนวนไบต์ที่บันทึกด้วยตัวเลือกอื่น (เช่นmap f list -> [f x\\x<-list] (11 bytes saved)(หรือบางอย่างที่คล้ายกัน))
Kevin Cruijssen

@KevinCruijssen เรียบร้อยแล้ว
Laikoni

5

รู้วิธีการเรียนรู้ภาษา

ท้ายที่สุดแล้วทุกคนสามารถเล่นกอล์ฟในภาษาที่พวกเขาไม่สามารถใช้!

ออนไลน์

ทำความสะอาดไม่ใช่ภาษาที่รู้จักกันดีหรือมีเอกสารดีและชื่อไม่ได้ทำให้ง่ายต่อการค้นหาแหล่งข้อมูลที่จำเป็นมากในการแก้ไขปัญหาเหล่านี้ ... หรือไม่

Clean เดิมเรียกว่าConcurrent Cleanซึ่งยังคงใช้ในส่วนนำหน้าของเอกสารเกือบทั้งหมดที่เกี่ยวข้องกับ Clean - ดังนั้นหากคุณกำลังมองหา Clean ให้มองหา Clean พร้อมกันแทน

หนึ่งในความคล้ายคลึงที่น่าทึ่งยิ่งกว่าของ Clean คือ Haskell (ซึ่งมีอยู่มากมาย) คือการมีอยู่ของCloogleซึ่งเป็นเครื่องมือค้นหาฟังก์ชั่นที่ครอบคลุมไลบรารีที่ Clean มาพร้อมกับ

ในท้องถิ่น

ไลบรารีที่ Clean จัดส่งด้วยนั้นอยู่ในรูปแบบของไฟล์ซอร์ส Clean ที่มีการติติงด้วยความคิดเห็นที่ค่อนข้างเหมาะสมซึ่งสามารถเรียกดูผ่านการใช้ IDE
(นอกจากนี้ยังมาพร้อมกับโปรแกรมตัวอย่างแบบเต็มภายใต้$INSTALL/Examples)

เมื่อพูดถึง Clean เวอร์ชัน Windows มาพร้อมกับ IDE - แม้ว่ามันจะถูก จำกัด ด้วยมาตรฐานที่ทันสมัย ​​แต่โลกก็ยังดีกว่าการใช้เท็กซ์เอดิเตอร์และบรรทัดคำสั่ง
คุณสมบัติที่มีประโยชน์มากที่สุดสองประการ (ในบริบทของการเรียนรู้) คือ:

  • คุณสามารถคลิกสองครั้งที่ข้อผิดพลาดเพื่อดูว่ามีเส้นใดอยู่
  • คุณสามารถไฮไลต์ชื่อโมดูลและกด[Ctrl]+[D]เพื่อเปิดไฟล์คำจำกัดความ (หรือใช้[Ctrl]+[I]สำหรับไฟล์การนำไปใช้) และสลับระหว่างคำจำกัดความและไฟล์การนำไปปฏิบัติด้วย[Ctrl]+[/]

4

ลืมการเข้ารหัสตัวอักษร

คอมไพเลอร์ของ Clean ไม่สนใจว่าการเข้ารหัสใดที่คุณคิดว่าคุณได้บันทึกไฟล์ต้นฉบับไว้เช่นเดียวกับค่าไบต์ในไฟล์ สิ่งนี้มีผลตามมาอย่างเรียบร้อย

ในร่างกายของรหัสที่มาเพียงไบต์ด้วยรหัสจุดที่สอดคล้องกับตัวอักขระ ASCII \t\r\nได้รับอนุญาตนอกจากนั้นสำหรับ

ตัวอักษร:

ในStringและ[Char]ตัวอักษร ( "stuff"และ['stuff']ตามลำดับ) ไบต์ใด ๆ ยกเว้น 0ได้รับอนุญาตด้วยข้อแม้ที่"และ'จะต้องหลบหนี (สำหรับStringและ[Char]ตามลำดับ) และการขึ้นบรรทัดใหม่และผลตอบแทน carraige จะต้องถูกแทนที่ด้วย\nและ\r(ยังตามลำดับ)

ในCharตัวอักษรไบต์ใด ๆ ยกเว้น 0ได้รับอนุญาตหมายความว่า:

'\n'

'
'

เหมือนกัน แต่ที่สองคือหนึ่งไบต์ที่สั้นกว่า

หนี:

นอกเหนือจากการ\t\r\nหลีกเลี่ยงจดหมายมาตรฐาน(ฯลฯ ) ลำดับการหลีกเลี่ยงที่ไม่ใช่ตัวเลขทั้งหมดใน Clean จะใช้สำหรับเครื่องหมายทับหรือสำหรับเครื่องหมายคำพูดที่ใช้เพื่อกำหนดขอบเขตตัวอักษรที่อยู่ภายใน

สำหรับลำดับ escape ตัวเลขตัวเลขจะถือว่าเป็นค่าฐานแปดที่ถูกยกเลิกหลังจากสามหลัก ซึ่งหมายความว่าหากคุณต้องการโมฆะตามด้วยตัวอักษร1ในStringคุณจำเป็นต้องใช้"\0001"(หรือ"\0\61") และไม่ได้ "\01"อย่างไรก็ตามถ้าคุณทำตามการหลบหนีด้วยอะไรก็ได้ยกเว้นตัวเลขคุณสามารถละเว้นเลขศูนย์นำหน้าได้

ผลกระทบ:

การเล่นโวหารด้วยวิธีที่ Clean จัดการกับไฟล์ต้นฉบับช่วยให้Stringและ['Char']กลายเป็นลำดับของตัวเลขหลักเดียว 256 หลักได้อย่างมีประสิทธิภาพซึ่งมีการใช้งานมากมายสำหรับ code-golf เช่นการจัดเก็บดัชนี (สูงสุด 255 รายการ)


3

ชื่อฟังก์ชั่นที่มีสัญลักษณ์

เมื่อกำหนดฟังก์ชั่นมักจะสั้นกว่าที่จะใช้การรวมกันบางส่วนของ!@$%^&*~-+=<:|>.?/\กว่าจะใช้ตัวอักษรและตัวเลขเพราะมันช่วยให้คุณสามารถเว้นช่องว่างระหว่างตัวระบุ

ตัวอย่างเช่น: ?a=a^2สั้นกว่าf a=a^2และการเรียกใช้จะสั้นกว่าเช่นกัน

อย่างไรก็ตาม :

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

ตัวอย่างเช่น: ?a+?bแยกวิเคราะห์เป็น? a +? b

นอกจากนี้:

มันเป็นไปได้ที่จะเขียนทับตัวบ่งชี้ที่นำเข้าในที่สะอาดและอื่น ๆ เพียงตัวเดียวตัวระบุสัญลักษณ์ที่ไม่ได้ใช้แล้วในการเป็นStdEnv @$?การเขียนทับ^-+(ฯลฯ ) อาจมีประโยชน์หากคุณต้องการตัวระบุสัญลักษณ์เพิ่มเติม แต่ระวังว่าคุณไม่ได้เขียนทับสิ่งที่คุณกำลังใช้อยู่


3

รู้ว่าโหนดKของคุณ

บางส่วนของโครงสร้างที่แข็งแกร่ง (สำหรับการเล่นกอล์ฟ) let ... in ...ในภาษาที่ทำงานได้ดี
สะอาดของหลักสูตรมีนี้และสิ่งที่ดีกว่า - #The

โหนดคืออะไร

ทั้งสะอาด#และแพร่หลาย|(ยามรูปแบบ) เป็นที่รู้จักกันทั้งสองเป็น 'การแสดงออกโหนด'
โดยเฉพาะอย่างยิ่งพวกเขาช่วยให้คุณสามารถตั้งโปรแกรม imperatively- ishในที่สะอาด (Clean ซึ่งเป็นสิ่งที่ดีจริงๆที่นี่!)

#(ขอก่อน):

ทั้งสองนี้คำนวณค่าของจำนวนเต็มที่กำหนดเป็นสตริงคูณด้วยผลรวมของตัวอักษร

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

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

แต่การใช้letจะง่ายกว่าเมื่อคุณต้องการบางสิ่งflip f = let g x y = f y x in g

The |(รูปแบบการป้องกัน):

รูปแบบการป้องกันของ Clean สามารถใช้งานได้เหมือนกับภาษาอื่น ๆ ที่มีฟังก์ชั่นการใช้งานif ... else ...มากมาย และเวอร์ชันที่สั้นกว่าของนิพจน์ประกอบไปด้วย

ตัวอย่างเช่นสิ่งเหล่านี้ทั้งหมดส่งคืนสัญลักษณ์ของจำนวนเต็ม:

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

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

หมายเหตุ:

คุณสามารถใช้นิพจน์เหล่านี้ได้ทุกที่ ใน lambdas, case ... of, let ... inฯลฯ


1

หากคุณกำลังใช้งานStringคุณควรใช้งานText

การแปลงเป็นสายอักขระและการจัดการกับสายอักขระ (ตัว{#Char}/ Stringไม่ใช่[Char]แบบชนิด) ค่อนข้างยาวและไม่ดีสำหรับการเล่นกอล์ฟ Textเยียวยาโมดูลนี้

การแปลง:

Textกำหนดผู้ประกอบการ<+สำหรับสองประเภทใด ๆ ที่ได้toStringกำหนดไว้
ผู้ประกอบการนี้ใช้เป็นa<+bเป็นเช่นเดียวกับtoString a+++toString b- ประหยัดอย่างน้อย 19 ไบต์ แม้ว่าคุณจะรวมการนำเข้าเพิ่มเติม,Textและใช้เพียงครั้งเดียวก็ยังบันทึกได้ 14 ไบต์!

การจัดการ:

Textกำหนดลวดเย็บกระดาษการจัดการสตริงที่หายไปจากStdEnv:

  • ผู้ประกอบการ+สำหรับสตริงซึ่งสั้นกว่า+++(จากStdEnv)
  • indexOfโดยมีพฤติกรรมคล้าย C ในการส่งคืน-1แทนที่จะเป็นNothingความล้มเหลว
  • concatซึ่งจะเชื่อมโยงรายการสตริง
  • joinซึ่งรวมรายการของสตริงโดยใช้สตริงตัวคั่น
  • splitซึ่งแยกสตริงเป็นรายการสตริงบนสตริงย่อย

1

ใช้เนื้อแกะที่สั้นกว่า

บางครั้งคุณพบว่าตัวเองใช้แลมบ์ดาแสดงออก (เพื่อส่งต่อmapหรือsortByฯลฯ ) เมื่อคุณทำเช่นนี้ (เขียน lambdas) มีหลายวิธีที่คุณสามารถทำได้

ทางที่ถูก:

นี่คือsortByรายการเรียงลำดับแลมบ์ดาที่มีสำนวนยาวที่สุดถึงสั้นที่สุด

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

อีกวิธีที่ถูกต้อง:

หากคุณกำลังใช้Data.Funcงานคุณสามารถทำได้

sortBy (on (>) length)

ทางสั้น ๆ :

นี่คือสิ่งเดียวกัน แต่มีไวยากรณ์นักกอล์ฟ

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

วิธีอื่น ๆ :

การใช้การแต่งเพลงไม่ได้สั้นลงในครั้งนี้ แต่บางครั้งอาจสั้นกว่านี้ได้

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

วิธีอื่น ๆ :

ในขณะที่มันถูกประดิษฐ์ขึ้นเล็กน้อยที่นี่คุณสามารถใช้การ์ดใน lambdas

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

และยังให้นิพจน์โหนดก่อน

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

หมายเหตุ:

มีอีกสองรูปแบบของแลมบ์ดามี(\a b . ...)และ(\a b -> ...)หลังซึ่งเป็นเหมือน=ที่แตกต่างและอดีตซึ่งมีอยู่ด้วยเหตุผลบางอย่างและมักจะดูเหมือนว่าคุณกำลังพยายามที่จะเข้าถึงทรัพย์สินของบางสิ่งบางอย่างแทนการกำหนดแลมบ์ดาเพื่อ don ใช้งานไม่ได้


1
หลังจากที่ได้เห็นรายการกอล์ฟของคุณฉันได้รับความประทับใจ\a=... คือไวยากรณ์แลมบ์ดาของ Clean: P
Ørjan Johansen

นอกจากนี้คุณยังสามารถเพิ่มยามในแลมบ์ดาที่ใช้ที่นี่ สิ่งนี้ไม่มีเอกสาร (แม้จะขัดแย้งกับรายงานภาษา) แต่ใช้งานได้ ยิ่งไปกว่านั้น->และ=สำหรับ lambdas นั้นเหมือนกันกับคอมไพเลอร์ที่เกี่ยวข้อง ( ->เป็นไวยากรณ์เก่า) .มีความแตกต่างเท่านั้น(แต่ฉันไม่รู้วิธี)

และในตัวอย่างนี้คุณสามารถพิจารณาใช้on(<)lengthแม้ว่าData.Funcการนำเข้าจะทำให้คุณเลิกเว้นแต่ว่าคุณต้องการมันอยู่แล้ว

@ คีลันคูล ฉันจะอัปเดตในภายหลังวันนี้ ฉันคิดว่าคุณสามารถใช้ let-before ( #) เป็น lambdas ได้ด้วย
Οurous

ใช่คุณสามารถ :-)

0

ใช้ตัวอักษรของรายการอักขระ

ตัวอักษรรายชื่อตัวละครเป็นวิธีที่จดชวเลขการเขียนสิ่งที่ต้องการเป็น['h','e','l','l','o']['hello']

นี่ไม่ใช่ขีด จำกัด ของสัญกรณ์ตัวอย่างเช่น:

  • repeat'c'กลาย['c','c'..]เป็น['cc'..]
  • ['z','y'..'a'] กลายเป็น ['zy'..'a']
  • ['beginning']++[a,b,c]++['end'] กลายเป็น ['beginning',a,b,c,'end']
  • ['prefix']++suffix กลายเป็น ['prefix':suffix]

งานเหล่านี้ในการจับคู่เกินไป:

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

0

บางครั้งcodeก็สั้น

Clean มีฟังก์ชั่นที่มีประโยชน์มากมายในไลบรารีมาตรฐานซึ่งบางฟังก์ชั่นใช้งานได้อย่างไม่น่าเชื่อ *Worldและการใช้*Worldcode-golf เป็นความคิดที่ไม่ดีอยู่แล้ว

เพื่อแก้ไขปัญหานี้บ่อยครั้ง ccallคุณสามารถใช้codeบล็อกภายในแทน

ตัวอย่างบางส่วน:

เวลาของระบบ

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

ด้านบนคือ 58 ไบต์ แต่คุณสามารถบันทึก 17 ไบต์ (ลงไปที่ 40 + 1) ด้วย:

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

ตัวเลขสุ่ม

อันนี้ไม่ได้บันทึกไบต์ด้วยตัวเอง แต่หลีกเลี่ยงการต้องผ่านรายการ genRandInt

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

ใช้อื่น ๆ

นอกเหนือจากสองสิ่งนี้ซึ่งอาจใช้เป็นหลักในการเขียนโค้ดกอล์ฟคุณสามารถเรียกใช้ฟังก์ชันที่มีชื่อใด ๆ (รวมถึง แต่ไม่ จำกัด เฉพาะ syscall ทุกแห่ง) ฝังแอสเซมบลีตามอำเภอใจด้วยinstruction <byte>และฝังโค้ดสำหรับเครื่อง ABC

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.