循环而不“循环” [关闭]


85

几年前曾提出过类似的问题,但这个问题甚至更棘手。

挑战很简单。写一个程序(在您所选择的语言),其不使用任何重复的结构,如重复执行代码whilefordo whileforeachgoto所以对于你nitpickers,你不能用一个循环)。但是,在调用自身的函数中,不允许递归(请参见下面的定义)。那将使这一挑战变得太容易了。

对于循环中需要执行的内容没有任何限制,但是可以在答案中发布说明,以便其他人可以准确地了解正在执行的内容。

对于那些可能迷恋定义的人,此问题的循环定义为:

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

这个问题的递归定义将是您的标准递归函数定义:

A function that calls itself.

7月16日东部时间上午10点,获胜者将是投票最多的答案。祝好运!

更新:

为了平息仍在表达的困惑,这可能会有所帮助:

上述规则:

  • 不要使用循环或转到
  • 函数不能调用自己
  • 在“循环”中做任何你想做的事

如果您想实现某些东西而规则没有明确禁止这样做,请继续进行。许多答案已经违反了规则。


27
对于那些想要一个简单技巧的人,我不会为之烦恼:P仅执行2个函数,function A调用function Bfunction B调用,function A而其中1个函数执行某些操作。由于该功能不调用它本身应该根据标准是有效的^^
特恩·普龙克

2
“为了专注于创造力而改为流行竞赛”改变的问题在欺骗!
CousinCocaine 2014年

4
“递归”的定义不是很有用。最好禁止递归函数,这些函数是直接或间接引用自己的函数。
lrn 2014年

3
不清楚的是循环构造函数和递归的“定义”。两者都不是很精确。示例:rep(f){f();f();}-这是一条允许重复执行代码的语句(函数声明是某些语言的语句)。是不允许的吗?您要求提供代码以实现循环。如果该代码在语法上是一个语句,则您刚刚不允许使用它。另一个示例:f(b) { b(); g(b); }; g(b) { f(b); }。我想说的f是一个递归函数(通过与相互递归g)。不允许吗?
lrn 2014年

3
@CailinP,我要“ 挂断 ”的是,网站上的问题应该是该网站的主题:这意味着要有一个清晰,客观的规范,而这个问题不是。
彼得·泰勒

Answers:


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”和“ also”(“ ahem”,“ ahen”,“ aheo”,“ ahep”,“ aheq”, “ aher”,“ ahes”,“ ahet”,“ aheu”,“ ahev” ...)首先打印香蕉,然后调用列表中的下一个。


4
最终,它会命中已定义的“也”,因此也不会丢失。
历史学家2014年

77
这是歇斯底里的。
Michael B

4
@barrycarter:在Ruby中String#next,该method_missing函数或多或少地像在数字上加1一样被调用,除了它适用于所有字母数字字符(如果它们是字符串中唯一的字符,则可以使用非数字)。参见ruby-doc.org/core-2.1.2/String.html#method-i-next
3Doubloons,2014年

2
@NickT可以在XML构建器之类的类中使用,在其中您可以使用仅由创建的标记b.my_tag。它也用于ActiveRecord模型或中OpenStruct。他在“ Wat talk”中说,全球性method_missing是不好的,但范围很广。
Hauleth 2014年

2
我记得对另一个红宝石程序的旧评论:“我喜欢它,因为它含有甲基
苯丙氨酸

82

Python-16

或任何其他具有eval的语言。

exec"print 1;"*9

你能描述一下你的程序做什么吗?
CailinP 2014年

10
它需要一个字符串("print 1;"),将其复制9次(*9),然后执行结果字符串(exec)。重复执行一小段代码,而实际上并没有循环。
scragar

12
是的,用于字符串乘法!
塔娜·布里姆霍尔

2
如果你改变了也能在红宝石execeval printecho
Ajedi32

80

尖锐的

我已经将代码扩展为更具可读性的方式,因为这不再是代码编程,并添加了一个增量计数器,以便人们可以实际看到此程序正在执行某些操作。

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

(请永远不要这样做)。

在开始时,我们创建P该类的新实例,当程序尝试退出时,将调用GC,该GC调用终结器,该终结器创建P该类的新实例,当其尝试清理时,该实例将创建一个调用终结器的新实例P。 。

该程序最终死亡。

编辑:莫名其妙地,这只在死亡前运行了大约45,000次。我不太清楚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 2014年

4
@TheRare可以,但是它们本质上是不确定的,并且在程序执行期间可能永远不会被调用。它们被实现为虚拟方法的替代,Finalize因此有时被称为finalizer。在实际的C#中,应该使用IDisposable模式。
Michael B

似乎在某个时刻发生了超时。我不认为是GC停止了您的周期,而是由操作系统决定您的程序花费太长时间才能结束。
LVBen 2014年

我认为这实际上是决定杀死程序(不一定是操作系统)的运行时。在程序结束时调用的垃圾回收器线程在被杀死之前被赋予约2秒的固定时间限制。
Michael B

经过一些小的修改(不让程序结束,将第一个P对象释放给GC,并反复调用GC.Collect),我可以使它无限期地运行。
LVBen 2014年

53

Befunge

.

好的旧的Befunge几乎永远输出0(从空堆栈中输出),因为线绕起来了。


1
哈!我喜欢这样的把戏
CailinP 2014年

47

JS

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

功能有趣!

一个函数,该函数创建与自身具有相同主体的另一个函数,然后运行它。

当达到堆栈限制并且整个对象折叠时,它将在最后显示hi。

免责声明:在达到堆栈限制之前,您将无法在浏览器中执行任何操作。


还有一个,更多的邪恶

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

它创建一个函数,该函数会打开一个窗口,然后在该窗口中创建该函数的副本,然后再运行它。

免责声明:如果您允许打开弹出窗口,那么完成此操作的唯一方法是重新启动计算机


5
这肯定是非常邪恶的;)
CailinP 2014年

28
@CailinP当然很不错。
seequ 2014年

我认为您在f第二个功能的末尾缺少。应该是}f()最后。
Chirag Bhatia-chirag64

2
不幸的是,我注意到了它,因为我尝试了它。:P
Chirag Bhatia-chirag64

7
-1-这只是递归。
immibis 2014年

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在返回之前在堆栈上的入口点地址,从而生成一个无限循环。


我想知道在组装中是否可行。
伊恩·斯科特

2
堆乱的后果自负。这是龙;-)
数字创伤

1
我本来打算将codegolf.stackexchange.com/a/34295/11259称为此副本,但实际上这是较早的答案
Digital Trauma 2014年

@DigitalTrauma:是的,我在张贴自己的作品后注意到了,但我仍然贴着Mim女士的照片。:-)幸运的是存在一些差异(他的语言更加模糊并且可以在32位Linux上运行,我的语言可以直接在DOS上播放并且没有其他跳转),如果没有人反对,我还是将其保留在这里。
Matteo Italia

@MatteoItalia不会混淆,只是cr脚;)(“添加eax,4”也使我感到困惑,我找不到操作码大小表,所以我只是猜测大小)。我在工作项目进行编译时使用在线编译器进行编译,因此看起来非常糟糕。使用“开始:”的好主意。
PTwr

37

爪哇

直接从XKCD

粘接

这是父母与孩子之间永无止境的捉迷藏游戏!

的目标CHILD设置为PARENT,目标PARENTCHILD。当PARENT调用时AIM,它将引发BALL该类的实例,并由catch语句捕获。然后catch语句调用PARENT.TARGET.AIM目标为的位置CHILD。该CHILD实例确实相同,“丢球后”的父母。


3
我喜欢那些漫画!
德里克·朕会功夫2014年

1
如果球实际上是在父母与孩子之间扔的,那会更好。照原样,球总是由同一个人“投掷”并接住。
Ajedi32

@ Ajedi32实际上看起来确实确实来回扔了它;父母TARGET是孩子,孩子的目标是父母。艾姆(Aim)被召唤给父母,后者使球跳动,让孩子瞄准并扔球,提示循环
亚历克斯·科尔曼

12
@AlexColeman此代码类似于父母将球高高地扔到空中,接住球,然后将球交给做相同操作的孩子,然后再将球交给父母,依此类推。
Ajedi32

11
TARGET.AIM(B);方法中的命令AIM是递归调用。因此,这违反了“功能不能自行调用”规则。
西奥多·诺维

31

重击,3个字符

yes

是,将反复向控制台返回“ y”

编辑:鼓励所有人编辑此行:

yes something | xargs someaction

(感谢Olivier Dulac)


1
为什么会继续运行?我没有质疑它只是想找出原因。
Teun Pronk

2
@TeunPronk yes是一个bash命令,它会打印出“是”一词,直到其被杀死或流关闭为止。如果它正在写入屏幕,它将永远不会停止,直到您将其杀死。不过这有点作弊,因为它是一个基本上由printf循环组成的命令。
scragar's

1
更有趣的是可以用来yes保持其他循环。
2014年

3
@izkata:但是您可以::yes something | xargs someaction无需递归(您甚至可以在xargs中添加-n 1以使每行只有1个“东西”,等等)。使用xargs为更复杂的行为(甚至与yes输出完全无关的行为)开辟了道路
Olivier Dulac 2014年

4
@scragar您应该刚刚回答了yes
daviewales 2014年

28

C,35个字符

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

程序自行执行。我不确定这是否被视为递归。


4
@mniip尾递归,那么,如果它在流程级别应用
Izkata 2014年

3
@Izkata Tail递归仍然是递归,但这不是递归。递归意味着一个函数(在这种情况下为过程)“等待”自身的另一次迭代终止。在这种情况下,exec导致新进程替换原来的进程,因此没有最终返回或溢出的调用堆栈。
millinon

4
@millinon在支持优化的语言中,尾部递归替换了调用堆栈中的上一个调用,类似于exec替换前一个过程的方式。它也不会溢出。
Izkata 2014年

1
@millinon只是为了超级学究,并且为了使讨论不再冗长,在Scheme编程语言中,此优化语言功能。在规范中,如果您进行尾递归调用,则解释器/编译器必须重用最后一个堆栈帧。这是因为Scheme没有内置的循环结构,因此在Scheme中实现循环的唯一方法是执行尾递归,如果尝试循环太多而导致堆栈溢出,那会很烂:)
Ord 2014年

2
如果您想要修脚,Scheme没有“尾部呼叫优化”,而是“适当的尾部呼叫”。这不是一种优化,它是语言标准的基本要求,不允许不提供它,因此“优化”(或与递归有关的建议)在很大程度上是不鼓励使用的。
Leushenko

28

C(带有GCC内置函数-似乎也可以与clang一起使用)

  • 没有明确的循环
  • 没有明确的提示
  • 没有递归
  • 只是把老式的东西弄乱了(孩子们,不要在没有监督的情况下在家尝试):
#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()内建函数获取将返回的返回地址(in 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()。这些不在c标准中,但是在标准库中。不过我今天还是想手动修改堆栈;-)
Digital Trauma

@BrianMinton我的猜测是因为它在CPU规格中,这使其(硬件)与平台有关。当自动生成stackframe时使用它是相当危险的,如果AV会为这样的代码而哭泣,我也不会感到惊讶。检查还是这个针对x86版本的汇编。
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调用just ycputStrLnand >>,这不是递归的。

说明:诀窍在于递归数据类型Strange。它是一种消耗自己的递归数据类型,如示例中所示,它允许任意重复。首先,我们可以构造extract x,它本质上x x在无类型的lambda演算中表示自我应用。并且这允许构造定义为的Y组合器λf.(λx.f(xx))(λx.f(xx))


更新:按照建议,我发布了一个变体,它更接近于无类型lambda演算中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组合器的数据类型为负数。这就是为什么所有语言要求重复出现的类型都出现在箭头的右边,并且为什么不允许玫瑰树。好东西!我在这里注册一个帐户只是为了支持这个!
2014年

您可以删除let绑定并定义yc f = extract $ C $ f.extract,因为let绑定可以说是一种允许递归的语言功能(经典let x = x in x)。这也减少了一些字符:)
Earth Engine

甚至是yc = extract . C . (.extract)
Earth Engine

@EarthEngine是的,我只是想使结构更接近的原始定义Y
彼得·普德拉克(PetrPudlák)2014年

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直接生成而无需中间形式。
Leushenko

@Leushenko是的,我知道。但是模板示例示例又做了同样的事情-在编译时使用图灵完备的语言中的递归函数-唯一的区别是constexpr使用的是一种看起来更像C ++的语言。与所有答案一样,这个人违反了规则,我很诚实。constexpr是专门为使模板元编程(某些方面)过时而设计的,因此在该主题的帖子中似乎绝对值得一提。
纳撒尼尔(Nathaniel)2014年

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

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来停止它:-)

它使用Mac的100,1%的CPU。

100,1%的CPU

您可以尝试System.out在主要功能的末尾移动,以尝试其他有趣的行为。


大声笑。使Java卡在自身中:)
masterX244

喜欢JVM递归加载技巧。
伊赛亚·梅多斯

20

尖锐的

同样邪恶的一个:

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(核心战争)

MOV 0, 1

将指令从地址0复制到地址1。因为在《核心战争》中,所有地址都相对于当前的PC地址,并且与核心的大小取模,所以这是一条非跳转指令中的无限循环。

该程序(战士)称为“ 小鬼 ”,最早由AK Dewdney发布。


3
小鬼将行军,准备好你的大门,准备好它们,否则你会被压碎。
seequ 2014年

准备好了SPL 0, 0; MOV 1, -2
wberry

很好,我希望尚未发布。+1
mbomb007'9

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组合器要求使用惰性语言以避免无限展开。唯一的区别是它的后半部分是eta展开的。
lrn 2014年

12

JS

不是很原始,但是很小。20个字符。

setInterval(alert,1)

您实际上可以删除,1,并且仍然可以使用,
Derek朕会功夫2014年

@Derek朕会功夫,如果我这样做的话,我只会在Firefox上收到一个警报
Xem 2014年

1
在chrome中,它不需要最后一个参数即可工作。如果该代码至少在一种环境中有效,则应将其视为有效。
德里克·朕会功夫2014年

3
@Derek朕会功夫setInterval不是一个声明;这只是一个功能。它用于表达式语句中,如果我们不能使用表达式语句,那么我什至不知道。
Keen 2014年

1
@Cory-好吧,我想那是有效的!
德里克·朕会功夫2014年

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强大功能的绝佳时机,其中“代码就是数据,数据就是代码”。当然,这些示例效率很低,因此切勿在实际环境中使用。

宏生成的代码是假定循环的展开版本,并且所生成的代码是在运行时评估的。

repeat-it:允许您循环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它允许您指定将绑定到循环索引的符号。它使用扩展时间符号来确保index变量在每个循环开始时都已正确设置,无论您是否在循环体内修改了它的值。

(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. 每次迭代开始时总是正确设置index变量

  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 2014年

恕我直言,lambda演算是一种计算模型,而不是编程语言(即,如果没有具体的机器模型,我们不能将lambda演算视为PL)。
塔廷丁

您绝对可以构建一台解释lambda演算的机器。并且该语法可以用作编程语言。参见例如github.com/MaiaVictor/caramel
Arthur B

10

Haskell,24个字符

sequence_ (repeat (print "abc"))

或简写形式(包含24个字符)

sequence_$repeat$print"" 

(尽管文本已更改,但仍会循环-这将无限打印两个引号和一个换行符)

说明:print“ abc”本质上是一个仅打印“ abc”的I / O操作。
repeat是一个函数,它使用值x并返回仅由x组成的无限列表。
sequence_是一个函数,它接收I / O动作的列表,并返回按顺序执行所有动作的I / O动作。

因此,从根本上说,该程序制作了打印“ abc”命令的无限列表,然后重复执行它们。没有循环或递归。


4
我打算在Clojure中发布基本相同的答案,但我认为repeat应该是a programming language statement which allows code to be repeatedly executed
Seequ 2014年

3
fix(print"">>),这也不涉及任何明确命名的重复功能。
mniip 2014年

1
@TheRare我不知道闭包是怎么回事,但是在Haskell中重复不是“允许重复执行代码的编程语言语句”-它是生成无限列表的函数。就像“ int [] arr = {x,x,x};”一样,这是一个循环。是一个循环。
自豪的haskeller 2014年

1
是的,但是必须使用递归来实现,因为没有递归,这基本上是不可能的
骄傲的haskeller 2014年

3
实际上,此代码中存在的每个函数都是使用递归定义的-甚至打印
骄傲的haskeller 2014年

10

ASM(用于Linux的x86 + I / O)

您微不足道的高级语言会费劲多少都无所谓,它仍然仅仅是隐藏的指令指针操作。最后,它将是某种“ goto”(jmp),除非您感到无聊足以在运行时展开循环。

您可以在Ideone上测试代码

您还可以在Matteo Italia 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 2014年

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');
?>

达恩,这也算作递归,不是吗?
Pichan 2014年

不要以为是-@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)

打印“ Hello world!” 给定的次数。

说明:函数z实现了严格的Z定点组合器,该组合器(未递归定义)允许表达任何递归算法。


我会称其为g间接递归。
seequ 2014年

@TheRare为什么?你在说什么 再次g调用的调用是什么g?当然,诀窍是自我应用g(g),但不涉及递归。g如果您还没有看到,您真的会调用间接递归g(g)吗?这是在不允许递归定义的语言(例如lambda演算)中执行此操作的标准方法。
PetrPudlák2014年

您给出g参数x,然后调用x(x)
seequ 2014年

2
@TheRare函数(或一组函数)的使用方式不是递归的也不是非递归的,这仅由其定义确定。
PetrPudlák2014年

1
所有的答案作弊在这种或那种方式:总有递归或循环的地方,如果不回答,然后在代码中调用答案。我喜欢这个作弊的方式。
韦恩·康拉德

8

z80机器码

在可以在每个地址执行并且将ROM映射到任何地方的环境中,将填充零的ROM 64kb映射到整个地址空间。

它的作用:什么都没有。反复。

工作原理:处理器将开始执行,字节00是一条nop指令,因此它将继续执行,到达地址$ffff,环绕至$0000,然后继续执行nops,直到将其重置为止。

为了使其更具趣味性,请使用其他值填充内存(请注意避免控制流指令)。


您可以用零填充内存,然后在其中的某个位置放置一个实际程序。
seequ 2014年

因此,您可以放入一个没有分支的64k程序,它将重复执行?
比尔·伍德格

@BillWoodger您可以,尤其是如果您在平台上没有中断(或没有启用中断)的话
harold 2014年

有点乐趣:-)
比尔·伍德格

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 2014年

是的,我在剧透评论中补充了这一点。我认为使用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时间。它只是在大量的调用堆栈后面爆炸。
Michael B

怎么不算?没有循环,也没有递归。
LVBen 2014年

1
另外,除非您认为32个高的调用量很大,否则调用堆栈就不会很大。
LVBen 2014年

1
为什么只有32次而不是4294967296次?
LVBen 2014年

4
@ ja72如果我曾经在一个无法使用循环或递归的开放源代码项目中工作,那么我将完全贡献这样的代码!
LVBen 2014年

6

JS(在浏览器中)

这个怎么样?

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

打印当前时间并重新加载页面。


哦,开枪。我刚刚发布了具有相同基本概念的答案。我一直在页面上扫描“ JavaScript”或任何显示HTML标记的东西。我想我可能会留下答案,只是因为它处理了位置包含“#”的特殊情况。无论如何,+ 1。
Keen 2014年

在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 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.