วนซ้ำโดยไม่ต้อง 'วนรอบ' [ปิด]


85

คำถามที่คล้ายกับคำถามนี้ถูกถามเมื่อสองสามปีก่อนแต่คำถามนี้ก็ยิ่งซับซ้อน

ความท้าทายนั้นง่าย เขียนโปรแกรม (ในภาษาของคุณเลือก) ที่ซ้ำ ๆ รันรหัสโดยไม่ต้องใช้โครงสร้างการทำซ้ำใด ๆ เช่นwhile, for, do while, foreachหรือgoto( ดังนั้นสำหรับสิ่งที่คุณ nitpickers คุณไม่สามารถใช้ห่วง ) อย่างไรก็ตามการเรียกซ้ำไม่ได้รับอนุญาตในการทำงานที่เรียกตัวเองรู้สึก (ดูคำนิยามด้านล่าง) นั่นจะทำให้ความท้าทายนี้ง่ายเกินไป

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

สำหรับผู้ที่อาจวางสายบนคำจำกัดความนิยามของวนสำหรับคำถามนี้คือ:

A programming language statement which allows code to be repeatedly executed.

และคำจำกัดความของการสอบถามซ้ำสำหรับคำถามนี้จะเป็นนิยามฟังก์ชันการเรียกซ้ำมาตรฐานของคุณ:

A function that calls itself.

ผู้ชนะจะเป็นคำตอบที่มีผู้โหวตมากที่สุดในวันที่ 16 กรกฎาคมเวลา 10.00 น. ตามเวลาฝั่งตะวันออก โชคดี!

UPDATE:

หากต้องการสงบความสับสนที่ยังคงแสดงออกอยู่อาจช่วย:

กฎที่ระบุไว้ข้างต้น:

  • อย่าใช้ลูปหรือข้ามไป
  • ฟังก์ชั่นไม่สามารถเรียกตัวเองว่า
  • ทำทุกอย่างที่คุณต้องการใน 'ลูป'

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


27
สำหรับผู้ที่ต้องการเคล็ดลับง่ายฉันไม่สามารถจะใส่ใจโพสต์: P เพียงให้ 2 ฟังก์ชั่นfunction Aการโทรfunction Bและfunction Bการโทรfunction Aในขณะที่ 1 ของการทำงานดำเนินการบางสิ่งบางอย่าง เนื่องจากฟังก์ชันไม่ได้เรียกตัวเองว่ามันควรจะถูกต้องตามเกณฑ์ ^. ^
Teun Pronk

2
"เปลี่ยนเป็นการประกวดความนิยมเพื่อมุ่งเน้นความคิดสร้างสรรค์" การเปลี่ยนคำถามคือการโกง!
CousinCocaine

4
คำจำกัดความของ "การเรียกซ้ำ" นั้นไม่มีประโยชน์มาก มันจะเป็นการดีกว่าถ้าไม่อนุญาตให้เรียกใช้ฟังก์ชันแบบเรียกซ้ำซึ่งเป็นฟังก์ชันที่อ้างถึงตนเองไม่ว่าทางตรงหรือทางอ้อม
lrn

3
สิ่งที่ไม่ชัดเจนคือ "คำจำกัดความ" ของตัวสร้างลูปและการเรียกซ้ำ ไม่แม่นยำมาก ตัวอย่าง: rep(f){f();f();}- นี่คือคำสั่ง (การประกาศฟังก์ชั่นเป็นคำสั่งในบางภาษา) ที่อนุญาตให้เรียกใช้รหัสซ้ำ ๆ มันไม่ได้รับอนุญาต คุณขอรหัสเพื่อใช้วนรอบ หากรหัสนั้นเป็นคำสั่งทางประโยคคุณไม่ได้รับอนุญาต ตัวอย่างอื่น: f(b) { b(); g(b); }; g(b) { f(b); }. ฉันจะบอกว่าfเป็นฟังก์ชั่นวนซ้ำ (โดยการเรียกซ้ำร่วมกันg) ไม่อนุญาตหรือไม่
lrn

3
@CailinP สิ่งที่ฉัน " วางสาย " คือคำถามบนเว็บไซต์ควรอยู่ในหัวข้อสำหรับเว็บไซต์: นั่นหมายถึงมีข้อกำหนดที่ชัดเจนและมีวัตถุประสงค์ซึ่งคำถามนี้ไม่ได้
Peter Taylor

คำตอบ:


258

ทับทิม

def method_missing(meth,*args)
  puts 'Banana'
  send(meth.next)
end

def also
  puts "Orange you glad I didn't say banana?"
end

ahem

การสาธิต

ล้างลำคอพิมพ์ "Banana" 3070 ครั้งแล้วใส่ "ส้มคุณดีใจที่ฉันไม่ได้พูดกล้วยเลย"

วิธีนี้ใช้ฟังก์ชันการกำหนดเมธอดแบบทันเวลาที่ไร้สาระของรูบี้เพื่อกำหนดวิธีการที่เรียงตามตัวอักษรระหว่างคำว่า 'อะแฮ่ม' และ 'ยัง' ("อะแฮ่ม", "อาเฮน", "อาเฮ๊ป", "อะเฮ๊ก") "aher", "ahes", "ahet", "aheu", "ahev" ... ) เพื่อพิมพ์ Banana ก่อนแล้วจึงเรียกรายการต่อไปในรายการ


4
ในที่สุดมันก็กระทบ "ด้วย" ซึ่งกำหนดไว้ดังนั้นจึงไม่หายไป
ชำนาญวิชาประวัติศาสตร์

77
นี่คือความคลั่งไคล้
Michael B

4
@barrycarter: ใน Ruby String#nextซึ่งถูกเรียกในmethod_missingฟังก์ชั่นมากกว่าหรือน้อยกว่าเช่นการเพิ่ม 1 ให้กับตัวเลขยกเว้นว่าจะทำงานกับตัวอักษรและตัวเลขทั้งหมด (และไม่ใช่ alnums หากเป็นอักขระตัวเดียวในสตริง) ดูruby-doc.org/core-2.1.2/String.html#method-i-next
3Doubloons

2
@NickT มันสามารถใช้งานได้ในชั้นเรียนเช่นสร้าง XML b.my_tagที่คุณสามารถแท็กที่สร้างขึ้นโดยเฉพาะ นอกจากนี้ยังถูกนำมาใช้ในรูปแบบ ActiveRecord OpenStructหรือ ใน 'Wat talk' เขากล่าวว่าโลกmethod_missingไม่ดี แต่มีขอบเขตที่ยอดเยี่ยม
Hauleth

2
ฉันจำความคิดเห็นเก่า ๆ ในโปรแกรมทับทิมอื่นได้: "ฉันชอบมันเพราะมันมีปรุงยา"
vidya sagar

82

Python - 16

หรือภาษาอื่นใดที่มีการประเมินผล

exec"print 1;"*9

คุณช่วยอธิบายว่าโปรแกรมของคุณทำอะไรได้บ้าง?
CailinP

10
มันต้องใช้สตริง ( "print 1;") ทำซ้ำมัน 9 ครั้ง ( *9) จากนั้นดำเนินการสตริงที่เกิดขึ้น ( exec) ทำซ้ำรหัสโดยไม่วนซ้ำเลย
scragar

12
Yay สำหรับการคูณสตริง!
Thane Brimhall

2
นอกจากนี้ยังทำงานในทับทิมถ้าคุณเปลี่ยนexecไปeval หรือไปprint echo
Ajedi32

80

CSharp

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

class P{
    static int x=0;
    ~P(){
        System.Console.WriteLine(++x);
        new P();
    }
    static void Main(){
        new P();
    }
}

โปรดอย่าทำอย่างนี้เลย)

เมื่อเริ่มต้นเราสร้างอินสแตนซ์ใหม่ของPคลาสซึ่งเมื่อโปรแกรมพยายามออกจากการเรียก GC ซึ่งเรียก finalizer ซึ่งสร้างอินสแตนซ์ใหม่ของPคลาสซึ่งเมื่อพยายามล้างข้อมูลจะสร้างใหม่Pที่เรียก finalizer .

ในที่สุดโปรแกรมก็จะตาย

แก้ไข: อย่างลึกลับนี้จะทำงานเพียงประมาณ 45k ครั้งก่อนตาย ฉันไม่ค่อยรู้ว่า GC หาห่วงอนันต์อันไร้ขอบเขตของฉันได้อย่างไร แต่มันก็เป็นเช่นนั้น สั้น ๆ ดูเหมือนว่ามันไม่ได้คิดออกและเธรดเพิ่งถูกฆ่าหลังจากผ่านไปประมาณ 2 วินาทีของการดำเนินการ: https://stackoverflow.com/questions/24662454/how-does-a-garbage-collector-avoid-an -infinite วง-ที่นี่

แก้ไข 2: หากคุณคิดว่านี่เป็นเรื่องเล็กน้อยเหมือนการเรียกซ้ำให้พิจารณาโซลูชันอื่นของฉัน: https://codegolf.stackexchange.com/a/33268/23300

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


20
ฉันไม่รู้ด้วยซ้ำว่า C # มีตัวทำลายล้าง +1 สำหรับสอนฉัน
Seequ

4
@TheRare มันทำ แต่พวกเขาไม่กำหนดในธรรมชาติและอาจไม่เคยถูกเรียกในระหว่างการดำเนินการของโปรแกรม พวกเขาจะดำเนินการตามการแทนที่ของวิธีเสมือนเพื่อให้พวกเขาบางครั้งเรียกว่าFinalize finalizerใน C # จริงคุณควรใช้IDisposableรูปแบบ
Michael B

ดูเหมือนว่ามีการหมดเวลาที่เกิดขึ้นในบางจุด ฉันไม่คิดว่ามันเป็น GC ที่หยุดวงจรของคุณ แต่แทนที่จะเป็นระบบปฏิบัติการที่ตัดสินใจว่าโปรแกรมของคุณใช้เวลานานจนจบ
LVBen

ฉันคิดว่านี่เป็นรันไทม์จริง ๆ ตัดสินใจฆ่าโปรแกรมไม่จำเป็นต้องเป็นระบบปฏิบัติการ เธรดตัวรวบรวมขยะที่เรียกใช้เมื่อสิ้นสุดโปรแกรมจะได้รับการ จำกัด เวลาคงที่ ~ 2 วินาทีก่อนที่จะถูกฆ่า
Michael B

ด้วยการดัดแปลงเล็กน้อย (ไม่ปล่อยให้โปรแกรมสิ้นสุดปล่อยวัตถุ P 1 ไปที่ GC และเรียก GC.Collect ซ้ำ ๆ ) ฉันสามารถทำให้มันทำงานได้อย่างไม่มีกำหนด
LVBen

53

Befunge

.

Befunge เก่าดีเอาท์พุท 0 (จากสแต็กเปล่า) ค่อนข้างมากตลอดไปเนื่องจากมีเส้นล้อมรอบ


1
ฮา! ฉันรักลูกเล่นเช่นนี้
CailinP

47

JS

(f=function(){ console.log('hi!'); eval("("+f+")()") })()

ฟังก์ชั่นสนุก!

ฟังก์ชั่นที่สร้างฟังก์ชั่นอื่นโดยมีเนื้อความเดียวกันกับตัวมันเองจากนั้นเรียกใช้งาน

มันจะแสดง hi เมื่อสิ้นสุดเมื่อถึงขีด จำกัด ของ stack และสิ่งทั้งหมดยุบ

คำเตือน: คุณจะไม่สามารถทำอะไรในเบราว์เซอร์ของคุณจนกว่าจะถึงขีด จำกัด ของสแต็ก


และอีกหนึ่งความชั่วร้ายที่มากขึ้น:

function f(){ var tab = window.open(); tab.f = f; tab.f()}()

มันสร้างฟังก์ชั่นที่เปิดหน้าต่างจากนั้นสร้างฟังก์ชั่นภายในหน้าต่างนั้นซึ่งเป็นสำเนาของฟังก์ชั่นแล้วเรียกใช้มัน

ข้อจำกัดความรับผิดชอบ: หากคุณอนุญาตให้เปิดป๊อปอัปวิธีเดียวที่จะเสร็จสิ้นคือการรีสตาร์ทคอมพิวเตอร์ของคุณ


5
นี่เป็นสิ่งที่ชั่วร้ายอย่างแน่นอน;)
CailinP

28
@CailinP ค่อนข้าง eval แน่นอน
seequ

ฉันคิดว่าคุณพลาดfฟังก์ชั่นที่สองในตอนท้าย มันควรจะเป็น}f()ในที่สุด
Chirag Bhatia - chirag64

2
น่าเสียดายที่ฉันสังเกตเห็นเพราะฉันลองแล้ว : P
Chirag Bhatia - chirag64

7
-1 - นี่เป็นแค่การเรียกซ้ำ
Immibis

39

x86 ชุดประกอบ / DOS

    org 100h

start:
    mov dx,data
    mov ah,9h
    int 21h
    push start
    ret

data:
    db "Hello World!",10,13,"$"

ฉันไม่ได้บอกว่าไม่มีการเรียกซ้ำหางย้อนกลับหรือไม่? ใช่ฉัน มาดามมิมมังกรสีม่วง

มันทำงานอย่างไร

retการเรียนการสอนที่ใช้ในการกลับมาจากการทำงานจริงปรากฏอยู่กลับมาจากสแต็ค (ซึ่งปกติจะใส่โดยมีที่สอดคล้องกันcall) และกระโดดไป ที่นี่ในการวนซ้ำแต่ละครั้งเราจะpushระบุที่อยู่จุดเริ่มต้นบนสแต็กก่อนส่งคืนดังนั้นจึงสร้างการวนซ้ำไม่สิ้นสุด


ฉันสงสัยว่าสิ่งนี้เป็นไปได้หรือไม่ในการชุมนุม
Ian D. Scott

2
ยุ่งกับสแต็คที่อันตรายของคุณ นี่คือมังกร ;-)
บาดเจ็บดิจิทัล

1
ฉันกำลังจะเรียกcodegolf.stackexchange.com/a/34295/11259ในฐานะคู่ของสิ่งนี้ แต่ฉันเห็นว่าเป็นคำตอบก่อนหน้านี้จริง ๆ
Digital Trauma

@DigitalTrauma: อ๋อฉันสังเกตเห็นหลังจากที่ฉันโพสต์รายการของฉัน แต่ฉันต้องแนบกับภาพของ Madame Mim :-) โชคดีที่มีความแตกต่างบางอย่าง (ของเขาค่อนข้างสับสนและทำงานบน 32 บิต Linux, ฉันเล่นได้โดยตรงบน DOS และไม่มีการกระโดดอื่น ๆ ) ถ้าไม่มีใครคัดค้านฉันจะออกจากที่นี่ต่อไป
Matteo Italia

@MatteoItalia ไม่งงงวยเพียงแค่เส็งเคร็ง;) (สรรพสินค้า "เพิ่ม eax, 4" ทำให้ฉันสับสนเช่นกันฉันไม่สามารถหาตารางขนาด opcode ดังนั้นฉันเพิ่งจะเดาขนาดป่า) ฉันทำมันในคอมไพเลอร์ออนไลน์ในขณะที่โครงการทำงานของฉันกำลังรวบรวมดังนั้นมันจึงดูน่ากลัว แนวคิดที่ยอดเยี่ยมในการใช้ "เริ่มต้น:"
PTwr

37

ชวา

ส่งตรงจาก XKCD

พันธะ

มันเป็นเกมที่ไม่มีที่สิ้นสุดที่จับกันระหว่างพ่อแม่กับลูก!

เป้าหมายของการCHILDมีการตั้งค่าPARENTและเป้าหมายของการเป็นPARENT CHILDเมื่อมีการPARENTเรียกAIMมันจะส่งอินสแตนซ์ของBALLคลาสและถูกดักจับโดยคำสั่ง catch คำสั่งจับแล้วโทรที่เป้าหมายคือPARENT.TARGET.AIM เช่นไม่เหมือนกันและ "โยนลูกกลับ" เพื่อแม่CHILDCHILD


3
ฉันชอบการ์ตูนเหล่านั้น!
Derek 朕會功夫

1
จะดีกว่าถ้าลูกถูกโยนระหว่างพ่อแม่กับลูก ตามที่เป็นอยู่ลูกบอลจะถูกโยนและถูก "บุคคล" คนเดียวกันเสมอ
Ajedi32

@ Ajedi32 มันจะปรากฏขึ้นจริง ๆ แล้วมันจะโยนมันไปมา; ผู้ปกครองเป้าหมายคือเด็กและเป้าหมายของเด็กคือผู้ปกครอง เอมถูกเรียกเมื่อพ่อแม่ที่ throes ลูกและมีลูกเล็งและโยนลูกคิววง
อเล็กซ์โคลแมน

12
@AlexColeman รหัสนี้คล้ายคลึงกับผู้ปกครองขว้างลูกบอลขึ้นไปในอากาศจับมันแล้วมอบให้กับเด็กที่ทำแบบเดียวกันก่อนที่จะส่งลูกบอลกลับไปที่ผู้ปกครองและอื่น ๆ
Ajedi32

11
คำสั่งTARGET.AIM(B);in method AIMเป็นการเรียกซ้ำ ดังนั้นนี่เป็นการละเมิดกฎ "ฟังก์ชั่นที่ไม่สามารถเรียกตัวเองได้"
Theodore Norvell

31

Bash, 3 ตัวอักษร

yes

ใช่จะส่งคืน 'y' ไปยังคอนโซลเป็นประจำ

แก้ไข: ทุกคนได้รับการสนับสนุนให้แก้ไขบรรทัดนี้:

yes something | xargs someaction

(ขอบคุณ Olivier Dulac)


1
เหตุใดสิ่งนี้จึงทำงานต่อไป ฉันไม่ได้ตั้งคำถามแค่พยายามหาสาเหตุ
Teun Pronk

2
@TeunPronk yesเป็นคำสั่ง bash ที่พิมพ์คำว่าใช่จนกระทั่งมันถูกฆ่าหรือกระแสข้อมูลถูกปิด ถ้ามันกำลังเขียนไปที่หน้าจอมันจะไม่หยุดจนกว่าคุณจะฆ่ามัน มันเป็นการโกงชนิดหนึ่งเนื่องจากเป็นคำสั่งที่โดยทั่วไปประกอบด้วยวนรอบ printf
scragar

1
ความสนุกสนานมากขึ้นคือการใช้yesเพื่อให้วงอื่น ๆ ดำเนินต่อไป
trlkly

3
@izkata: แต่คุณสามารถ: yes something | xargs someactionไม่มีการเรียกซ้ำ (คุณสามารถเพิ่ม -n 1 ถึง xargs ให้มีเพียง 1 "บางอย่าง" ต่อบรรทัด ฯลฯ ) การใช้ xargs เป็นการเปิดทางให้กับพฤติกรรมที่ซับซ้อนมากขึ้น (แม้แต่คนที่ไม่มีอะไรเกี่ยวข้องกับผลลัพท์ใช่)
Olivier Dulac

4
@scarar คุณควรจะตอบกลับyesไป
daviewales

28

C, 35 ตัวอักษร

main(int a,char**v){execv(v[0],v);}

โปรแกรมดำเนินการเอง ฉันไม่แน่ใจว่านี่เป็นการพิจารณาการเรียกซ้ำหรือไม่


4
recursion หาง @mniip แล้วถ้ามันนำมาใช้ในระดับกระบวนการ
Izkata

3
@Izkata การเรียกซ้ำ Tail ยังคงเป็นการเรียกซ้ำ แต่นี่ไม่ใช่การเรียกซ้ำ การเรียกซ้ำหมายถึงฟังก์ชั่น (หรือกระบวนการในกรณีนี้) 'รอ' สำหรับการทำซ้ำตัวเองอีกครั้งเพื่อยุติ ในกรณีนี้execทำให้กระบวนการใหม่แทนที่กระบวนการเดิมดังนั้นจึงไม่มีการเรียกสแต็คที่จะส่งคืนหรือโอเวอร์โฟลว์ในที่สุด
millinon

4
@millinon ในภาษาที่รองรับการเพิ่มประสิทธิภาพ tail recursion จะแทนที่การโทรก่อนหน้านี้ใน call stack คล้ายกับวิธีการexecแทนที่กระบวนการก่อนหน้า มันจะไม่ล้นเช่นกัน
Izkata

1
@millinon เป็นคนคล่องแคล่วอย่างยิ่งและจะดึงการอภิปรายนี้ออกมานานขึ้นในภาษาการเขียนโปรแกรม Scheme การเพิ่มประสิทธิภาพนี้เป็นคุณลักษณะด้านภาษา มันอยู่ในสเปคที่ว่าถ้าคุณโทรออกหาง recursive, ล่าม / คอมไพเลอร์มีเพื่อนำมาใช้กองกรอบที่ผ่านมา นี่เป็นเพราะ Scheme ไม่มีโครงสร้างวนลูปในตัวดังนั้นวิธีเดียวที่จะใช้ลูปใน Scheme คือการวนรอบแบบวนซ้ำและมันจะดูดถ้าคุณมีสแต็คล้นจากการพยายามวนซ้ำมากเกินไป :)
Ord

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

28

C (กับ GCC builtins - ดูเหมือนว่าจะทำงานกับเสียงดังกราว)

  • ไม่มีลูปอย่างชัดเจน
  • ไม่มี gotos ชัดเจน
  • ไม่มีการเรียกซ้ำ
  • แค่เล่นกับสแต็คเก่า ๆ (เด็ก ๆ อย่าลองทำที่บ้านโดยไม่มีผู้ดูแล):
#include <stdio.h>

void *frameloop (void *ret_addr) {
    void **fp;
    void *my_ra = __builtin_return_address(0);

    if (ret_addr) {
        fp = __builtin_frame_address(0);
        if (*fp == my_ra) return (*fp = ret_addr);
        else fp++;
        if (*fp == my_ra) return (*fp = ret_addr);
        else fp++;
        if (*fp == my_ra) return (*fp = ret_addr);
        else fp++;
        if (*fp == my_ra) return (*fp = ret_addr);
        return NULL;
    } else {
        return (my_ra);
    }
}

int main (int argc, char **argv) {
    void *ret_addr;
    int i = 0;

    ret_addr = frameloop(NULL);
    printf("Hello World %d\n", i++);
    if (i < 10) {
        frameloop(ret_addr);
    }
}

คำอธิบาย:

  • main()frameloop(NULL)สายแรก ในกรณีนี้ใช้__builtin_return_address()builtin เพื่อรับที่อยู่ผู้ส่ง (ในmain()) ที่frameloop()จะกลับไป เราส่งคืนที่อยู่นี้
  • printf() เพื่อแสดงว่าเรากำลังวนซ้ำ
  • ตอนนี้เราโทรหาframeloop()ด้วยที่อยู่ผู้ส่งสำหรับการโทรก่อนหน้า เราดูสแต็กสำหรับที่อยู่ผู้ส่งคืนปัจจุบันและเมื่อเราพบเราจะแทนที่ที่อยู่ผู้ส่งก่อนหน้านี้
  • จากนั้นเรากลับจากการframeloop()โทรครั้งที่สอง แต่เนื่องจากที่อยู่ผู้ส่งถูกแฮ็กข้างต้นเราจึงกลับไปที่จุดmain()ที่การโทรครั้งแรกควรกลับไป ดังนั้นเราจบลงในวง

การค้นหาที่อยู่ผู้ส่งคืนในสแต็คแน่นอนว่าจะสะอาดกว่าแบบวนซ้ำ แต่ฉันไม่ได้ลงทะเบียนซ้ำเพื่อดูว่าไม่มีการวนซ้ำใด ๆ

เอาท์พุท:

$ CFLAGS=-g make frameloop
cc -g    frameloop.c   -o frameloop
$ ./frameloop 
Hello World 0
Hello World 1
Hello World 2
Hello World 3
Hello World 4
Hello World 5
Hello World 6
Hello World 7
Hello World 8
Hello World 9
$ 

2
ดี! ฉันสงสัยว่าทำไมฟังก์ชั่นเหล่านั้นไม่ได้เป็นส่วนหนึ่งของข้อมูลจำเพาะ C? ;-D
Brian Minton

4
@BrianMinton ที่จริงสิ่งที่คล้ายกันควรจะทำได้ด้วย/setjmp() longjmp()เหล่านี้ไม่ได้อยู่ในคมาตรฐาน แต่อยู่ในห้องสมุดมาตรฐาน ฉันรู้สึกเหมือนกำลังสแต็กสแต็กด้วยตนเองในวันนี้แม้ว่า ;-)
Digital Trauma

@BrianMinton ฉันเดาว่าเป็นเพราะมันอยู่ในรายละเอียด CPU ซึ่งทำให้มันขึ้นอยู่กับแพลตฟอร์ม และมันค่อนข้างอันตรายที่จะใช้เมื่อสร้างเฟรมสแต็คโดยอัตโนมัติฉันจะไม่แปลกใจถ้า AV จะร้องไห้เกี่ยวกับรหัสดังกล่าว ตรวจสอบสิ่งนี้หรือสิ่งนี้สำหรับ x86 รุ่น asm
PTwr

27

Haskell

รหัสต่อไปนี้ไม่มีฟังก์ชั่นวนซ้ำ (แม้แต่ทางอ้อม) ไม่มีการวนซ้ำแบบดั้งเดิมและไม่เรียกใช้ฟังก์ชันการเรียกซ้ำแบบใด ๆ ในตัว (ใช้เฉพาะIOเอาต์พุตและการรวมเท่านั้น)

data Strange a = C (Strange a -> a)

-- Extract a value out of 'Strange'
extract :: Strange a -> a
extract (x@(C x')) = x' x

-- The Y combinator, which allows to express arbitrary recursion
yc :: (a -> a) -> a
yc f =  let fxx = C (\x -> f (extract x))
        in extract fxx

main = yc (putStrLn "Hello world" >>)

ฟังก์ชั่นextractไม่เรียกอะไรycเรียกเพียงextractและmainเรียกร้องเพียงycและputStrLnและ>>ที่ไม่ได้เรียกซ้ำ

คำอธิบาย:Strangeเคล็ดลับคือในชนิดข้อมูล recursive เป็นชนิดข้อมูลแบบเรียกซ้ำที่ใช้เองซึ่งดังที่แสดงในตัวอย่างอนุญาตการทำซ้ำโดยพลการ อันดับแรกเราสามารถสร้างextract xได้ซึ่งเป็นการแสดงออกถึงการประยุกต์ใช้ด้วยตนเองx xในแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์ออกมา และสิ่งนี้จะช่วยให้การสร้างCombinator วายλf.(λx.f(xx))(λx.f(xx))กำหนดให้เป็น


อัปเดต:ตามที่แนะนำฉันกำลังโพสต์ชุดตัวเลือกที่ใกล้เคียงกับคำนิยามของYในแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์มากขึ้น:

data Strange a = C (Strange a -> a)

-- | Apply one term to another, removing the constructor.
(#) :: Strange a -> Strange a -> a
(C f) # x = f x
infixl 3 #

-- The Y combinator, which allows to express arbitrary recursion
yc :: (a -> a) -> a
yc f =  C (\x -> f (x # x)) # C (\x -> f (x # x))

main = yc (putStrLn "Hello world" >>)

3
โครงสร้างข้อมูลแบบเรียกซ้ำโดยใช้ฟังก์ชันแบบเรียกซ้ำ ... ดี
ApproachingDarknessFish

6
อันนี้ใกล้กับหัวใจของฉันเป็นคนที่มีความสนใจในการเขียนโปรแกรมการทำงานทั้งหมด คุณเพิ่งแสดงให้เห็นถึงวิธีการทำให้ Y-combinator มีประเภทข้อมูลซ้ำ ๆ นี่คือเหตุผลที่ภาษาทั้งหมดต้องการประเภทการเกิดซ้ำทางด้านขวาของลูกศรและสาเหตุที่ไม่อนุญาตให้ใช้ต้นไม้กุหลาบ ทำได้ดีนี่! ฉันสร้างบัญชีที่นี่เพียงเพื่อโหวตสิ่งนี้!
Jake

คุณสามารถลบการletเชื่อมและกำหนดyc f = extract $ C $ f.extractเนื่องจากการletรวมเนื้อหาภาษาที่อนุญาตให้เรียกซ้ำ (แบบดั้งเดิมlet x = x in x) นอกจากนี้ยังช่วยลดตัวอักษรบาง :)
โลกเครื่องยนต์

หรือแม้แต่yc = extract . C . (.extract)
Earth Engine

@EarthEngine Yทรูฉันแค่อยากจะให้โครงสร้างที่ใกล้ชิดกับความหมายเดิมของ
Petr Pudlák

26

C ++

ผลลัพธ์ต่อไปนี้นับถอยหลังจาก 10 ถึง "Blast off!" ใช้ metaprogramming แม่แบบ

#include <iostream>

template<int N>
void countdown() {
    std::cout << "T minus " << N << std::endl;
    countdown<N-1>();
}

template<>
void countdown<0>() {
    std::cout << "Blast off!" << std::endl;
}

int main()
{
    countdown<10>();
    return 0;
}

มันอาจดูเหมือนเป็นตัวอย่างคลาสสิกของการเรียกซ้ำ แต่อย่างน้อยในทางเทคนิคก็ไม่ได้ขึ้นอยู่กับคำนิยามของคุณ คอมไพเลอร์จะสร้างสิบฟังก์ชั่นที่แตกต่างกัน countdown<10>พิมพ์ "T ลบ 10" จากนั้นโทรcountdown<9>และอื่น ๆ ลงไปcountdown<0>ซึ่งพิมพ์ "Blast off!" แล้วส่งคืน การเรียกซ้ำเกิดขึ้นเมื่อคุณคอมไพล์รหัส แต่การปฏิบัติการไม่มีโครงสร้างวนลูปใด ๆ

ใน C ++ 11 เราสามารถใช้เอฟเฟกต์ที่คล้ายกันโดยใช้constexprคำสำคัญเช่นฟังก์ชันแฟคทอเรียล (เป็นไปไม่ได้ที่จะใช้ตัวอย่างการนับถอยหลังด้วยวิธีนี้เนื่องจากconstexprฟังก์ชั่นไม่สามารถมีผลข้างเคียง แต่ฉันคิดว่ามันอาจเป็นไปได้ใน C ++ 14 ที่กำลังจะมาถึง)

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n-1));
}

อีกครั้งนี้ดูเหมือนว่าการเรียกซ้ำ แต่คอมไพเลอร์จะขยายออกfactorial(10)ไป10*9*8*7*6*5*4*3*2*1แล้วอาจแทนที่ด้วยค่าคงที่3628800ดังนั้นปฏิบัติการจะไม่มีรหัสวนซ้ำหรือเรียกซ้ำ


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

@Lushushenko ใช่ฉันรู้ แต่จากนั้นอีกตัวอย่างเทมเพลตก็ทำสิ่งเดียวกัน - ใช้ฟังก์ชั่นแบบเรียกซ้ำในภาษาทัวริงที่รวบรวมเวลาความแตกต่างเพียงอย่างเดียวคือ constexpr ใช้ภาษาที่ดูเหมือน C ++ เช่นเดียวกับคำตอบทั้งหมดที่คนคนนี้ก้มกฎและฉันแค่เป็นคนซื่อสัตย์เกี่ยวกับมัน constexprได้รับการออกแบบมาโดยเฉพาะเพื่อให้ (บางแง่มุม) ของเทมเพลต metaprogramming ล้าสมัยดังนั้นมันจึงดูเหมือนว่าควรพูดถึงในโพสต์เรื่อง
นาธาเนียล

1
&countdown<N-1> != &countdown<N>1:
Thomas Eding

21

ชวา

มาเล่นกับ Java class loader และตั้งเป็น parent ของมัน:

import java.lang.reflect.Field;

public class Loop {
    public static void main(String[] args) throws Exception {
        System.out.println("Let's loop");
        Field field = ClassLoader.class.getDeclaredField("parent");
        field.setAccessible(true);
        field.set(Loop.class.getClassLoader(), Loop.class.getClassLoader());

    }
}

วงนี้จริง ๆ แล้วคุณจะต้องใช้ a kill -9เพื่อหยุดมัน :-)

มันใช้ CPU ของ Mac ของฉันได้ 100,1%

CPU 100,1%

คุณสามารถลองย้ายส่วนSystem.outท้ายของฟังก์ชั่นหลักเพื่อทดลองพฤติกรรมตลก ๆ


ฮ่า ๆ. รับ java ติดอยู่ในตัวเอง :)
masterX244

รักแฮ็คโหลด JVM แบบเรียกซ้ำ
Isiah Meadows

20

CSharp

อีกคนหนึ่งที่ชั่วร้ายและเท่าเทียมกัน ::

public class P{

    class A<B>{
        public static int C<T>(){
            System.Console.WriteLine(typeof(T));
            return C<A<T>>();
        }
    }
    public static void Main(){
        A<P>.C<int>();
    }
}

นี่ไม่ใช่การเรียกซ้ำ ... นี่เป็นการยืนยันเทมเพลตของรหัส ในขณะที่เรากำลังเรียกใช้วิธีการเดียวกัน แต่รันไทม์กำลังสร้างวิธีการใหม่อยู่ตลอดเวลา เราใช้พารามิเตอร์ type ของ int เนื่องจากจริง ๆ แล้วมันบังคับให้สร้างชนิดใหม่ทั้งหมดและแต่ละอินสแตนซ์ของเมธอดต้อง jit วิธีการใหม่ ไม่สามารถแบ่งปันรหัสได้ที่นี่ ในที่สุดเราจะฆ่า call stack เพราะจะรอการส่งคืน int ที่เราสัญญาไว้ แต่ไม่เคยส่งมอบ ในแบบเดียวกันเราเขียนแบบที่เราสร้างขึ้นเพื่อให้มันน่าสนใจ โดยทั่วไป C แต่ละตัวที่เราเรียกว่าเป็นวิธีการใหม่ที่น่าสนใจซึ่งมีรูปร่างเหมือนกัน นี่เป็นไปไม่ได้ในภาษาอย่าง C ++ หรือ D ที่ทำเทมเพลตในเวลารวบรวม เนื่องจาก C # JIT นั้นขี้เกียจสุด ๆ มันจะสร้างสิ่งนี้ในช่วงเวลาที่เป็นไปได้สุดท้ายเท่านั้น ดังนั้น,


14

Redcode 94 (สงครามแกน)

MOV 0, 1

คัดลอกคำสั่งที่ศูนย์ที่อยู่ไปยังที่อยู่หนึ่ง เนื่องจากใน Core War ที่อยู่ทั้งหมดสัมพันธ์กับที่อยู่พีซีปัจจุบันและโมดูโลขนาดของแกนนี่เป็นลูปที่ไม่มีที่สิ้นสุดในหนึ่งคำสั่งไม่กระโดด

โปรแกรมนี้ (นักรบ) เรียกว่า " Imp " และเผยแพร่ครั้งแรกโดย AK Dewdney


3
อิมพีจะเดินขบวนเตรียมประตูของคุณเตรียมพวกเขามิฉะนั้นคุณจะถูกบดขยี้
seequ

พร้อมของคุณSPL 0, 0; MOV 1, -2แน่นอน
wberry

เยี่ยมมากฉันหวังว่าจะยังไม่ได้โพสต์นี้ +1
mbomb007

14

โผ

ฉันเดาว่านี่จะเป็นวิธีคลาสสิคในการเรียกซ้ำโดยไม่มีฟังก์ชันเวียนเกิดขึ้นจริง ไม่มีฟังก์ชั่นด้านล่างอ้างถึงตัวเองโดยใช้ชื่อโดยตรงหรือโดยอ้อม

(ลองที่dartpad.dartlang.org )

// Strict fixpoint operator.
fix(f) => ((x)=>f(x(x))) ((x)=>(v)=>f(x(x))(v));
// Repeat action while it returns true.
void repeat(action) { fix((rep1) => (b) { if (b()) rep1(b); })(action); }

main() {
  int x = 0;
  repeat(() {  
    print(++x);
    return x < 10;
  });
}

6
Combinator Y หรือไม่
aditsu

5
ในทางเทคนิคแล้วฉันคิดว่ามันคือ Z combinator เพราะมันเป็นภาษาที่เข้มงวด Y combinator ต้องใช้ภาษาขี้เกียจเพื่อหลีกเลี่ยงการตีแผ่ไม่สิ้นสุด ความแตกต่างเพียงอย่างเดียวคือส่วนหลังของมันคือการขยายกทพ.
lrn

12

JS

ไม่ได้ต้นฉบับมาก แต่เล็ก 20 ตัวอักษร

setInterval(alert,1)

คุณสามารถลบออกได้จริง,1และจะยังใช้งานได้
ดีเร็ก朕會功夫

@ ดีเร็ก朕會功夫ถ้าฉันทำอย่างนั้นฉันจะได้รับการแจ้งเตือนเพียงครั้งเดียวใน Firefox
xem

1
ในโครเมี่ยมมันทำงานโดยไม่มีพารามิเตอร์สุดท้าย รหัสควรนับว่าถูกต้องหากทำงานในสภาพแวดล้อมอย่างน้อยหนึ่งแห่ง
Derek 朕會功夫

3
@Derek 朕會功夫setIntervalไม่ใช่คำแถลง มันเป็นเพียงฟังก์ชั่น มันถูกใช้ในคำแถลง expression และถ้าเราไม่สามารถใช้คำแถลง expression ได้ฉันก็ไม่รู้ด้วยซ้ำ
คม

1
@Cory - ดีฉันเดาว่าถูกต้องแล้ว!
Derek 朕會功夫

12

สัญญาณใน C

#include <stdio.h>
#include <signal.h>

int main(void) {
    signal(SIGSEGV, main);
    *(int*)printf("Hello, world!\n") = 0;
    return 0;
}

เห็นได้ชัดว่าพฤติกรรมของโปรแกรมนี้มีความชัดเจนมาก แต่วันนี้บนคอมพิวเตอร์ของฉันมันยังคงพิมพ์ "Hello, world!"


11

Emacs Lisp

นี่เป็นเวลาที่ยอดเยี่ยมในการแสดงการออกแบบที่ทรงพลังของ Lisp โดยที่ "รหัสคือข้อมูลและข้อมูลคือรหัส" จริงอยู่ที่ตัวอย่างเหล่านี้ไม่มีประสิทธิภาพมากและไม่ควรใช้ในบริบทจริง

แมโครสร้างรหัสที่เป็นรุ่นที่ไม่ได้ควบคุมของวนซ้ำที่ควรได้รับและรหัสที่สร้างขึ้นนั้นเป็นสิ่งที่ถูกประเมินที่รันไทม์

เล่นซ้ำ: ให้คุณวนซ้ำ N ครั้ง

(defmacro repeat-it (n &rest body)
  "Evaluate BODY N number of times.
Returns the result of the last evaluation of the last expression in BODY."
  (declare (indent defun))
  (cons 'progn (make-list n (cons 'progn body))))

ทดสอบซ้ำ:

;; repeat-it test
(progn
  (setq foobar 1)

  (repeat-it 10
    (setq foobar (1+ foobar)))

  ;; assert that we incremented foobar n times
  (assert (= foobar 11)))

ซ้ำมันมีดัชนี:

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

(defmacro repeat-it-with-index (var-and-n &rest body)
  "Evaluate BODY N number of times with VAR bound to successive integers from 0 inclusive to n exclusive..
VAR-AND-N should be in the form (VAR N).
Returns the result of the last evaluation of the last expression in BODY."
  (declare (indent defun))
  (let ((fallback-sym (make-symbol "fallback")))
    `(let ((,(first var-and-n) 0)
           (,fallback-sym 0))
       ,(cons 'progn
              (make-list (second var-and-n)
                         `(progn
                            (setq ,(first var-and-n) ,fallback-sym)
                            ,@body
                            (incf ,fallback-sym)))))))

ทดสอบซ้ำพร้อมดัชนี:

การทดสอบนี้แสดงให้เห็นว่า:

  1. ร่างกายประเมิน N ครั้ง

  2. ตัวแปรดัชนีจะถูกตั้งค่าอย่างถูกต้องที่จุดเริ่มต้นของแต่ละการวนซ้ำ

  3. การเปลี่ยนค่าของสัญลักษณ์ชื่อ "fallback" จะไม่ทำให้ยุ่งเหยิงกับดัชนี

;; repeat-it-with-index test
(progn
  ;; first expected index is 0
  (setq expected-index 0)

  ;; start repeating
  (repeat-it-with-index (index 50)
    ;; change the value of a  'fallback' symbol
    (setq fallback (random 10000))
    ;; assert that index is set correctly, and that the changes to
    ;; fallback has no affect on its value
    (assert (= index expected-index))
    ;; change the value of index
    (setq index (+ 100 (random 1000)))
    ;; assert that it has changed
    (assert (not (= index expected-index)))
    ;; increment the expected value
    (incf expected-index))

  ;; assert that the final expected value is n
  (assert (= expected-index 50)))

11

แคลคูลัสแลมบ์ดาที่ยังไม่พิมพ์

λf.(λx.f (x x)) (λx.f (x x))

3
ฉันไม่แน่ใจว่าสิ่งนี้นับว่าเป็นการเรียกซ้ำหรือไม่สิ่งที่เป็นพื้นฐานทางทฤษฎีพื้นฐานสำหรับมัน ... +1 ต่อไป
ปุย

@fluffy มันไม่เรียกซ้ำตัวเองไม่มีฟังก์ชั่นที่เรียกตัวเอง (โดยเฉพาะอย่างยิ่งเพราะฟังก์ชั่นไม่ได้ตั้งชื่อ)
ภูมิใจ haskeller

IMHO แลมบ์ดาแคลคูลัสเป็นแบบจำลองการคำนวณและไม่ใช่ภาษาโปรแกรม (เช่นไม่มีโมเดลเครื่องจักรคอนกรีตเราไม่สามารถพิจารณาแลมบ์ดาแคลคูลัสเป็น PL)
Ta Thanh Dinh

คุณสามารถสร้างเครื่องจักรที่ตีความแลมด้าแคลคูลัสได้อย่างแน่นอน และไวยากรณ์สามารถใช้เป็นภาษาการเขียนโปรแกรม ดูตัวอย่างgithub.com/MaiaVictor/caramel
Arthur B

10

Haskell, 24 ตัวอักษร

sequence_ (repeat (print "abc"))

หรือในรูปแบบย่อมี 24 ตัวอักษร

sequence_$repeat$print"" 

(แม้ว่าข้อความจะเปลี่ยนแปลง แต่จะยังคงวนซ้ำ - จะพิมพ์เครื่องหมายคำพูดสองบรรทัดและขึ้นบรรทัดใหม่ไม่สิ้นสุด)

คำอธิบาย: พิมพ์ "abc" นั้นเป็นการกระทำของ i / o ที่เพิ่งพิมพ์ "abc"
ซ้ำเป็นฟังก์ชั่นที่ใช้ค่า x และส่งกลับรายการที่ไม่มีที่สิ้นสุดที่ทำจาก x เท่านั้น
sequence_ เป็นฟังก์ชั่นที่รับรายการของแอ็คชั่น i / o และส่งคืนแอ็คชัน i / o ที่ดำเนินการทั้งหมดตามลำดับ

ดังนั้นโดยทั่วไปโปรแกรมนี้จะสร้างรายการคำสั่ง "abc" ที่ไม่มีที่สิ้นสุดและดำเนินการคำสั่งเหล่านั้นซ้ำ ๆ ไม่มีลูปหรือเรียกซ้ำ


4
ฉันกำลังจะไปโพสต์โดยทั่วไปคำตอบที่เหมือนกันใน Clojure แต่ผมคิดว่าจะเป็นrepeat a programming language statement which allows code to be repeatedly executed
seequ

3
fix(print"">>)สิ่งนี้ยังไม่เกี่ยวข้องกับฟังก์ชั่นการทำซ้ำที่มีชื่ออย่างชัดเจน
mniip

1
@TheRare ฉันไม่ทราบวิธีปิด แต่ใน Haskell ซ้ำไม่ได้เป็น "คำสั่งภาษาการเขียนโปรแกรมที่ช่วยให้รหัสที่จะดำเนินการซ้ำ ๆ " - มันเป็นฟังก์ชั่นที่สร้างรายการที่ไม่มีที่สิ้นสุด มันเป็นวงเดียวกับ "int [] arr = {x, x, x};" เป็นห่วง
ภูมิใจ Haskeller

1
ใช่ แต่บางสิ่งต้องดำเนินการโดยใช้การเรียกซ้ำเนื่องจากไม่มีมันเป็นไปไม่ได้โดยทั่วไป
ภูมิใจ haskeller

3
จริงๆแล้วทุกฟังก์ชั่นที่มีอยู่ในรหัสนี้ถูกกำหนดโดยใช้การเรียกซ้ำ - แม้กระทั่งพิมพ์
ภูมิใจ Haskeller

10

ASM (x86 + I / O สำหรับ Linux)

ไม่สำคัญว่าภาษาระดับสูงที่อ่อนแอของคุณจะต้องดิ้นรนเพียงใดมันจะยังคงเป็นเพียงการชี้แนะที่ซ่อนอยู่ ในท้ายที่สุดมันจะเป็น "goto" (jmp) เว้นแต่คุณจะรู้สึกเบื่อที่จะเปิดลูปใน runtime

คุณสามารถทดสอบโค้ดบนIdeone

นอกจากนี้คุณยังสามารถตรวจสอบรุ่นกลั่นมากขึ้นของความคิดนี้ในรหัสมัตเตโอเลีย DOS

มันเริ่มต้นด้วยสตริง 0..9 และแทนที่ด้วย A..J ไม่มีการใช้การกระโดดโดยตรง (ดังนั้นสมมติว่าไม่มีการ "ข้าม" เกิดขึ้น) ไม่เกิดขึ้นอีก

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

ส่วนหลัก:

mov dl, 'A' ; I refuse to explain this line!
mov ebx, msg ; output array (string)

call rawr   ; lets put address of "rawr" line on stack
rawr: pop eax ; and to variable with it! In same time we are breaking "ret"

add eax, 4 ; pop eax takes 4 bytes of memory, so for sake of stack lets skip it
mov [ebx], dl ; write letter
inc dl ; and proceed to next 
inc ebx
cmp dl, 'J' ; if we are done, simulate return/break by leaving this dangerous area
jg print

push eax ; and now lets abuse "ret" by making "call" by hand
ret

รหัสทั้งหมด

section     .text
global      _start                              

_start:

;<core>
mov dl, 'A'
mov ebx, msg

call rawr
rawr: pop eax

add eax, 4
mov [ebx], dl
inc dl
inc ebx
cmp dl, 'J'
jg print

push eax
ret
;</core>

; just some Console.Write()
print:
    mov     edx,len
    mov     ecx,msg
    mov     ebx,1
    mov     eax,4
    int     0x80

    mov     eax,1
    xor     ebx, ebx
    int     0x80

section     .data

msg     db  '0123456789',0xa
len     equ $ - msg

ฉันจะเรียกสิ่งนี้ว่าเป็นคู่ของcodegolf.stackexchange.com/a/34298/11259แต่ฉันเห็นว่านี่เป็นคำตอบก่อนหน้านี้ +1
บาดเจ็บทางดิจิตอล

@DigitalTrauma โอ้ฉันเห็นคนทำรุ่นกลั่นของความคิดของฉัน - เคล็ดลับเก่า แต่ในยุคของการจัดการรหัสคนมักจะลืมว่าสิ่งต่าง ๆ ใช้งานได้จริง (ฉันไม่ชอบเล่นกอล์ฟบ่อยเกินไปจะลดลงเป็น "ดูแม่! ฉันสามารถทำให้สิ่งต่าง ๆ เกิดขึ้นได้โดยการกดปุ่มเดียว!")
PTwr

9

ตัวประมวลผลล่วงหน้า C

"เทคนิค" เล็ก ๆ น้อย ๆ ที่ฉันคิดขึ้นมาในระหว่างการท้าทายที่ทำให้งงงวย ไม่มีการเรียกใช้ฟังก์ชันซ้ำ แต่มี ... การเรียกใช้ไฟล์ซ้ำ

noloop.c:

#if __INCLUDE_LEVEL__ == 0
int main() 
{
    puts("There is no loop...");
#endif
#if __INCLUDE_LEVEL__ <= 16
    puts(".. but Im in ur loop!");
    #include "noloop.c"
#else
    return 0;
}
#endif

ฉันเขียน / ทดสอบโดยใช้ gcc เห็นได้ชัดว่าคอมไพเลอร์ของคุณจำเป็นต้องสนับสนุน__INCLUDE_LEVEL__แมโคร (หรืออีกทางหนึ่งคือ__COUNTER__มาโครที่มีการปรับแต่ง) เพื่อให้สามารถคอมไพล์ได้ มันควรจะชัดเจนว่ามันใช้งานอย่างไร แต่เพื่อความสนุกให้รันตัวประมวลผลล่วงหน้าโดยไม่ต้องคอมไพล์โค้ด (ใช้-Eแฟล็กกับ gcc)


8

PHP

นี่คือหนึ่งเดียวกับ PHP วนซ้ำโดยรวมไฟล์เดียวกันจนกว่าตัวนับจะถึง $ สูงสุด:

<?php
if (!isset($i))
    $i = 0;        // Initialize $i with 0
$max = 10;         // Target value

// Loop body here
echo "Iteration $i <br>\n";

$i++;               // Increase $i by one on every iteration

if ($i == $max)
    die('done');    // When $i reaches $max, end the script
include(__FILE__);  // Proceed with the loop
?>

เช่นเดียวกับ for-loop:

<?php
for ($i = 0; $i < 10; $i++) {
    echo "Iteration $i <br>\n";
}
die('done');
?>

แย่แล้วสิ่งนี้นับว่าเป็นการเรียกซ้ำเช่นกันใช่ไหม
Pichan

อย่าคิดว่าเป็น - ความคล้ายคลึงกันมาจากใจของตัวอย่างของ @ Nathaniel: ผู้ประมวลผลล่วงหน้าจะรวมไฟล์เหล่านี้ซึ่งจะถูกประเมินพร้อมกัน
eithed

@Pichan ฉันจะบอกว่ามันเป็นห่วงมากขึ้นในขณะที่คุณจบด้วยสำเนาของรหัสในหน่วยความจำ
PTwr

ฉันเพิ่งเห็นคำถามวันนี้และมาพร้อมกับรหัสที่เกือบเหมือนกัน สายเกินไปสำหรับฉัน!
TecBrat

จะถูกheader("Location: .?x=".$_GET['x']+1);นับเป็น recursion?
Charlie

8

หลาม

รหัสต่อไปนี้ไม่มีฟังก์ชั่นวนซ้ำ (โดยตรงหรือโดยอ้อม) ไม่มีการวนซ้ำดั้งเดิมและไม่เรียกใช้ฟังก์ชันในตัว (ยกเว้นprint):

def z(f):
    g = lambda x: lambda w: f(lambda v: (x(x))(v), w)
    return g(g)

if __name__ == "__main__":
    def msg(rec, n):
        if (n > 0):
            print "Hello world!"
            rec(n - 1)
    z(msg)(7)

พิมพ์ "Hello world!" จำนวนครั้งที่กำหนด

คำอธิบาย:ฟังก์ชั่นzใช้combinator จุดคงที่Zอย่างเข้มงวดซึ่ง (ในขณะที่ไม่ได้กำหนดซ้ำ) อนุญาตให้แสดงอัลกอริทึมแบบเรียกซ้ำ


ฉันจะเรียกgซ้ำโดยอ้อมมาก
seequ

@ ธีร์ทำไม? อะไรคือเหตุผลของคุณ อะไรgโทรที่เรียกร้องgอีกครั้งหรือไม่ แน่นอนว่าเคล็ดลับคือแอปพลิเคชันด้วยตนเองg(g)แต่ไม่มีการเรียกซ้ำ คุณจะโทรgซ้ำโดยอ้อมถ้าคุณไม่เห็นg(g)? นี่เป็นวิธีมาตรฐานในการทำในภาษาที่ไม่อนุญาตให้ใช้คำจำกัดความซ้ำเช่นแคลคูลัสแลมบ์ดา
Petr Pudlák

คุณให้gเป็นอาร์กิวเมนต์แล้วโทรx x(x)
seequ

2
@TheRare ฟังก์ชั่น (หรือชุดของฟังก์ชั่น) ไม่ได้เรียกซ้ำหรือไม่เรียกซ้ำโดยวิธีการใช้มันจะถูกกำหนดโดยนิยามของมัน
Petr Pudlák

1
คำตอบทั้งหมดที่โกงไม่ทางใดก็ทางหนึ่ง: มีการเรียกซ้ำหรือวนซ้ำอยู่ที่ไหนสักแห่งถ้าไม่ได้อยู่ในคำตอบแล้วในรหัสคำตอบจะเรียก ฉันชอบวิธีนี้หนึ่งกลโกง
Wayne Conrad

8

รหัสเครื่อง z80

ในสภาพแวดล้อมที่คุณสามารถดำเนินการได้ทุกที่อยู่และแผนที่ ROM ทุกหนทุกแห่งแผนที่ 64kb ของ ROM ที่เต็มไปด้วยศูนย์ถึงพื้นที่ที่อยู่ทั้งหมด

มันทำอะไร: ไม่มีอะไร ซ้ำแล้วซ้ำเล่า

วิธีการทำงาน: โปรเซสเซอร์จะเริ่มดำเนินการไบต์00เป็นnopคำสั่งดังนั้นมันจะดำเนินการต่อไปถึงที่อยู่$ffffห่อรอบ$0000และดำเนินการnopต่อจนกว่าคุณจะรีเซ็ต

เพื่อให้น่าสนใจขึ้นเล็กน้อยเติมหน่วยความจำด้วยค่าอื่น ๆ (ระวังเพื่อหลีกเลี่ยงคำสั่งโฟลว์ควบคุม)


คุณสามารถเติมหน่วยความจำด้วยศูนย์และวางโปรแกรมจริงไว้ที่นั่นสักแห่ง
Seequ

ดังนั้นคุณสามารถใส่โปรแกรม 64k โดยไม่มีการแตกสาขาใด ๆ และมันจะรันซ้ำ ๆ
Bill Woodger

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

ชนิดของความสนุก :-)
Bill Woodger

8

Perl-regex

(q x x x 10) =~ /(?{ print "hello\n" })(?!)/;

การสาธิต

หรือลองเป็น:

perl -e '(q x x x 10) =~ /(?{ print "hello\n" })(?!)/;'

(?!)ไม่ตรงกับ ดังนั้นโปรแกรม regex พยายามจับคู่ตำแหน่งความกว้างศูนย์แต่ละอันในสตริงที่ตรงกัน

(q x x x 10)เป็นเช่นเดียวกับ(" " x 10)- ทำซ้ำspaceสิบครั้ง

แก้ไข: เปลี่ยน "อักขระ" เป็นตำแหน่งความกว้างศูนย์เพื่อให้แม่นยำยิ่งขึ้นเพื่อความเข้าใจที่ดีขึ้น ดูคำตอบสำหรับคำถาม stackoverflowนี้


6

T-SQL -12

print 1
GO 9

ที่จริงแล้วสิ่งที่แปลกมากของ Studio Management Server ของ SQL GO เป็นตัวคั่นสคริปต์และไม่ได้เป็นส่วนหนึ่งของภาษา T-SQL หากคุณระบุ GO ตามด้วยหมายเลขมันจะดำเนินการบล็อกนั้นหลายครั้ง


1
ฉันใช้ T-SQL เกือบทุกวันและไม่ทราบว่าคุณสามารถใช้ GO ได้ +1
CailinP

ในทางเทคนิคแล้วนั่นไม่ใช่ T-SQL GOจริง ๆ แล้วเป็นคำสั่ง SSMS ซึ่งเป็นสาเหตุที่คุณไม่สามารถใส่ไว้ในวัตถุที่เขียนสคริปต์ T-SQL เช่นพูดว่ากระบวนการที่เก็บไว้
RBarryYoung

ใช่ฉันเพิ่มว่าในความคิดเห็นสปอยเลอร์ของฉัน ฉันคิดว่าการใช้ sqlcmd น่าจะเป็นการโกงมากเกินไป
Michael B

6

C #

พิมพ์จำนวนเต็มทั้งหมดจาก uint.MaxValue เป็น 0

   class Program
   {
      public static void Main()
      {
          uint max = uint.MaxValue;
          SuperWriteLine(ref max);
          Console.WriteLine(0);
      }

      static void SuperWriteLine(ref uint num)
      {
          if ((num & (1 << 31)) > 0) { WriteLine32(ref num); }
          if ((num & (1 << 30)) > 0) { WriteLine31(ref num); }
          if ((num & (1 << 29)) > 0) { WriteLine30(ref num); }
          if ((num & (1 << 28)) > 0) { WriteLine29(ref num); }
          if ((num & (1 << 27)) > 0) { WriteLine28(ref num); }
          if ((num & (1 << 26)) > 0) { WriteLine27(ref num); }
          if ((num & (1 << 25)) > 0) { WriteLine26(ref num); }
          if ((num & (1 << 24)) > 0) { WriteLine25(ref num); }
          if ((num & (1 << 23)) > 0) { WriteLine24(ref num); }
          if ((num & (1 << 22)) > 0) { WriteLine23(ref num); }
          if ((num & (1 << 21)) > 0) { WriteLine22(ref num); }
          if ((num & (1 << 20)) > 0) { WriteLine21(ref num); }
          if ((num & (1 << 19)) > 0) { WriteLine20(ref num); }
          if ((num & (1 << 18)) > 0) { WriteLine19(ref num); }
          if ((num & (1 << 17)) > 0) { WriteLine18(ref num); }
          if ((num & (1 << 16)) > 0) { WriteLine17(ref num); }
          if ((num & (1 << 15)) > 0) { WriteLine16(ref num); }
          if ((num & (1 << 14)) > 0) { WriteLine15(ref num); }
          if ((num & (1 << 13)) > 0) { WriteLine14(ref num); }
          if ((num & (1 << 12)) > 0) { WriteLine13(ref num); }
          if ((num & (1 << 11)) > 0) { WriteLine12(ref num); }
          if ((num & (1 << 10)) > 0) { WriteLine11(ref num); }
          if ((num & (1 << 9)) > 0) { WriteLine10(ref num); }
          if ((num & (1 << 8)) > 0) { WriteLine09(ref num); }
          if ((num & (1 << 7)) > 0) { WriteLine08(ref num); }
          if ((num & (1 << 6)) > 0) { WriteLine07(ref num); }
          if ((num & (1 << 5)) > 0) { WriteLine06(ref num); }
          if ((num & (1 << 4)) > 0) { WriteLine05(ref num); }
          if ((num & (1 << 3)) > 0) { WriteLine04(ref num); }
          if ((num & (1 << 2)) > 0) { WriteLine03(ref num); }
          if ((num & (1 <<  1)) > 0) { WriteLine02(ref num); }
          if ((num & (1 <<  0)) > 0) { WriteLine01(ref num); }
      }

      private static void WriteLine32(ref uint num) { WriteLine31(ref num); WriteLine31(ref num); }
      private static void WriteLine31(ref uint num) { WriteLine30(ref num); WriteLine30(ref num); }
      private static void WriteLine30(ref uint num) { WriteLine29(ref num); WriteLine29(ref num); }
      private static void WriteLine29(ref uint num) { WriteLine28(ref num); WriteLine28(ref num); }
      private static void WriteLine28(ref uint num) { WriteLine27(ref num); WriteLine27(ref num); }
      private static void WriteLine27(ref uint num) { WriteLine26(ref num); WriteLine26(ref num); }
      private static void WriteLine26(ref uint num) { WriteLine25(ref num); WriteLine25(ref num); }
      private static void WriteLine25(ref uint num) { WriteLine24(ref num); WriteLine24(ref num); }
      private static void WriteLine24(ref uint num) { WriteLine23(ref num); WriteLine23(ref num); }
      private static void WriteLine23(ref uint num) { WriteLine22(ref num); WriteLine22(ref num); }
      private static void WriteLine22(ref uint num) { WriteLine21(ref num); WriteLine21(ref num); }
      private static void WriteLine21(ref uint num) { WriteLine20(ref num); WriteLine20(ref num); }
      private static void WriteLine20(ref uint num) { WriteLine19(ref num); WriteLine19(ref num); }
      private static void WriteLine19(ref uint num) { WriteLine18(ref num); WriteLine18(ref num); }
      private static void WriteLine18(ref uint num) { WriteLine17(ref num); WriteLine17(ref num); }
      private static void WriteLine17(ref uint num) { WriteLine16(ref num); WriteLine16(ref num); }
      private static void WriteLine16(ref uint num) { WriteLine15(ref num); WriteLine15(ref num); }
      private static void WriteLine15(ref uint num) { WriteLine14(ref num); WriteLine14(ref num); }
      private static void WriteLine14(ref uint num) { WriteLine13(ref num); WriteLine13(ref num); }
      private static void WriteLine13(ref uint num) { WriteLine12(ref num); WriteLine12(ref num); }
      private static void WriteLine12(ref uint num) { WriteLine11(ref num); WriteLine11(ref num); }
      private static void WriteLine11(ref uint num) { WriteLine10(ref num); WriteLine10(ref num); }
      private static void WriteLine10(ref uint num) { WriteLine09(ref num); WriteLine09(ref num); }
      private static void WriteLine09(ref uint num) { WriteLine08(ref num); WriteLine08(ref num); }
      private static void WriteLine08(ref uint num) { WriteLine07(ref num); WriteLine07(ref num); }
      private static void WriteLine07(ref uint num) { WriteLine06(ref num); WriteLine06(ref num); }
      private static void WriteLine06(ref uint num) { WriteLine05(ref num); WriteLine05(ref num); }
      private static void WriteLine05(ref uint num) { WriteLine04(ref num); WriteLine04(ref num); }
      private static void WriteLine04(ref uint num) { WriteLine03(ref num); WriteLine03(ref num); }
      private static void WriteLine03(ref uint num) { WriteLine02(ref num); WriteLine02(ref num); }
      private static void WriteLine02(ref uint num) { WriteLine01(ref num); WriteLine01(ref num); }
      private static void WriteLine01(ref uint num) { Console.WriteLine(num--); }
   }

1
ฉันไม่รู้จริงๆว่าสิ่งนี้มีค่าหรือเปล่า คุณกำลังเรียก WriteLine01 Int.MaxValue ครั้งอย่างชัดเจน มันเพิ่งระเบิดออกมาหลัง callstack จำนวนมหาศาล
Michael B

มันจะไม่นับได้อย่างไร ไม่มีการวนซ้ำและไม่มีการเรียกซ้ำ
LVBen

1
นอกจากนี้สแตกการโทรไม่ได้อยู่ใกล้กับสถานที่ขนาดใหญ่เว้นแต่คุณอาจพิจารณาว่าสายที่สูงถึง 32 สายนั้นมีขนาดใหญ่
LVBen

1
ทำไมมีเพียง 32 ครั้งแทนที่จะเป็น 4294967296 ครั้ง
LVBen

4
@ ja72 หากฉันเคยทำงานในโครงการโอเพนซอร์ซซึ่งฉันไม่สามารถใช้ลูปหรือเรียกซ้ำได้ฉันก็จะมีส่วนร่วมในรหัสเช่นนี้!
LVBen

6

JS (ในเบราว์เซอร์)

แล้วเรื่องนี้ล่ะ

document.write(new Date());
location = location;

พิมพ์เวลาปัจจุบันและโหลดหน้าซ้ำ


โอ้โห ฉันเพิ่งโพสต์คำตอบด้วยแนวคิดพื้นฐานเดียวกัน ฉันสแกนหน้าเพื่อหา "JavaScript" หรืออะไรก็ตามที่แสดงแท็ก HTML ฉันคิดว่าฉันอาจปล่อยให้คำตอบของฉันเพียงเพราะมันจัดการกับมุมกรณีที่สถานที่มี "#" อย่างไรก็ตาม +1
คม

ใน Firefox 30:[Exception... "The operation is insecure." code: "18" nsresult: "0x80530012 (SecurityError)" location: "<unknown>"]
Alex Reynolds

@AlexReynolds Huh แปลก ฉันทำงานได้ดีใน FF 30
Pichan

ฉันคัดลอกและวางในโค้ดของคุณตามที่เขียนเท่านั้น มันไม่ทำงาน บางทีคุณอาจเปิดใช้งานการตั้งค่าความปลอดภัยพิเศษบางอย่างเพื่อให้ทำงานได้หรือไม่
Alex Reynolds

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