Цикл без 'циклу' [закрито]


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 ранку за східним часом. Удачі!

ОНОВЛЕННЯ:

Заспокоїти розгубленість, яка все ще виражається, це може допомогти:

Правила, як зазначено вище:

  • Не використовуйте циклів або goto
  • Функції не можуть називати себе
  • Робіть все, що завгодно, у "циклі"

Якщо ви хочете щось реалізувати, і правила цього прямо не забороняють, продовжуйте це робити. У багатьох відповідях правила вже зігнуті.


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, те, що я " повісив ", - це те, що питання на сайті повинні бути тематичними для сайту: це означає мати чітку, об'єктивну специфікацію, чого це питання не відповідає.
Пітер Тейлор

Відповіді:


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

Демо

Очищає горло, друкує "Банан" 3070 разів, а також ставить "Апельсин ти радий, що я не сказав банан?".

Це використовує смішні функціональні можливості визначення методу Рубі, щоб визначити кожен метод, який знаходиться в алфавітному порядку між словами "ах" і "також" ("ах", "ахен", "ахео", "ахеп", "ахек", "aher", "ahes", "ahet", "aheu", "ahev" ...) спочатку надрукуйте Banana, а потім зателефонуйте наступному у списку.


4
Врешті-решт потрапляє "також", що визначено, а тому не пропускається.
гістократ

77
Це істерично.
Майкл Б

4
@barrycarter: У Ruby, String#nextякий викликається method_missingфункціями, схожим на додавання 1 до числа, за винятком того, що він працює з усіма буквено-цифровими символами (і не-alnum, якщо вони є єдиними символами в рядку). Дивіться 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

Пітон - 16

або будь-якою іншою мовою з eval.

exec"print 1;"*9

Чи можете ви описати, що робить ваша програма?
CailinP

10
Він бере рядок ( "print 1;"), дублює його 9 разів ( *9), потім виконує отриманий рядок ( exec). Повторення шматка коду, фактично не циклічно.
scragar

12
Так, для множення рядків!
Thane Brimhall

2
Також працює в Ruby , якщо ви зміните 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, який викликає фіналізатор, створює новий екземпляр Pкласу, який при спробі очищення створює новий, Pякий викликає фіналізатор .. .

Програма врешті-решт гине.

Редагувати: Незрозуміло, що це відбувається лише близько 45 тис. Разів перед смертю. Я не зовсім знаю, як GC з'ясував мій хитрий нескінченний цикл, але це так. Коротше, схоже, він цього не зрозумів, і нитка просто загинула приблизно через 2 секунди виконання: https://stackoverflow.com/questions/24662454/how-does-a-garbage-collector-avoid-an -нескінченна-петля-тут

Edit2: Якщо ви вважаєте, що це занадто багато, як рекурсія, розгляньте моє інше рішення: https://codegolf.stackexchange.com/a/33268/23300

Він використовує переосмислення загальних методів так, що під час виконання він постійно генерує нові методи, і кожен метод в терміні називає новоспечений метод. Я також уникаю використання referenceпараметрів типу, оскільки зазвичай час виконання може надавати код для цих методів. З valueпараметром типу час виконання змушений створювати новий метод.


20
Я навіть не знав, що у C # були деструктори. +1 для навчання мене.
seequ

4
@TheRare, це так, але вони мають недетермінований характер і їх ніколи не можна викликати під час виконання програми. Вони реалізуються як перевизначення віртуального методу, Finalizeтому їх іноді називають finalizer. У реальному C # ви повинні використовувати IDisposableшаблон.
Майкл Б

Здається, є час очікування, який виникає в якийсь момент. Я не думаю, що саме GC зупиняє ваш цикл, але натомість операційна система вирішує, що ваша програма займає занадто багато часу, щоб закінчитись.
LVBen

Я думаю, що це справді час виконання рішення, що вбивати програму не обов'язково ОС. Потоку збору сміття, що викликається в кінці програми, надається фіксований часовий проміжок ~ 2 секунди до його загибелі.
Майкл Б

З деякими незначними модифікаціями (не дозволяючи програмі закінчуватися, випускаючи об'єкт 1-го Р в GC і багаторазово викликаючи GC.Collect), я можу змусити його працювати безстроково.
LVBen

53

Befunge

.

Старий добрий Befunge виводить 0 (з порожнього стека) майже назавжди, коли лінії обертаються.


1
Га! Я люблю подібні трюки
CailinP

47

JS

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

Функція весело!

Функція, яка створює іншу функцію з тим самим тілом, що і сама, а потім виконує її.

Він з’явиться привіт в кінці, коли буде досягнуто ліміту стека і вся справа згортається.

Відмова від відповідальності: ви не зможете робити нічого у своєму браузері, поки не буде досягнуто ліміту стека.


І ще одне, більше зла :

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вказуємо адресу входу на стеку перед поверненням, створюючи таким чином нескінченний цикл.


Мені було цікаво, чи це можливо в зборах.
Ян Д. Скотт


1
Я збирався зателефонувати codegolf.stackexchange.com/a/34295/11259 як копію цього, але я бачу, що це насправді попередня відповідь
Digital Trauma

@DigitalTrauma: так, я помітив після того, як я опублікував свій запис, але я потрапив до фотографії мадам Мім. :-) На щастя, є деякі відмінності (він трохи більш затуманений і працює на 32-бітному Linux, моя грається прямо на DOS і не має іншого стрибка), якщо ні в кого немає заперечень, я б це все одно залишив тут.
Matteo Italia

@MatteoItalia Не заплутаний, просто шалений;) (Тхо "додати eax, 4" мене також бентежив, я не зміг знайти таблицю розмірів опкоду, тому я просто дико здогадався). Я зробив це в онлайн-компіляторі, поки мій робочий проект складався, щоб це виглядало жахливо. Відмінна ідея використання "start:".
PTwr

37

Java

Прямо від XKCD

Зв’язування

Це нескінченна гра на вилов між батьком і дитиною!

Мета CHILDзадана, PARENTа мета PARENT- CHILD. Під час PARENTвиклику AIMвін викидає екземпляр BALLкласу, і він потрапляє у заяву catch. Потім заява про вилов називає, PARENT.TARGET.AIMде є ціль CHILD. CHILDПримірник робить те ж саме і «кидає м'яч назад» батькові.


3
Мені подобаються ці комікси!
Дерек 朕 會 功夫

1
Було б краще, якби м'яч насправді кидався між батьком і дитиною. Як є, куля завжди кидається і ловиться тим самим «людиною».
Ajedi32

@ Ajedi32 Насправді, схоже, він кидає його назад і назад; Батьки TARGET - це дитина, а ціль дитини - батько. Призваний батько, який кидає м'яч і має націлену дитину та кидає м'яч, київ петлю
Алекс Коулман

12
@AlexColeman Цей код аналогічний тому, що батько кидає м'яч у повітря, ловить його, а потім передає дитині, яка робить те саме, перед тим як повернути м'яч батькові тощо.
Ajedi32

11
Команда TARGET.AIM(B);в методі AIM- рекурсивний виклик. Тож це порушує правило "функції не можуть назвати себе".
Теодор Норвелл

31

Баш, 3 символи

yes

так, неодноразово повертається "y" на консоль

Редагувати: усім рекомендується редагувати цей рядок:

yes something | xargs someaction

(спасибі Олів'є Дулаку)


1
Чому це буде продовжувати працювати? Я не ставив під сумнів це лише намагаючись з'ясувати, чому.
Teun Pronk

2
@TeunPronk yes- це команда bash, яка виводить слово так, поки воно не вбите або потік не закриється. Якщо він пише на екран, він ніколи не зупиниться, поки не вб'єш його. Це свого роду обман, оскільки це команда, яка в основному складається з циклу над printf.
scragar

1
Більш весело було б використовувати, yesщоб продовжувати інший цикл.
trlkly

3
@izkata: але тоді ви можете:: yes something | xargs someactionнемає рекурсії (ви можете навіть додати -n 1 до xargs, щоб мати лише 1 "щось" на рядок тощо). Використання xargs відкриває шлях до складнішої поведінки (навіть тих, у кого взагалі нічого не пов’язано з результатом "так")
Олів'є Дулак

4
@scragar ви мали би просто відповісти yes.
daviewales

28

C, 35 символів

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

Програма виконує себе. Я не впевнений, вважається це рекурсією чи ні.


4
@mniip Хвост рекурсії, тоді, якщо він застосовувався на рівні процесу
Izkata

3
@Izkata Хвост рекурсії все ще є рекурсією, але це не рекурсія. Рекурсія передбачає функцію (або процес в даному випадку) "очікування", щоб закінчиться ще одна ітерація. У цьому випадку execновий процес замінює вихідний, тому немає стека викликів, який з часом повернеться або переповниться.
мільйон

4
@millinon Мовою, яка підтримує оптимізацію, хвостова рекурсія замінює попередній виклик у стеку викликів, подібно до того, як execзамінює попередній процес. Він також не переповниться.
Ізката

1
@millinon просто для того, щоб бути супер педантичним і довше затягувати цю дискусію, в мові програмування схеми ця оптимізація є мовною особливістю. Це в специфікації , що якщо ви зробите хвостову рекурсію виклику, інтерпретатор / компілятор має викликати останній кадр стека. Це тому, що в схемі немає вбудованих циклів циклу, тому єдиний спосіб реалізувати цикл у схемі - це зробити хвостову рекурсію, і це було б дуже смачно, якщо у вас є стік переповнення від спроби циклу занадто багато разів :)
Орд

2
Якщо ви хочете керувати педангією, у схемі немає "оптимізації виклику хвоста", вона має "належні хвостові дзвінки". Це не оптимізація, це основна вимога мовного стандарту, а якщо його не надавати, це не дозволяється, тому "оптимізація" (або припущення, що це має щось спільне з рекурсією) - це дуже обережний термін.
Левшенко

28

C (із вбудованими GCC - також, здається, працює з клангом)

  • Немає явних циклів
  • Немає явних готів
  • Рекурсії немає
  • Просто гарний старомодний возитися зі стеком (діти, не намагайтеся цього вдома без нагляду):
#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()вбудований, щоб отримати зворотну адресу (в main()), до frameloop()якої повернетесь. Ми повертаємо цю адресу.
  • printf() щоб показати, що ми лупимо
  • тепер ми дзвонимо frameloop()із зворотною адресою попереднього дзвінка. Ми переглядаємо стек поточної зворотної адреси, і коли ми знаходимо її, підміняємо попередню зворотну адресу.
  • Потім повертаємося з 2-го 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
Брайан Мінтон

4
@BrianMinton Насправді подібну річ слід досягти за допомогою setjmp()/ longjmp(). Вони не в стандарті c, але є у стандартній бібліотеці . Мені сьогодні здавалося, що я міняю стек вручну ;-)
Digital Trauma

@BrianMinton Думаю, що він знаходиться в специфікаціях процесора, що робить його (апаратну) платформу залежною. І це досить небезпечно використовувати, коли стекфрейм автоматично генерується, я би не здивувався, якби AV плакав про такий код. Перевірте це чи це для версій x86 asm.
PTwr

27

Хаскелл

Наведений нижче код не містить рекурсивної функції (навіть опосередковано), не має циклічної примітиви та не викликає жодної вбудованої рекурсивної функції (використовує лише 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. Це рекурсивний тип даних, який споживає себе, що, як показано в прикладі, дозволяє довільне повторення. По-перше, ми можемо побудувати extract x, що по суті виражає самозастосування x xв нетиповому рахунку лямбда. І це дозволяє побудувати комбінатор Y, визначений як λ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
Рекурсивні структури даних замість рекурсивних функцій ... приємно.
Наближається до

6
це мені близько до серця, коли хтось зацікавлений у цілому функціональному програмуванні. Ви щойно показали, як зробити Y-комбінатор з негативно повторюваним типом даних. Ось чому загальні мови вимагають повторюваних типів, які трапляються праворуч від стрілки, і чому дерева троянди заборонені. Хороший! Я зробив тут рахунок, щоб підтвердити це!
Джейк

Ви можете вилучити letприв'язку та визначити yc f = extract $ C $ f.extract, оскільки, letможливо, прив'язка є мовною функцією, яка дозволяє рекурсію (класичну let x = x in x). Це також зменшує деякі особливості :)
Earth Engine

або навітьyc = extract . C . (.extract)
Earth Engine

@EarthEngine Щоправда, я просто хотів зберегти структуру ближче до початкового визначення Y.
Петро Пудлак

26

C ++

Нижче наведено зворотний відлік від 10 до "Вибух!" використовуючи метапрограмування шаблонів.

#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безпосередньо без проміжна форма.
Левшенко

@ Левшенко так, я знаю. Але знову ж таки, наприклад, приклад шаблону робить те ж саме - використовує рекурсивну функцію на мові повного Тюрінга під час компіляції - різниця лише в тому, що constexpr використовує мову, схожу на C ++. Як і у всіх відповідях, і цей відповідає правилам, і я просто чесно ставлюся до цього. constexprбула спеціально розроблена для того, щоб зробити метапрограмування шаблону (деякими аспектами) застарілим, тому його, безумовно, варто згадати у публікації з цього питання.
Натаніел

1
+1: &countdown<N-1> != &countdown<N>.
Томас Едінг

21

Java

Давайте пограємо з завантажувачем класу Java та встановимо його як власного батька:

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());

    }
}

Цей цикл насправді настільки сильний, що вам доведеться використовувати його, kill -9щоб зупинити його :-)

Він використовує 100,1% мого процесора Mac.

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, оскільки це насправді змушує його створити абсолютно новий тип, і кожен екземпляр методу повинен створювати новий метод. Тут не можна поділитись кодом. Врешті-решт, ми вбиваємо стек викликів, оскільки він нескінченно чекає повернення int, яке ми обіцяли, але ніколи не доставляли. Аналогічно ми продовжуємо писати створений нами тип, щоб зробити його цікавим. В основному кожен C, який ми називаємо, це абсолютно новий метод, який просто має той самий корпус. Це насправді неможливо для такої мови, як C ++ або D, які роблять свої шаблони під час компіляції. Оскільки C # JIT дуже лінивий, він створює цей матеріал лише в останній можливий момент. Таким чином,


14

Redcode 94 (основна війна)

MOV 0, 1

Копіює інструкцію за нульовою адресою для адреси однієї Оскільки в Core War всі адреси відносяться до поточної адреси ПК і за модулем розміром ядра, це нескінченний цикл в одній інструкції, що не перестрибує.

Ця програма (воїн) називається " Imp " і вперше була опублікована А. К. Дьюдней.


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
Комбінатор Y?
aditsu

5
Технічно я думаю, що це комбінатор Z, оскільки це для суворої мови. Комбінатор Y вимагає ледачої мови, щоб уникнути нескінченного розгортання. Різниця лише в тому, що остання його частина є ета-розширеною.
lrn

12

JS

Не дуже оригінально, але мало. 20 символів.

setInterval(alert,1)

Ви насправді можете видалити, ,1і воно все одно спрацює,
Дерек 朕 會 功夫

@Derek 朕 會 功夫 якщо я це зроблю, я отримую лише одне попередження про Firefox
xem

1
У хромі він працює без останнього параметра. Код слід вважати дійсним, якщо він працює принаймні в одному середовищі.
Дерек 朕 會 功夫

3
@Derek 朕 會 功夫setInterval- це не твердження; це лише функція. Він використовується всередині оператора виразів, і якщо ми не можемо використовувати вирази, я просто навіть не знаю більше.
Кін

1
@Cory - Ну, мабуть, це дійсно тоді!
Дерек 朕 會 功夫

12

Сигнали в С

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

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

Поведінка цієї програми, очевидно, дуже невизначена, але сьогодні на моєму комп’ютері вона продовжує друкувати "Привіт, світ!".


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. зміна значення символу під назвою "запасний" не пов'язуватиметься з індексом

;; 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

ІМХО, обчислення лямбда - це модель обчислень і не є мовою програмування (тобто без конкретної моделі машини ми не можемо розглядати лямбда-обчислення як ПЛ).
Та Тхань Дінь

Ви можете абсолютно створити машину, що інтерпретує обчислення лямбда. А синтаксис можна використовувати як мову програмування. Дивіться, наприклад, github.com/MaiaVictor/caramel
Артур Б

10

Хаскелл, 24 символи

sequence_ (repeat (print "abc"))

або у стислому вигляді з 24 символами

sequence_$repeat$print"" 

(хоча текст змінено, це все одно буде циклічно - це буде друкувати дві лапки та новий рядок нескінченно)

пояснення: друк "abc" - це в основному дія вводу-виводу, яка просто друкує "abc".
повтор - це функція, яка приймає значення x і повертає нескінченний список, складений лише з x.
послідовність_ - це функція, яка приймає список дій вводу-виводу і повертає дію вводу-виводу, яка виконує всі дії послідовно.

тому, в основному, ця програма складає нескінченний список команд "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 + введення / виведення для Linux)

Не важливо, наскільки ваші мовчазні мови високого рівня будуть боротися, все одно це буде просто прихована маніпуляція з покажчиком інструкцій. Зрештою, це буде якийсь "goto" (jmp), якщо тільки вам не нудно, щоб розгорнути цикл під час виконання.

Ви можете перевірити код на Ideone

Ви також можете переглянути більш вдосконалену версію цієї ідеї у DOS-коді Matteo Italia .

Він починається з рядка 0..9 і замінює його на A..J, не використовуються прямі стрибки (тому дозвольмо сказати, що жодного "goto" не сталося), а також повторення.

Код, мабуть, міг би бути меншим, якщо зловживати розрахунком адреси, але робота над онлайн-компілятором є клопіткою, тому я залишу його таким, яким він є.

Основна частина:

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
Digital Trauma

@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. Цикли, включивши той самий файл, поки лічильник не досягне $ max:

<?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:

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

Дарн, це також вважається рекурсією, чи не так?
Пічан

Не думайте, що це - подібність приходить на думку прикладу @ Nathaniel: препроцесор включить ці файли, які потім оцінюються одночасно.
вийшов

@Pichan Я б сказав, що це більше циклу розгортання, оскільки ви закінчуєте копіями коду в пам'яті.
PTwr

Я щойно побачив це питання і придумав майже однаковий код. Занадто пізно для мене!
TecBrat

Чи header("Location: .?x=".$_GET['x']+1);зараховується до рекурсії?
Чарлі

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)

Друкує "Привіт, світ!" задана кількість разів.

Пояснення: Функція zреалізує суворий Z -комбінатор з фіксованою точкою , який (хоча не рекурсивно визначений) дозволяє виражати будь-який рекурсивний алгоритм.


Я б назвав gдуже побічно рекурсивним.
seequ

@TheRare Чому? Який ваш аргумент? Що gназиває цей дзвінок gзнову? Зрозуміло, що хитрість полягає в самозастосуванні g(g), але рекурсії немає. Ви б дійсно назвали gпобічно рекурсивним, якби не бачили g(g)? Це стандартний спосіб зробити це в мовах, які не дозволяють рекурсивних визначень, наприклад, лямбдального числення.
Петро Пудлак

Ви наводите gяк аргумент, xа потім дзвоните x(x).
seequ

2
@TheRare Функція (або набір функцій) не є рекурсивною чи не рекурсивною за тим, як вона використовується, це визначається саме її визначенням.
Петро Пудлак

1
всі відповіді підробляють так чи інакше: завжди є десь рекурсія або цикл , якщо ні у відповіді, то в коді відповідь викликає. Мені подобається, як це обманює.
Уейн Конрад

8

z80 машинного коду

У середовищі, де ви можете виконати будь-яку адресу та скріпити карту ROM скрізь, нанесіть 64 кб ПЗУ, заповнені нулями, на весь адресний простір.

Що це робить: нічого. Неодноразово.

Як це працює: процесор почне виконувати, байт 00- це nopінструкція, тому він буде просто продовжувати, діставатиметься до адреси $ffff, обертатись $0000та продовжувати виконувати nops, поки ви не скинете її.

Щоб зробити це трохи цікавіше, заповніть пам’ять яким-небудь іншим значенням (будьте обережні, щоб уникнути інструкцій з управління потоком).


Ви можете заповнити пам'ять нулями, а десь помістити справжню програму.
seequ

Отже, ви могли б вкласти програму 64k, не маючи розгалуження, і вона просто повторюється?
Білл Вудгер

@BillWoodger ви могли б, особливо якщо у вас немає перебоїв на платформі (або жодної не ввімкнено)
harold

Вид веселощів :-)
Білл Вудгер

8

Перл-регекс

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

демонстрація

або спробуйте як:

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

(?!)Ніколи не збігаються. Отже, двигун регулярного випробовування намагається відповідати кожному положенню нульової ширини у відповідній рядку.

Те (q x x x 10)саме, що (" " x 10)- повторити spaceдесять разів.

Редагувати: змінив "символи" на позиції нульової ширини, щоб бути точнішими для кращої зрозумілості. Дивіться відповіді на це запитання про поточний процес .


6

T-SQL -12

print 1
GO 9

Насправді більше примх студії управління сервісом Sql. GO є роздільником скриптів і не є частиною мови T-SQL. Якщо ви вказали GO з наступним числом, він виконає блок, який багато разів.


1
Я використовую T-SQL майже щодня і не уявляв, що ви можете це зробити з GO. +1
CailinP

Технічно це не T-SQL. GOнасправді є директивою SSMS, і тому ви не можете розмістити її в об'єктах, написаних T-SQL, як, наприклад, збережена процедура.
RBarryYoung

Так, я додав це у своєму коментарі до спойлера. Я вважаю, що використання sqlcmd було б занадто великим обманом.
Майкл Б

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 раз. Він просто вибухнув за величезною кількістю стовбурів.
Майкл Б

Як це не рахується? Немає циклу і немає рекурсії.
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>"]
Алекс Рейнольдс

@AlexReynolds Так, дивно. Шахта працює чудово на FF 30.
Пічан

Я лише скопіював і вставив у ваш код так, як було написано. Це не працює. Можливо, у вас є якісь спеціальні налаштування безпеки, щоб зробити цю роботу?
Алекс Рейнольдс

@AlexReynolds Ні, ніколи не змінював жодних налаштувань безпеки. І він працює і на Chrome.
Пічан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.