เคล็ดลับสำหรับการเขียน quines


30

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

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

tips  quine 

คำตอบ:


14

ใช้evalเพื่อลดความต้องการในการคัดลอกรหัส

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

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

นี่คือตัวอย่างของวิธีการทำงาน (ตัวอย่างเขียนใน Python แต่มีบางสิ่งที่คล้ายกันทำงานในภาษาอื่น ๆ ):

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

2
@QPaysTaxes: ฉันมุ่งหวังที่จะทำให้โค้ดของฉันที่นี่เป็นแบบอ่านได้และบำรุงรักษาได้ตามเงื่อนไขของชัยชนะ น่าเสียดายที่โดยทั่วไปยังคงแยกไม่ออกจากสัญญาณรบกวนสาย ASCII (หรือเสียงรบกวนสายปกติถ้าฉันใช้ Jelly) จากผู้ที่ไม่คุ้นเคยกับภาษา

14

ใช้ประโยชน์จากการจัดรูปแบบสตริง

วิธีที่ง่ายที่สุดวิธีหนึ่งในการสร้างควินินคือการกำหนดสตริงจากนั้นวางสตริงไว้ภายในตัวเองด้วยการจัดรูปแบบสตริง

s='s=%r;print s%%s';print s%s

ดังนั้นในตัวอย่างนี้ Python Quine เราประกาศสตริงที่มีส่วนแรกเท่ากับสิ่งที่อยู่ก่อนสตริงs=แล้วเราอนุญาตให้สตริงที่จะแทรกด้วยการจัดรูปแบบด้วย%rและในที่สุดเราก็วางสิ่งที่มาหลังจากสตริงที่จะพิมพ์และจัดรูปแบบมัน . บรรทัดใหม่ต่อท้ายเป็นเพราะprintพิมพ์ขึ้นบรรทัดใหม่

ดังนั้นเทมเพลตจึงเป็นแบบนี้จริงๆใน Python:

<A>'<A>%r<B>'<B>

หากต้องการขยายควินินที่มีอยู่ด้วยรหัสเพิ่มเติม:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>

9

ฟังก์ชันที่ทำให้เป็นสตริง

ในหลาย ๆ ภาษาฟังก์ชั่นวัตถุ (หรือโครงสร้างที่เทียบเท่า) จัดเก็บซอร์สโค้ดโดยนัยและจะส่งคืนเมื่อแปลงเป็นสตริง นี้จะช่วยให้ quines ขนาดกะทัดรัดโดยไม่ต้องใช้สตริง EVAL ตัวอย่างที่เด่นของภาษาดังกล่าวคือ JavaScript:

function f(){console.log(f+"f()")}f()

รหัสนี้กำหนดและเรียกใช้ฟังก์ชันfที่เมื่อเรียกใช้จะพิมพ์ซอร์สโค้ดของตัวเองตามด้วยการเรียกไปยังตัวมันเอง f()เพียงส่วนหนึ่งของโปรแกรมที่จะต้องมีการทำซ้ำเป็นสายฟังก์ชั่น แน่นอนว่าร่างกายของฟังก์ชั่นสามารถรวมโค้ด "payload" โดยพลการซึ่งจะถูกเรียกใช้งานเมื่อเรียกใช้ฟังก์ชัน


เคล็ดลับเดียวกันนี้มีขนาดกะทัดรัดกว่านี้ใช้งานได้ในภาษากอล์ฟGolfScript :

{".~"}.~

และCJam :

{"_~"}_~

แต่ละ quines เหล่านี้ก่อนกำหนดบล็อกรหัสที่ไม่ระบุชื่อ (อยู่ในเครื่องหมายปีกกา) ซึ่งมีลักษณะคล้ายกับวัตถุฟังก์ชั่นใน JavaScript: มันสามารถดำเนินการได้และถ้าเป็นสตริงมันจะส่งกลับรหัสที่มาของมัน ส่วนที่เหลือของรหัส ( .~ใน GolfScript หรือ_~ใน CJam) จากนั้นดำเนินการบล็อกในขณะที่ทิ้งสำเนาไว้ในกองซ้อน โค้ดภายในบล็อกนั้นจะส่งสตริงไปยังสแต็กที่ทำซ้ำโค้ดภายนอกบล็อก เมื่อล่ามออกมาจะทำการตรวจสอบและพิมพ์ทุกอย่างที่เหลือบนสแต็กโดยอัตโนมัติ เช่นเดียวกับตัวอย่างของ JavaScript บล็อกโค้ดเหล่านี้สามารถนำมาใช้เพื่อดำเนินการและดำเนินการ payload โดยพลการของโค้ดเพิ่มเติมโดยไม่ต้องทำซ้ำ


9

ใช้ตัวคั่นสตริงที่ซ้อนโดยไม่ต้องหลบหนี

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

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

Perl เป็นตัวอย่างที่ดีของภาษาที่ใช้เคล็ดลับนี้ได้ แม้ว่าตัวคั่นสตริงปกติ"…"หรือจะเป็นรังที่'…'ใช้กันน้อยกว่าq(…)ก็อนุญาตให้เขียนควินชนิดนี้ได้:

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

นี่คือรหัส + ข้อมูล quine s///เป็นการดำเนินการแทนที่สตริง regex; เราใช้0เป็นตัวทำเครื่องหมายและจับคู่ภายใน regex เป็น\d("ตัวเลขใด ๆ ") เพื่อหลีกเลี่ยงการใช้เครื่องหมายมากกว่าหนึ่งครั้ง (แม้ว่าเป็นการเพิ่มประสิทธิภาพอื่นเราจริง ๆ แล้วสามารถใช้0อีกครั้งเพราะ Perl ของs///แทนที่เหตุการณ์แรกโดย ค่าเริ่มต้น). โปรดทราบว่าไม่มีขั้นตอนการหลีกเลี่ยงที่ชัดเจนที่นี่เนื่องจากq(…)ตัวคั่นสามารถรวมไว้ในสตริงข้อมูลได้


8

รหัสข้อมูล + quines

โครงสร้างทั่วไปมากที่สุดสำหรับ quine ดูเหมือนว่า pseudocode นี้:

data = " เวอร์ชันที่หลีกเลี่ยงของโปรแกรมทั้งหมด
        ด้วยสตริงนี้แทนที่ด้วยเครื่องหมาย "
โปรแกรม = data.replace (
  การแสดงออกที่ประเมินเครื่องหมาย แต่ไม่ได้พูดถึงมัน ,
  หนี (ข้อมูล))
โปรแกรมการพิมพ์;

โครงสร้างนี้สามารถใช้ในการเขียนควิน (ค่อนข้างไร้เดียงสา) ในภาษาส่วนใหญ่ อย่างไรก็ตามมันมีแนวโน้มที่จะทำคะแนนได้ไม่ดีนักในระบบการให้คะแนนส่วนใหญ่เพราะคุณต้องเขียนโปรแกรมทั้งหมดสองครั้ง อย่างไรก็ตามโครงสร้างควินส่วนใหญ่สามารถพิจารณาการเพิ่มประสิทธิภาพของสิ่งนี้

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

ตัวอย่างเช่นเราสามารถเขียน Python Quine เพื่อหนีค่าสตริงโดยใช้reprและใช้x"สตริงลำดับ 2 ตัวอักษร(ซึ่งสามารถแทน"x\""ได้เช่นไม่ใช้ลำดับx"ในการแทนสตริงของสตริงเอง) เป็นเครื่องหมาย:

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

2
มันอาจจะคุ้มค่าที่จะสังเกต (อาจเป็นคำตอบอื่น) ว่าการแทรกสตริงเข้าไปในตำแหน่งของเครื่องหมายมักจะมีราคาแพงใน esolangs และมันก็คุ้มค่าที่จะสร้างโค้ดเช่นที่สตริงนั้นเป็นสิ่งแรกหรือสิ่งสุดท้าย (อาจแยกจากกัน สิ้นสุดโดยหนึ่งหรือสองตัวอักษรซึ่งคุณสามารถ hardcode) เพื่อให้คุณรู้ว่ามันจะต้องไปที่ไหน
Martin Ender

@ มาร์ตินเอนเดอร์: ฉันยอมรับว่ามันควรค่าแก่การกล่าวถึง แต่ก็อาจเป็นคำตอบอื่น (แทนที่จะเป็นความคิดเห็นหรือการแก้ไขในคำตอบนี้) เคล็ดลับ quine ส่วนใหญ่เป็นการปรับเปลี่ยนโครงสร้างทั่วไปนี้ดังนั้นฉันต้องการให้มันออกมาเป็นเคล็ดลับของตัวเองก่อนเพราะหลาย ๆ คนไม่มีความคิดที่จะเริ่มเขียน quine

ทางเลือกในการเครื่องหมายคือการใช้สองสายผมว่าสำหรับกระจก
Ørjan Johansen

4

ใช้ประโยชน์จากการตัดรหัสที่มา

ในบางภาษา (ส่วนใหญ่เป็นภาษา 2D) ซอร์สโค้ดสามารถล้อมรอบได้ ภายใต้สถานการณ์บางอย่าง (เช่นใน Befunge-98 หากโปรแกรมของคุณเป็นสายการบินเดียว) เมื่อผ่านจุดสิ้นสุดของโปรแกรมจะนำคุณกลับไปที่จุดเริ่มต้นของโปรแกรม พฤติกรรมที่ไม่เชิงเส้นแบบนี้หมายความว่าคุณสามารถเขียนโค้ดที่อยู่ภายในและภายนอกสตริงตัวอักษรได้ในเวลาเดียวกัน "(หรือตัวคั่นสตริงใด ๆ ) ที่ไม่มีใครเทียบจะช่วยให้คุณมีสตริงที่มีส่วนที่เหลือทั้งหมดของโปรแกรมได้อย่างมีประสิทธิภาพ (ยกเว้น"ตัวของมันเอง)

ปัญหาอย่างหนึ่งของการใช้เคล็ดลับนี้ก็คือคุณจะได้รับสตริงตามที่เห็นจากมุมมองของ"แทนที่จะเป็นตั้งแต่เริ่มต้นโปรแกรม (เหมือนที่คุณต้องการ) ดังนั้นจึงเป็นเรื่องง่ายที่สุดที่จะจัดเรียงโปรแกรมใหม่เพื่อให้"ปรากฏเมื่อเริ่มต้นหรือสิ้นสุด ซึ่งมักจะหมายถึงการตัดโปรแกรมของคุณออกเป็นหลาย ๆ ส่วนและใช้ประโยชน์จากสิ่งที่น่าสนใจ / ผิดปกติในการควบคุมการไหลของคำสั่งภาษาของคุณ

ตัวอย่างที่ดีคือ@ จัสติน Befunge-98 ควิน :

<@,+1!',k9"

ที่ไม่มีใครเทียบ"ในตอนท้ายของโปรแกรมห่อทั้งโปรแกรมในสตริงตัวอักษรดังนั้น (วิ่งไปทางซ้ายไปทางซ้ายเนื่องจาก<ที่จุดเริ่มต้น) สิ่งที่เราต้องทำคือการส่งออกโปรแกรม ( 9k) แล้วส่งออกคำพูดสองครั้ง ( '!1+,) และ ทางออก ( @) สิ่งนี้จะช่วยประหยัดโปรแกรมที่ต้องการสองชุด (หนึ่งเป็นรหัสหนึ่งสำเนาเป็นข้อมูล) สำเนาสองชุดเป็นโค้ดชิ้นเดียวกันตีความในรูปแบบที่แตกต่างกัน


4

จำโครงสร้างของควิน

ฉันชอบคิดว่า quines เป็นสามส่วนมากกว่า 2:

  • ส่วนที่ 1: สร้างข้อมูลที่แสดงถึงส่วนที่ 2 และ 3
  • ส่วนที่ 2: ใช้ข้อมูลเพื่อพิมพ์ส่วนที่ 1 แบบอัลกอริทึม
  • ส่วนที่ 3: ถอดรหัสการนำเสนอเพื่อพิมพ์ส่วนที่ 2 และ 3

สิ่งนี้สามารถทำให้คิดได้ง่ายขึ้น นี่คือ Python Quine โดยแต่ละบรรทัดจะสัมพันธ์กับส่วน:

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

บางครั้งคุณใช้evalหรือคล้ายกันเพื่อลบข้อมูลที่ซ้ำกัน แต่โดยทั่วไปฉันพบว่าสิ่งนี้ช่วยในการเขียนแบบง่าย ๆ

ลองมาดูที่สองอันเดอร์ Underload ที่แตกต่างกัน นี่เป็นครั้งแรก:

(:aSS):aSS

ส่วนแรกคือการ(:aSS)สร้างการแสดงข้อมูล ประการที่สองคือที่พิมพ์:aS (:aSS)ส่วนที่สามคือซึ่งพิมพ์S:aSS

นี่คือควินที่สอง:

(:aS(:^)S):^

ตอนแรกดูเหมือนว่าจะไม่พอดี แต่ถ้าคุณขยายควินินคุณจะได้:

(:aS(:^)S):aS(:^)S

ตอนนี้(:aS(:^)S)คือส่วนที่ 1 :aSคือส่วนที่ 2 และ(:^)Sตอนที่ 3

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