";str(chr 34)^it;(print(it^it);fn x=>print(x^it^x^it))";str(chr 34)^it;(print(it^it);fn x=>print(x^it^x^it))
เวอร์ชันที่ไม่ได้อ้างอิง: ลองใช้บน codingground
รุ่นที่ยกมา: ลองใน codingground
โปรดทราบว่าผลลัพธ์มีลักษณะเช่นนี้
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
เพราะรหัสถูกตีความประกาศโดยการประกาศ (แต่ละ;
ปลายประกาศ) และแสดงค่าและประเภทของการประกาศแต่ละ
พื้นหลัง
ใน SML มีรูปแบบ quine <code>"<code in quotes>"
:
str(chr 34);(fn x=>print(x^it^x^it))"str(chr 34);(fn x=>print(x^it^x^it))"
และหนึ่งในรูปแบบ"<code in quotes>"<code>
:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
ทั้งสองต้องพึ่งพาความจริงที่ว่า<code>
-part มีคำพูดใด ๆ ดังนั้นจึงสามารถอ้างที่มีออกมาความจำเป็นในการหลบหนีอะไรที่จำเป็นในการส่งออกควินจะได้รับโดย"
str(chr 34)
พวกเขายังต้องพึ่งพาตัวบ่งชี้นัยit
ซึ่งใช้เมื่อไม่มีตัวระบุที่ชัดเจนในการประกาศ
ในควินแรกstr(chr 34);
ผูกit
กับสตริงที่มี"
, fn x=>
เริ่มต้นการทำงานที่ไม่ระบุชื่อการหนึ่งโต้แย้งx
แล้ว concatenates x^it^x^it
และพิมพ์สตริงที่เกิด ฟังก์ชั่นนี้ไม่ระบุชื่อถูกนำมาใช้โดยตรงกับสตริงที่มีรหัสโปรแกรมเพื่อการ concatenation อัตราผลตอบแทนx^it^x^it
<code>"<code>"
ที่สองควินเริ่มต้นที่มีเพียงแค่รหัสโปรแกรมเป็นสตริงที่ถูกผูกไว้กับ";str(chr 34)^it;print(it^it)";
it
จากนั้นstr(chr 34)^it;
เชื่อมใบเสนอราคาจะเริ่มต้นของสตริงและเป็นอีกครั้งระบุไม่ชัดเจนจะได้รับสตริงส่งผลให้ถูกผูกไว้กับ"<code>
it
ในที่สุดprint(it^it)
concatenates สตริงด้วยตัวเองยอม"<code>"<code>
ซึ่งพิมพ์แล้ว
คำอธิบาย
แก้ไข:ไม่ทันสมัยกับรุ่น 108 ไบต์ แต่อาจเข้าใจได้หลังจากอ่านคำอธิบายนี้แล้ว
"<code>"<code>
อ้างปลอดภัยรวมทั้งควินของวิธีการข้างต้นและเป็นตัวเองของแบบฟอร์ม การใส่สิ่งนี้ลงไปอีกครั้งในเครื่องหมายคำพูดทำให้""<code>"<code>"
เราได้สตริงที่ว่างแล้วจึงเป็นแบบฟอร์มอื่น
นั่นหมายความว่าโปรแกรมนั้นจะได้รับแหล่งที่มาของตัวเองในรูปแบบ"<code>
โดยตัวระบุit
หรือit
เป็นเพียง"
และเราจะได้รับแหล่งที่มาของเรา<code>
เป็นข้อโต้แย้งและจะต้องเป็นฟังก์ชั่นที่จัดการข้อโต้แย้งดังกล่าว
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
ในการระบุในกรณีที่เราเป็นเราตรวจสอบว่าขนาดของit
มีขนาดใหญ่กว่า 1 ถ้าไม่it
เป็น"
และเราอยู่ในกรณีที่สองดังนั้นelse
ส่วนที่ส่งกลับฟังก์ชั่นที่ไม่ระบุชื่อfn x=>print(it^it^x^it^x^it)
ซึ่งถูกเรียกเพราะมันตามมาด้วยสตริง . สังเกตส่วนนำit^it^
ที่จำเป็นสำหรับสตริงว่างตอนเริ่มต้นของโปรแกรม
หากsize it
มีขนาดใหญ่กว่า 1 เราอยู่ในthen
ส่วนและเพียงดำเนินการprint(it^it)
ใช่ไหม? ไม่มากนักเพราะฉันละเลยที่จะบอกคุณว่า SML พิมพ์อย่างรุนแรงซึ่งหมายความว่าเงื่อนไขif <cond> then <exp_1> else <exp_2>
ต้องมีประเภทเดียวกันเสมอซึ่งหมายความว่าการแสดงออก<exp_1>
และ<exp_2>
จำเป็นต้องมีประเภทเดียวกันเสมอ เรารู้อยู่แล้วว่าประเภทของelse
ส่วน: ฟังก์ชั่นที่ไม่ระบุชื่อซึ่งจะใช้เวลาสตริงแล้วเรียกprint
มีประเภทstring -> <return type of print>
และprint
มีประเภทstring -> unit
( unit
อยู่ในวิธีการบางอย่างที่คล้ายกับvoid
ในภาษาอื่น ๆ ) string -> unit
ดังนั้นชนิดที่เกิดขึ้นอีกครั้ง
ดังนั้นถ้าthen
ส่วนนั้นเป็นเพียงแค่print(it^it)
ที่มีประเภทunit
เราจะได้รับข้อผิดพลาดประเภทไม่ตรงกัน แล้วไงfn _=>print(it^it)
ล่ะ ( _
เป็นตัวแทนสำหรับอาร์กิวเมนต์ที่ไม่ได้ใช้) ฟังก์ชั่นที่ไม่ระบุตัวตนนี้มีประเภท'a -> unit
ที่'a
ยืนสำหรับประเภทใด ๆ ดังนั้นในบริบทของเงื่อนไขของเราซึ่งบังคับใช้string -> unit
ประเภทนี้จะทำงาน (ตัวแปรประเภท'a
รับอินสแตนซ์กับประเภทstring
) อย่างไรก็ตามในกรณีนี้เราจะไม่พิมพ์อะไรเลยเพราะฟังก์ชั่นที่ไม่ระบุชื่อจะไม่ถูกเรียก! จำไว้ว่าเมื่อเราเข้าไปในthen
ส่วนของรหัสโดยรวมคือ"<code>"<code>
ดังนั้น<code>
ส่วนที่ประเมินถึงฟังก์ชั่น แต่เนื่องจากไม่มีอะไรเกิดขึ้นหลังจากนั้นมันไม่ได้ถูกเรียก
แต่เราใช้ sequentialisation ซึ่งมีแบบฟอร์ม(<exp_1>; ...; <exp_n>)
ที่<exp_1>
จะ<exp_n-1>
อาจจะมีประเภทโดยพลการและประเภทของการ<exp_n>
ให้บริการประเภทของ sequentialisation ทั้ง จากมุมมองการทำงานค่าของ<exp_1>
ถึง<exp_n-1>
ถูกยกเลิกอย่างง่าย ๆ อย่างไรก็ตาม SML ยังสนับสนุนโครงสร้างที่จำเป็นเพื่อให้การแสดงออกอาจมีผลข้างเคียง ในระยะสั้นเราทำ(print(it^it);print)
หน้าที่เป็นthen
ส่วนที่ดังนั้นการพิมพ์ครั้งแรกแล้วกลับมาฟังก์ชั่นprint
ที่มีประเภทที่ถูกต้อง