Цикл без «зацикливания» [закрыто]


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 часов утра по восточному времени. Удачи!

ОБНОВИТЬ:

Чтобы смягчить путаницу, которая все еще выражается, это может помочь:

Правила как указано выше:

  • Не используйте петли или переход
  • Функции не могут называть себя
  • Делай, что хочешь в «петле»

Если вы хотите реализовать что-то, а правила явно не запрещают это, сделайте это. Многие ответы уже согнули правила.


27
Для тех, кто хочет простой трюк, я не могу потрудиться опубликовать его: P Просто сделайте 2 функции, function Aвызовы function Bи function Bвызовы, function Aпока одна из функций выполняет что-то. Поскольку функция не вызывает сама себя, она должна быть действительной на основе критериев ^. ^
Теун Пронк

2
«Изменился конкурс популярности за акцент на творчестве» Изменение вопроса - это обман!
двоюродный брат Кокаин

4
Определение «рекурсия» не очень полезно. Было бы лучше запретить рекурсивные функции , которые являются функциями, которые ссылаются на себя, прямо или косвенно.
ЛРН

3
Что неясно, так это «определения» конструктора цикла и рекурсии. Ни один не очень точный. Пример: rep(f){f();f();}- это оператор (объявление функции - это оператор в некоторых языках), который позволяет выполнять код повторно. Это запрещено. Вы просите код для реализации цикла. Если этот код синтаксически является оператором, вы просто запретили его. Другой пример: f(b) { b(); g(b); }; g(b) { f(b); }. Я бы сказал, fчто это рекурсивная функция (будучи взаимно рекурсивным с g). Это запрещено?
ЛРН

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 раз, а также помещает «Апельсин, ты рад, что я не сказал банан?».

При этом используется смешная функциональность определения метода «точно в срок» в Ruby для определения каждого метода, который находится в алфавитном порядке между словами «ahem» и «Кроме того» («ahem», «ahen», «aheo», «ahep», «aheq», "aher", "ahes", "ahet", "aheu", "ahev" ...), чтобы сначала напечатать Banana, а затем вызвать следующий в списке.


4
В конце концов он попадает в «также», что определено и, следовательно, не пропущено.
гистократ

77
Это истерика.
Майкл Б

4
@barrycarter: в Ruby, String#nextкоторый вызывается в method_missingфункциях, более или менее похожих на добавление 1 к числу, за исключением того, что он работает со всеми буквенно-цифровыми символами (и не с буквенными значениями, если они являются единственными символами в строке). См. Ruby-doc.org/core-2.1.2/String.html#method-i-next
3Doubloons

2
@NickT его можно использовать в таких классах, как XML-сборщики, где вы можете использовать любой тег, созданный только b.my_tag. Это также используется в моделях ActiveRecord или OpenStruct. В «Ват-разговоре» он говорит, что глобальный method_missing- это плохо, но сфера действия - это круто.
Хаулет

2
Я помню старый комментарий к другой рубиновой программе: «Мне нравится, потому что в нем есть мет»
Видья Сагар

82

Python - 16

или любой другой язык с eval.

exec"print 1;"*9

Можете ли вы описать, что делает ваша программа?
CailinP

10
Он принимает строку ( "print 1;"), дублирует ее 9 раз ( *9), затем выполняет результирующую строку ( exec). Повторение фрагмента кода на самом деле без зацикливания вообще.
scragar

12
Yay для умножения строк!
Тейн Бримхолл

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который вызывает финализатор. ,

Программа со временем умирает.

Edit: необъяснимо, это работает только около 45k раз, прежде чем умереть. Я не совсем знаю, как GC вычислил мой хитрый бесконечный цикл, но это произошло. Суть в том, что кажется, что он этого не понял, и поток был просто убит примерно через 2 секунды выполнения: https://stackoverflow.com/questions/24662454/how-does-a-garbage-collector-avoid-an -infinite-петли здесь

Edit2: если вы думаете, что это слишком похоже на рекурсию, рассмотрите мое другое решение: https://codegolf.stackexchange.com/a/33268/23300

Он использует преобразование универсальных методов, так что во время выполнения он постоянно генерирует новые методы, и каждый метод в термине вызывает новый метод. Я также избегаю использования referenceпараметров типа, так как обычно среда выполнения может совместно использовать код для этих методов. С valueпараметром типа среда выполнения вынуждена создавать новый метод.


20
Я даже не знал, что у C # есть деструкторы. +1 для обучения меня.
Seequ

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

Кажется, что в определенный момент происходит тайм-аут. Я не думаю, что это GC останавливает ваш цикл, но вместо этого операционная система решает, что ваша программа заканчивается слишком долго.
LVBen

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

С некоторыми незначительными изменениями (не позволяя программе завершиться, выпустив 1-й объект P в 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 Довольно точно наверняка.
Seequ

Я думаю, что вам не хватает fв конце вашей второй функции. Это должно быть }f()в конце.
Чираг Бхатия - chirag64

2
К сожалению, я это заметил, потому что попробовал. : P
Чираг Бхатия - chirag64

7
-1 - это просто рекурсия.
user253751

39

сборка x86 / DOS

    org 100h

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

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

Я сказал нет реверсии хвоста ? Сделал я? madame mim purple dragons

Как это устроено

retИнструкция, используется для возврата из функции, фактически выталкивает адрес возврата из стека (который обычно ставится там соответствующий call) и скачет к нему. Здесь на каждой итерации pushперед входом мы указываем адрес точки входа в стек, создавая, таким образом, бесконечный цикл.


Мне было интересно, если это было возможно в сборке.
Ян Д. Скотт


1
Я собирался вызвать codegolf.stackexchange.com/a/34295/11259 как дубликат этого, но я вижу, что это на самом деле более ранний ответ
Digital Trauma

@DigitalTrauma: да, я заметил после того, как я разместил свою запись, но я попал на прикрепленную к фотографии мадам Мим. :-) К счастью, есть некоторые отличия (он немного более запутан и работает на 32-битном Linux, мой работает прямо на DOS и не имеет другого прыжка), если никто не возражает, я все равно оставлю это здесь.
Matteo Italia

@MatteoItalia Не запутанный, просто дурацкий;) (хотя "add eax, 4" также смутило меня, я не смог найти таблицу размеров кода операции, поэтому я просто угадал размер) Я сделал это в онлайн-компиляторе, пока мой рабочий проект компилировался, поэтому это выглядит ужасно. Отличная идея использования "start:".
PTwr

37

Ява

Прямо из XKCD

Bonding

Это бесконечная игра в ловушку между родителем и ребенком!

Цель для CHILDустановлена ​​в PARENTи цель PARENTявляется CHILD. Когда PARENTвызывает AIM, он бросает экземпляр BALLкласса, и это ловится оператором catch. Затем оператор catch вызывает, PARENT.TARGET.AIMгде находится цель CHILD. CHILDЭкземпляр делает то же самое и «бросает мяч обратно» родителю.


3
Мне нравятся эти комиксы!
Дерек 朕 會 功夫

1
Было бы лучше, если бы мяч был на самом деле брошен между родителем и ребенком. Как есть, мяч всегда брошен и пойман одним и тем же «человеком».
Ajedi32

@ Ajedi32 Может показаться, что он бросает туда-сюда; Родители TARGET - это ребенок, а целью ребенка является родитель. Цель вызвана на родителя, который ударяет мяч, и ребенок целится и бросает мяч, кий петля
Алекс Коулман

12
@AlexColeman Этот код аналогичен тому, как родитель подбрасывает мяч в воздух, ловит его, затем передает его ребенку, который делает то же самое, прежде чем передать мяч родителю, и так далее.
Ajedi32

11
Команда TARGET.AIM(B);в методе AIMявляется рекурсивным вызовом. Таким образом, это нарушает правило «функции не могут себя называть».
Теодор Норвелл

31

Баш, 3 персонажа

yes

yes будет многократно возвращать 'y' на консоль

Редактировать: Всем рекомендуется редактировать эту строку:

yes something | xargs someaction

(спасибо Оливье Дюлаку)


1
Почему это будет продолжаться? я не подвергаю сомнению это, только пытаясь выяснить почему.
Теун Пронк

2
@TeunPronk yes- это команда bash, которая выводит слово yes до тех пор, пока оно не будет уничтожено или поток не закроется. Если он пишет на экран, он никогда не остановится, пока вы его не убьете. Это своего рода обман, так как это команда, которая в основном состоит из цикла над printf.
scragar

1
Более забавным было бы использовать, yesчтобы поддерживать какой-то другой цикл.
Trlkly

3
@izkata: но тогда вы можете:: yes something | xargs someactionбез рекурсии (вы можете даже добавить -n 1 к xargs, чтобы иметь только 1 «что-то» на строку и т. д.). Использование xargs открывает путь для более сложных поведений (даже тех, которые вообще не имеют ничего общего с выводом yes)
Оливье Дюлак

4
@ scragar ты должен был только что ответить yes.
daviewales

28

C, 35 символов

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

Программа выполняется сама. Я не уверен, считается ли это рекурсией или нет.


4
@mniip Хвостовой рекурсии, то, если он применяется на уровне процесса
Изката

3
@Izkata Хвостовая рекурсия все еще является рекурсией, но это не рекурсия. Рекурсия подразумевает функцию (или процесс в этом случае), «ожидающую» завершения другой итерации. В этом случае execновый процесс заменяет исходный, поэтому нет стека вызовов, который в конечном итоге будет возвращен или переполнен.
Миллион

4
@millinon В языке, который поддерживает оптимизацию, хвостовая рекурсия заменяет предыдущий вызов в стеке вызовов, подобно тому, как execзаменяет предыдущий процесс. Это не переполнится, либо.
Изката

1
@millinon просто для того, чтобы быть супер педантичным и затянуть это обсуждение дольше, на языке программирования Scheme эта оптимизация является языковой особенностью. Это в спецификации, что если вы делаете хвостовой рекурсивный вызов, интерпретатор / компилятор должен повторно использовать последний кадр стека. Это связано с тем, что в Scheme нет встроенных циклических структур, поэтому единственный способ реализовать цикл в Scheme - выполнить хвостовую рекурсию, и это будет отстой, если вы переполните стек из-за слишком большого количества циклов :)
Орд

2
Если вам нужна педантичность, у Scheme нет «оптимизации хвостовых вызовов», у нее есть «правильные хвостовые вызовы». Это не оптимизация, это базовое требование языкового стандарта, и недопустимо указывать его недопустимо, поэтому термин «оптимизация» (или предположение, что оно имеет какое-либо отношение к рекурсии) очень обескураживающий термин.
Леушенко

28

C (со встроенными GCC - также, кажется, работает с Clang)

  • Нет явных циклов
  • Нет явных 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()встроенный, чтобы получить адрес возврата (в 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

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. Это рекурсивный тип данных, который потребляет сам себя, что, как показано в примере, допускает произвольное повторение. Во-первых, мы можем построить 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 до «Blast off!» используя шаблонное метапрограммирование.

#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 и установим его как своего родителя:

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чтобы остановить его :-)

Он использует 100,1% процессора моего Mac.

100,1% of CPU

Вы можете попробовать переместить System.outв конце основной функции, чтобы поэкспериментировать с другим забавным поведением.


лол.
Ява

Люблю взломать загрузку JVM.
Исия Медоуз

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

Это не рекурсия ... это повторение шаблонов кода. Хотя кажется, что мы вызываем один и тот же метод, среда выполнения постоянно создает новые методы. Мы используем параметр типа int, поскольку это фактически вынуждает его создавать новый тип, и каждый экземпляр метода должен содержать новый метод. Здесь нельзя поделиться кодом. В конце концов мы убиваем стек вызовов, поскольку он бесконечно ждет возврата int, который мы обещали, но никогда не доставляли. Аналогичным образом мы продолжаем писать тип, который мы создали, чтобы сделать его интересным. По сути, каждый C, который мы вызываем, представляет собой совершенно новый метод, который имеет одно и то же тело. На самом деле это невозможно в таких языках, как C ++ или D, которые делают свои шаблоны во время компиляции. Поскольку C # JIT очень ленив, он создает эти вещи только в последний момент. Таким образом,


14

Redcode 94 (Core War)

MOV 0, 1

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

Эта программа (воин) называется « Бес » и была впервые опубликована А. К. Дьюдни.


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 комбинатор требует ленивого языка, чтобы избежать бесконечного разворачивания. Разница лишь в том, что последняя его часть расширена.
ЛРН

12

JS

Не очень оригинальный, но маленький. 20 символов

setInterval(alert,1)

Вы можете фактически удалить, ,1и это все еще будет работать,
Дерек 功夫 會 功夫

@Derek 朕 會 功夫 если я сделаю это, я получу только одно предупреждение в Firefox
xem

1
В хроме это работает без последнего параметра. Код должен считаться действительным, если он работает хотя бы в одной среде.
Дерек 朕 會 功夫

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

1
@ Кори - Ну, я думаю, это действительно так!
Дерек 朕 會 功夫

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

ИМХО, лямбда-исчисление является моделью вычислений и не является языком программирования (т. Е. Без конкретной машинной модели мы не можем рассматривать лямбда-исчисление как PL).
Та Тан Динь

Вы можете создать машину, которая интерпретирует лямбда-исчисление. И синтаксис может быть использован в качестве языка программирования. См. Например, github.com/MaiaVictor/caramel
Артур Б

10

Хаскель, 24 персонажа

sequence_ (repeat (print "abc"))

или в сжатой форме, с 24 символами

sequence_$repeat$print"" 

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

объяснение: print "abc" - это, по сути, действие ввода-вывода, которое просто печатает "abc".
repeat - это функция, которая принимает значение x и возвращает бесконечный список, состоящий только из x.
sequence_ - это функция, которая принимает список действий ввода-вывода и возвращает действие ввода-вывода, которое выполняет все действия последовательно.

так что, в основном, эта программа создает бесконечный список команд печати «abc» и многократно выполняет их. без петель или рекурсии.


4
Я собирался опубликовать в основном тот же ответ в Clojure, но я думал, repeatчто будет a programming language statement which allows code to be repeatedly executed.
Seequ

3
fix(print"">>), это также не включает в себя явно названные функции повторения.
Мниип

1
@TheRare Я не знаю, как обстоят дела с замыканием, но в Haskell повтор не является «оператором языка программирования, который позволяет многократно выполнять код» - это функция, которая генерирует бесконечные списки. это цикл так же, как "int [] arr = {x, x, x};" это петля.
гордый haskeller

1
да, но что-то должно быть реализовано с помощью рекурсии, потому что без этого в принципе невозможно
гордый haskeller

3
Фактически, каждая функция, присутствующая в этом коде, определяется с помощью рекурсии - даже печати
гордый haskeller

10

ASM (x86 + I / O для 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
цифровая травма

@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: препроцессор будет включать эти файлы, которые затем будут оцениваться одновременно.
eithed

@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

В среде, где вы можете выполнять на каждом адресе и отображать ПЗУ везде, отобразите 64 КБ ПЗУ, заполненного нулями, на все адресное пространство.

Что он делает: ничего. Несколько раз.

Как это работает: процессор начнет выполнение, байт 00- это nopинструкция, поэтому он просто продолжит работу, достигнет адреса $ffff, переместится $0000и продолжит выполнение nops до тех пор, пока вы не сбросите его.

Чтобы сделать это немного более интересным, заполните память другим значением (будьте осторожны, чтобы избежать инструкций потока управления).


Вы можете заполнить память нулями и поместить туда настоящую программу.
Seequ

Таким образом, вы могли бы поместить программу размером 64 КБ, без каких-либо ветвлений, и она просто будет многократно выполняться?
Билл Вуджер,

@BillWoodger вы могли бы, особенно если у вас нет прерываний на платформе (или ни один не включен)
Гарольд

Вид удовольствия :-)
Билл Вуджер

8

Perl-регулярное выражение

(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десять раз.

Редактировать: изменил «символы» в позиции нулевой ширины, чтобы быть более точным для лучшего понимания. Смотрите ответы на этот вопрос stackoverflow .


6

T-SQL -12

print 1
GO 9

На самом деле это более странная особенность Sql Server Management Studio. 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.