val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]
ลองออนไลน์!
สำหรับ MLton โปรแกรม SML เต็มรูปแบบจะถูกคั่นด้วยนิพจน์และยกเลิกโดย;
(เช่นprint"Hello";print"World";
) หรือการประกาศด้วยvar
และfun
คีย์เวิร์ด (เช่นvar _=print"Hello"var _=print"World"
) โดยที่_
เป็น wild card ซึ่งสามารถแทนที่ด้วยชื่อตัวแปรใด ๆ ก็ได้
ตัวเลือกแรกไม่มีประโยชน์สำหรับการเขียนโปรแกรมที่เก่าแก่เพราะ;
ในตัวมันเป็นโปรแกรมที่ถูกต้อง (ซึ่งไม่ทำอะไรเลย แต่ไม่มีข้อผิดพลาดอย่างใดอย่างหนึ่ง) ปัญหาด้วยวิธีที่สองคือการประกาศเช่นvar _=print"Hello"
สามารถสั้นลงเพียงvar _="Hello"
(หรือแม้กระทั่งvar _=print
) เพราะการประกาศด้วยvar
ผลงานตราบใดที่ด้านขวาเป็นนิพจน์ SML ที่ถูกต้องหรือค่า (SML เป็นภาษาที่ใช้งานได้ดังนั้นฟังก์ชันสามารถ ใช้เป็นค่าเกินไป)
ณ จุดนี้ฉันก็พร้อมที่จะประกาศการเขียนโปรแกรมที่เก่าแก่ใน SML เป็นไปไม่ได้เมื่อฉันบังเอิญพบรูปแบบการจับคู่ในval
-declarations ปรากฎว่าไวยากรณ์สำหรับการประกาศไม่ใช่val <variable_name> = <expression>
แต่val <pattern> = <expression>
ที่รูปแบบอาจประกอบด้วยชื่อตัวแปรค่าคงที่และตัวสร้าง ในฐานะที่เป็นprint
ฟังก์ชั่นมีประเภทstring -> unit
เราสามารถใช้รูปแบบการแข่งขันในunit
-value ในการบังคับใช้ที่ฟังก์ชั่นการพิมพ์ถูกนำไปใช้จริงสตริง:()
val()=print"Hey"
ด้วยวิธีการนี้ให้ลบอย่างใดอย่างหนึ่งprint
หรือ"Hey"
ผลในPattern and expression disagree
-error
ด้วยวิธีการพิมพ์ที่เก่าแก่นี้อยู่ในมือขั้นตอนต่อไปคือการเขียนคำพูดก่อนที่จะต้องมีการเพิ่มการป้องกันแบบประหยัด ก่อนหน้านี้ฉันใช้เทคนิค SML Quine ง่าย ๆ (ดูประวัติการแก้ไข ) แต่ Anders Kaseorg ชี้ให้เห็นถึงวิธีการที่แตกต่างกันซึ่งสามารถบันทึกไบต์ได้ในกรณีของเขา มันใช้String.toString
ฟังก์ชั่นในตัวเพื่อจัดการกับการหลบหนีสตริงและเป็นของรูปแบบทั่วไป<code>"<data>"
ที่"<data>"
เป็นสตริงที่หลบหนีจากcode
ก่อนหน้านี้:
val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"
นี่คือควินินที่ใช้งานได้ แต่ยังไม่บริสุทธิ์ ก่อนอื่น Anders Kaseorg พบว่า MLton ยอมรับการเสนอราคาเดียว"
เป็นรหัสโดยไม่มีข้อผิดพลาดซึ่งหมายความว่าเราไม่สามารถมีรหัสสิ้นสุดในการอ้างอิงดังกล่าวข้างต้น ทางที่สั้นที่สุดเพื่อป้องกันไม่ให้เรื่องนี้จะเป็นที่จะตัดทุกอย่างหลังจากval()=
ในคู่ของวงเล็บ val()=()
แต่แล้วรหัสอาจจะลดลงไป วิธีที่สั้นที่สุดที่สองที่ฉันพบคือใช้val()=hd[ ... ]
นั่นคือเราห่อทุกอย่างไว้ในรายการและคืนองค์ประกอบแรกเพื่อให้ตัวตรวจสอบชนิดมีความสุข
เพื่อให้แน่ใจว่าไม่มีส่วนใดของสตริงข้อมูลที่สามารถลบออกได้โดยไม่สังเกตเห็นการจับคู่รูปแบบในval
-declarations มีประโยชน์อีกครั้ง: ความยาวของสตริงสุดท้ายที่จะพิมพ์ (และความยาวของโปรแกรม) ควรเท่ากับ 195 ดังนั้น เราสามารถเขียนlet val t=... val 195=size t in print t end
ในร่างกายของสิ่งที่เป็นนามธรรมแทนfn
print(...)
การลบส่วนหนึ่งของสตริงส่งผลให้มีความยาวน้อยกว่า 189 จึงทำให้เกิดBind
ข้อยกเว้นขึ้น
ยังมีปัญหาเหลืออยู่: การval 195=size t
ตรวจสอบทั้งหมดอาจถูกยกเลิกได้ เราสามารถป้องกันสิ่งนี้ได้ด้วยการขยายการตรวจสอบให้ตรงกับสิ่งอันดับ: val t=... val(216,u)=(n+size t,t)in print u end
เช่นการลบผลการตรวจสอบในตัวแปรที่ไม่ได้ผูกu
ไว้
ทั้งหมดนี้ให้โซลูชัน 195 ไบต์ต่อไปนี้:
val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]
ใช้เคล็ดลับการเล่นกอล์ฟของการใช้ชื่อตัวแปรประกอบการเช่น!
, $
และ%
แทนn
, t
และu
เพื่อประหยัดพื้นที่สีขาวบาง (ดูเคล็ดลับนี้ ) นำไปสู่ขั้นสุดท้ายรุ่น 182 ไบต์
การลบซับสตริงอื่นทั้งหมดที่ไม่ได้ระบุไว้อย่างชัดเจนในคำอธิบายควรส่งผลให้เกิดข้อผิดพลาดทางไวยากรณ์หรือชนิด
แก้ไข 1: เป็นเพียงlength(explode t)
แก้ไข 2:ขอบคุณ Anders Kaseorg สำหรับแนวทางแบบควินอื่นและชี้ให้เห็น "ช่องโหว่"size t