图灵完整性的最少(不同)字符


107

摘要:

对于任何给定的语言,要成为图灵完整的,最少的唯一字符数是多少?

挑战:

对于您选择的任何语言,请找到使您的语言成为图灵全集的最小字符子集。您可以根据需要多次重复使用字符集。


例子:

  • JavaScript:+!()[]http://www.jsfuck.com

  • Brainfuck :(+<>[]假定包装单元的大小)

  • Python 2 :(()+1cehrx由类似的脚本制作exec(chr(1+1+1)+chr(1))

得分:

此挑战以字符而不是字节计分。例如,示例的分数是6、5和9。


笔记:

  • 在您仅使您的语言成为图灵全集的意义上,此挑战与其他挑战有所区别(不一定能够使用该语言的所有功能。)

  • 尽管可以,但请不要在不减少使用字符的情况下发布答案。示例:8个字符的Brainfuck(因为默认情况下,每个其他字符都是注释。)

  • 您必须至少提供关于您的子集为何是图灵完成的简短说明。


90
一元,1个字符。感叹
Dennis

4
@Dennis与Jelly或05AB1E没什么不同,它内置了一个有趣的数论问题。对于任何一种并非旨在解决问题的语言,此挑战似乎仍然是一个有趣且重要的优化问题。
马丁·恩德

7
@MartinEnder我会特别感兴趣,看到像Java或C语言的答案
朱利安Lachniet

9
请不要在解决方案是语言中每个有效字符的esolangs中发布解决方案。它不是有趣或聪明的。
帕维尔

20
@Pavel不有趣或不聪明可能意味着它不应该被投票,但是肯定不是应该不被发布。
丹尼斯

Answers:


77

Haskell,4个字符

()=;

随着()=我们能够确定S,K和I必须由分开的定义;或NL。

我们定义(==)为S(第二行显示了更易读的版本):

((=====)==(======))(=======)=(=====)(=======)((======)(=======))
( a     == b      ) c       = a      c       ( b       c       )

(===) 如K:

(=====)===(======)=(=====)
 a     === b      = a

(====)我一样:

(====)(=====)=(=====)
(====) a     = a 

幸运的是(==)(===)(====)等都是有效的功能/参数名称。

正如@ ais523所指出的那样,在像Haskell这样的强类型语言中,SKI是不够的,因此我们需要添加一个fixpoint组合器(=====)

(=====)(======)=(======)((=====)(======))
(=====) f      = f      ((=====) f      )

17
这种结构不能直接工作;像Haskell这样的强类型语言,SKI并不是图灵完整的。但是,我相信您可以使用相同的技术来定义,即使使用强类型语言fix,SKI + fix 也可以完成Turing。

哦,所以您在每个程序的开头都添加了这些定义?
PyRulez '17

@PyRulez:是的。根据我们的默认设置,我假设能够使用给定的字符集构造函数就足够了-不需要完整的程序。
NIMI

1
您可能应该替换(==)

@proudhaskeller:是的,如果您确实要编程,最好重命名(==),但是上面的代码只是证明完整性的证明。
nimi

61

JavaScript(ES6),5个字符

感谢@ETHproductions和@ATaco的帮助;这是一个小组项目,尽管最初的想法是我的,但许多细节都是他们的。请参阅此处讨论此JavaScript子集的聊天讨论。

[]+=`

众所周知,任何Javascript程序都可以使用字符([]()+!)编写,但是5个字符是不够的。但是,这对于编写任意JavaScript来说不是挑战。编写图灵完备的语言并利用图灵完备的语言不需要访问DOM甚至不需要交互I / O的事实,这是一个挑战,事实证明我们可以编写具有所需功能的程序,即使没有任何能力也无法运行eval

基本自举

JavaScript 在类型方面非常灵活。因此,例如,[]是一个空数组,但+[]为0,并且[]+[]为空字符串。值得注意的是,我们可以使用此字符集产生0的事实使得可以模拟括号的分组效果;(a)可以写成[a][+[]]。我们可以使用这种技巧来仅使用以下命令生成各种字符+[]

  • [][+[]]undefined(是空数组的第一个元素);所以
  • []+[][+[]]"undefined"(的字符串化undefined);所以
  • [[]+[][+[]]]["undefined"](将其包装在数组中);所以
  • [[]+[][+[]]][+[]]"undefined"(第一个元素);所以
  • [[]+[][+[]]][+[]][+[]]"u"(第一个字母)。

u是最容易创建的字符之一,但是类似的技术使我们可以创建一系列其他字符。在同一条链路之前给我们下面配有残疾人专用字符的列表+[](这是相同的列表为+[](),排除,因为它是用括号超过分组/优先级以外的目的只有建设):

0123456789acdefinotuvyIN (){}.

我们不能从这组字符中拼出很多有用的单词(请记住,这是我们可以作为字符串产生的一组字符;没有某种方法,我们就无法执行它们eval)。因此,我们需要另一个角色。我们将使用=,因为稍后会用到它,但是目前,我们将使用它来拼写比较运算符==。这样就可以产生falsetrue,并且可以将其字符串化和建立索引,并立即让我们添加lrs可以包含在字符串中的字符。

到目前为止,这让我们可以拼写的最重要的单词是constructor。现在,JavaScript具有如下属性访问语法:

object.property

但您也可以这样写:

object["property"]

并没有阻止我们使用计算属性,而不是字符串文字。因此,我们可以按照

object["c"+"o"+"n"+"s"+"t"+"r"+"u"+"c"+"t"+"o"+"r"]

(使用如上所述生成的字母;代码很快变得长!);这等效于object.constructor,它使我们可以访问任意对象的构造函数。

我们可以使用几种技巧。从平凡到梦幻:

  • 对象的构造函数是一个函数。值得注意的是,它有一个名称,该名称是函数字符串化的一部分。因此,例如,我们可以[[]+[]][+[]]["constructor"]获取名称为String的字符串的构造函数,然后对其进行字符串化以达到大写S字符。这会稍微扩展我们的字母,稍后我们将需要一些新字符。
  • 所有数组都具有相同的构造函数。[]["constructor"] == []["constructor"]true(不同于[] == [],这是错误的)。这看似微不足道,但这非常重要,因为它为我们提供了一种持久存储值的方法。我们可以在构造函数上设置一个随机属性,然后再读回去。(这是我们正在使用的原因之一=,特别,而不是的其他方式来生成一个truefalse。)例如,我们可以评估[[]["constructor"]["a"]=[]],以及后来的阅读[]["constructor"]["a"],回到我们那里存储在同一个阵列。

    这满足了我们对图灵完备性(存储和检索任意数量的数据的能力)所需的要求之一。我们可以使用一个由两个元素组成的数组创建一个con单元,从构造函数属性存储中获取值,然后将其存储回去代替那些值之一,从而使我们能够在内存中建立任意大的数据结构;我们可以通过相反的操作来访问它的存储空间,将它逐段拆散,直到我们想要的数据变得可访问为止。读取具有破坏性,但是可以接受,因为我们有多个地方可以存储数据,因此我们可以在读取数据时将其复制,然后将副本放回原始位置。

  • 它使我们能够获取函数的构造函数(可以使用有限的字母访问很多函数;[]["find"]即Array.find是最易于访问的函数,但还有其他函数)。为什么这样有用?好吧,我们实际上可以将其用于构造函数的预期目的,并构造函数!不幸的是,对于我们的字符集,我们无法将函数的构造函数传递给计算的字符串。但是,使用`do可以让我们为它传递字符串文字(例如[]["find"]["constructor"]`function body goes here`);这意味着我们可以在执行时定义具有任何行为的函数类型的自定义值,只要我们可以使用完全表达该行为即可[]+=。例如,[]["find"]["constructor"]`[]+[]`是一个相当无聊的函数,它计算空字符串,将其丢弃并退出;函数没有用,但是更复杂的函数会有用。请注意,尽管函数不能接受参数或返回值,但实际上这不是问题,因为我们可以使用构造函数属性存储从一个函数与另一个函数进行通信。另一个限制是我们不能`在函数体内使用。

    现在,我们可以定义自定义函数了,但是现在让我们受阻的是调用它们时遇到的困难。在程序的顶层,我们可以使用调用函数``,但是只能从顶层调用函数并不能进行任何循环。相反,我们需要函数能够相互调用。

    我们通过一个相当巧妙的技巧来完成此任务。还记得S我们之前产生的资本吗?那让我们拼写"toString"。我们不会称呼它。我们可以通过添加[]将它们转换为字符串。相反,我们将替换它。我们可以使用构造函数存储来定义持久存储的数组。然后,我们可以将创建的函数分配给数组的toString方法,这些分配也将保留下来。现在,我们所要做的就是+[]在数组上创建一个简单的数组,突然,我们的自定义函数将运行。这意味着我们可以使用字符+=[]调用函数,因此我们的函数可以相互调用,也可以自己调用。这给了我们递归,给了我们循环,突然之间我们拥有了图灵完备性所需的一切。

这是一组提供图灵完备性及其实现方式的功能的简要介绍:

  • 无限存储:构造函数存储中的嵌套数组
  • 控制流:使用if和递归实现:
    • if:将布尔值转换为数字,并将索引转换为2元素数组;一个元素then在字符串化的情况下运行该函数,另一个元素else在字符串化的情况下运行该函数
    • 递归:对构造函数存储的适当元素进行字符串化
  • 命令排序[a]+[b]+[c]评估abc从左至右(至少在我检查过的浏览器上)

不幸的是,这是不切实际的。它不仅是巨大的大型给出的字符串必须构建字符一个字符从第一原理,它也没有办法做I / O(这是不是是图灵完备的需要)。但是,如果终止,则至少以后可以手动查看构造函数存储,因此调试程序不是没有可能,并且它们也不是完全不通信的。


16
如果未命名,建议使用J5h * t。
CalculatorFeline

1
一个好的示例程序是什么?主要测试?你好,世界?
CalculatorFeline

3
我的,这真是太棒了 ……美味的答案,就像一部恐怖电影一样。
停止了转动

4
我认为Angular1 toString()对依赖项注入的使用是使用该函数的最富创造性的方式。现在我改变了主意。
阳光明媚的双关

1
这是一个示例:pastebin.com/QGbjmB5Y
Esolanging Fruit

55

一元一字符

0

角色的选择并不重要。程序的长度定义了其转存到的Brainfuck程序。虽然规范要求使用0字符,但大多数编译器似乎都没有对此进行检查。


44
我们可能应该通过验证规范的编译器来公开问题,这是一个非常严重的问题。
曼队长

5
我很震惊 我需要20分钟的时间来讲这是否是个玩笑。
彼得·施耐德

3
@ PeterA.Schneider某些谷歌搜索人员会发现有人实际上在理论上以这种方式实现了一个quine,尽管最终的0字符串可能是我在任何实际情况下见过的最大数字,并且永远无法在真实机器上实现。
达伦·林格

12
那串零实际上是我在任何情况下见过的最小的数字。
马修(Matthew)阅读了

1
大声笑,好吧,如果您做一些愚蠢的事情,例如将唯一的符号定义为加性标识...:p
Darren Ringer

37

vim,9 8 7 6个字符

<C-v><esc>1@ad

我们可以按照以下步骤构建和执行任意vimscript程序:

  1. 使用该序列aa<C-v><C-v>1<esc>dd@1<esc>dddd获取输入<C-a>寄存器1

  2. 使用进入插入模式a,然后插入a,稍后将用于在宏中重新进入插入模式。

  3. 对于所需的vimscript程序中的每个字符,

    1. 用于<C-v><C-v>1<esc>插入文字序列<C-v>1

    2. 不断增加使用次数@1(由于位于最后一行<C-a><cr>,所以最后一次<cr>是空操作),1直到达到所需字符的ASCII值为止,

    3. 并使用再次进入插入模式a

  4. 使用将行(以及结尾的换行符)删除到1寄存器中<esc>dd

  5. 通过使用vim击键来执行结果@1,然后<esc>dd从上一步中删除尾随换行符输入的行。

  6. 使用运行结果的任意字节序列dd@1。如果以a开头:,它将被解释为vimscript代码,并且由于尾随换行符而将其运行dd

我不认为这是最小的字符集,但是很容易证明它是图灵完备的。


2
您不能i<C-v>1<ESC>先写<C-a>然后再dd使用@1,以便您可以递增任何数字并导致不必使用<C-a>吗?
牛嘎嘎声

4
哇,这个答案真是不可思议!+1!
DJMcMayhem

@KritixiLithos经过一点改组后,它的确可以工作,谢谢!
Doorknob

2
@ mbomb007实际上...由于一个有趣的实现细节,<C-v>10插入一个NUL而不是\ n(不要问)。是的,无论如何,图灵完备性都无关紧要。
门把手


33

Perl,5个字符

<>^es

与其他脚本语言一样,该想法是针对eval任意字符串。但是,我们的字符集不包括引号或串联运算符,因此构造任意字符串将变得更加复杂。注意,这eval^"将更容易处理,但又具有一个特征。

我们的主要工具是s<^><CODE>ee,先评估CODE,然后评估其输出。e可以添加更多,达到预期效果。

我们使用来获取字符串<>,这是glob运算符,除非不是。第一个字符不能<(否则看起来像<<运算符),尖括号必须平衡,并且必须至少有一个非字母字符(否则将其解释为readline运算符)。

通过将这些字符串异或在一起,^B^V^S(*-/9;<>HJMOY[`\^begqstv只要我们接受周围有一些垃圾(其中的前三个是控制字符),就可以从中获得任何字符组合。

例如,假设我们要获取"v99"。一种获取方法v99"><<" ^ "e>>" ^ "see" ^ "^^^",但是由于对的限制,我们无法表示这些字符串<>。因此,我们使用:

<^<<^>><>>^<^^^^^<>>^<^^^^^^e>^<^^^^^^^>^<^^^^^e>^<^^^^e^>^<e^^es>^<^ee^^>^<^<^^^^^>>^<^<>^^^^>^<^^^^^^^e>^<^^^^^^^^>

上面的yi​​eld Y9;v99;,当评估时,产生的结果与纯v99文本(即ASCII码为99的字符)相同。

因此,我们可以使用整个^B^V^S(*-/9;<>HJMOY[`\^begqstv字符集生成任意字符串,然后将其如上所述转换并粘贴到a中s<><CODE>eeee以执行它。不幸的是,这个字符集仍然非常有限,没有任何明显的方式来执行串联。

但幸运的是,它包含了星星。这可以让我们写*b,其结果为字符串"*main::b"。然后,*b^<^B[MMH^V^SY>(^ B,^ V和^ S是文字控制字符)给我们(6, $&);,当再次求值时,它返回Perl的match变量的值$&。这使我们可以使用有限的级联形式:我们可以反复在之前$_使用s<^><THINGS>e,然后使用s<\H*><*b^<^B[MMH^V^SY>>eee进行评估$_\H匹配除水平空白之外的任何内容;我们使用它代替字符集中的点而不是点)。

使用9-/,我们可以轻松生成所有数字。使用数字,v和并置,我们可以生成任意字符(vXXX产生ASCII码为XXX的字符)。而且我们可以将它们串联起来,以便生成任意字符串。看来我们可以做任何事情。

让我们写一个完整的例子。假设我们想要一个打印自己的PID的程序。我们从自然程序开始:

say$$

我们将其转换为v符号:

s<><v115.v97.v121.v36.v36>ee

我们仅使用重写它^B^V^S(*-/9;<>HJMOY[`\^begqstv(空格仅出于可读性,不影响输出):

s<^><
    s<^><9*9-9-9-9-9-9>e;
    s<^><v>;
    s<v\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><9*9-9-9-9-9-9>e;
    s<^><v>;
    s<v\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><99-99/-9-99/-9>e;
    s<^><v>;
    s<v\H\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><99-9/9-9/9>e;
    s<^><v>;
    s<v\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><999/9-9/-9-9/-9-9/-9-9/-9>e;
    s<^><v>;
    s<v\H\H\H><*b^<^B[MMH^V^SY>>eee;
    s<\H*><*b^<^B[MMH^V^SY>>eee;
>eee;

最后,我们将上述程序转换为only <>^espastebin。可悲的是,这使Perl崩溃Excessively long <> operator,但这只是技术限制,不应考虑在内。

ew,那是一段漫长的旅程。看到有人提出了一组5个字符来简化事情,这真的很有趣。

编辑:通过使用略有不同的方法,我们可以避免达到上的长度限制<>。只能使用全功能的Brainfuck解释器<>^es在线尝试!。自动Perl <>^es转译器:pastebin


1
我明白了。您的编码出现了二次爆炸,因为您的字符分为两组,一组只能通过对偶数个基本字符进行异或来生成,而另一组只能通过奇数来生成,从而使您不得不在它们之间进行更改时,添加另一个glob。您是否有机会将程序分成与^其他字符或其他基本字符捆绑在一起的可评估的较短部分?
与Orjan约翰森

@ØrjanJohansen是,很好,发现了这一点。我正在研究一个解决方案。
Grimy

您可以将缩小的示例作为TIO链接进行在线尝试!
与Orjan约翰森

7
请求:解释这种“略有不同的方法”
CalculatorFeline

32

Python 2、7个字符

exc="%\n

可以使用这7个字符(\n是换行符)对任何Python 2程序进行编码。

构造任意字符串

我们可以通过%在单个字符串上重复应用替换运算符来执行串联。例如,如果a=1, b=2, c=3"%d%%d%%%%d" % a % b % c将给我们字符串"123"。幸运的是,中的字母exec使我们可以访问%x%c,它们基本上是hex()chr()。使用%c,我们可以构造任何字符串,只要我们具有代表字符的必要数字即可。然后,我们可以使用exec关键字将此字符串作为python代码执行。

拨打号码

我们可以去访问0,并1马上用比较蝙蝠(==)。通过组合数字和模,可以构造以ASCII 43表示的数字+。这使我们能够构造代码所需的数字。

把它放在一起

我在此解释中省略了一些细节,因为它们对于理解如何编写受这些约束的程序不是必不可少的。以下是我编写的Python 2程序,该程序将任何python程序转换为仅使用这7个字符的功能等效的版本。所使用的技术是由灵感提交关于无政府主义的高尔夫球用k。还可以使用一些简单的技巧来将生成的程序的大小保持在合理的范围内。

import sys

var = {
    43: 'e',
    'prog': 'x', # the program will be stored in this variable
    'template': 'c',
    0: 'ee',
    1: 'ex',
    2: 'ec',
    4: 'xe',
    8: 'xx',
    16: 'xc',
    32: 'ce',
    64: 'cc',
    'data': 'cx', # source program will be encoded here
}

unpacker = 'exec"".join(chr(eval(c))for c in {}.split())'.format(var['data'])

source = sys.stdin.read()
charset = sorted(list(set(source+unpacker)))
codepoints = map(ord, charset)

output = (
    # create template for joining multiple characters
    '{}="%c%%c%%%%c%%%%%%%%c"\n'.format(var['template']) +

    # create 1
    '{0}={1}=={1}\n'.format(var[1], var['template']) +

    # create 0
    '{}={}==""\n'.format(var[0], var['template']) +

    # create 3
    # store it at var[43] temporarily
    (
        'exec"{0}=%x%%x"%{2}%{2}\n' +
        'exec"{0}%%%%%%%%=%x%%x%%%%x"%{1}%{2}%{1}\n'
    ).format(var[43], var[0], var[1]) +

    # create 4
    # this step overwrites the value stored at var[0]
    (
        'exec"{1}=%x%%x"%{0}%{1}\n' +
        'exec"{1}%%%%=%x%%x"%{2}%{0}\n'
    ).format(var[43], var[0], var[1]) +

    # create 43
    'exec"{0}=%x%%x"%{1}%{0}\n'.format(var[43], var[0])
)

# create powers of 2
for i in [2, 4, 8, 16, 32, 64]:
    output += 'exec"{0}={1}%c{1}"%{2}\n'.format(var[i], var[i/2], var[43])

for i, c in enumerate(codepoints):
    # skip if already aliased
    if c in var:
        continue

    # generate a new name for this variable
    var_name = ''
    if i < 27:
        for _ in range(3):
            var_name += 'exc'[i%3]
            i /= 3
    else:
        i -= 27
        for _ in range(4):
            var_name += 'exc'[i%3]
            i /= 3
    var[c] = var_name

    # decompose code point into powers of two
    rem = c
    pows = []
    while rem:
        pows.append(rem&-rem)
        rem -= rem&-rem

    # define this variable
    front = 'exec"{}={}'.format(var[c], var[pows.pop()])
    back = '"'
    for i, p in enumerate(pows):
        front += '%'*(2**i) + 'c' + var[p]
        back += '%' + var[43]
    output += front + back + '\n'

# initialise the unpacker
output += 'exec"""{}=""\n"""\n'.format(var['prog'])
i = 0
length = len(unpacker)
while i < length:
    if (length-i) % 4 == 0:
        # concat 4 characters at a time
        w, x, y, z = [var[ord(unpacker[i+j])] for j in range(4)]
        output += 'exec"{}%c={}%%{}%%{}%%{}%%{}"%{}\n'.format(var['prog'], 
                    var['template'], w, x, y, z, var[43])
        i += 4
    else:
        output += 'exec"""{}%c="%%c"%%{}"""%{}\n'.format(var['prog'],
                    var[ord(unpacker[i])], var[43])
        i += 1

# encode source data
output += var['data'] + '="""'
output += '\n'.join(var[ord(c)] for c in source)
output += '"""\n'

# execute the program
output += 'exec"exec%c{}"%{}'.format(var['prog'], var[32])

print output

在线尝试


您可以添加一些检查,以查看输入程序是否已被限制为必需的字符集,如果是,则为cat。
mbomb007 '18

26

Mathematica,5个 4个字符

I[]

私有的Unicode字符,用作操作符Function,可让您使用命名参数为未命名函数编写文字。角色看起来很像Mathematica中的角色,因此为了清楚起见,我将在其余答案中使用该角色。

通过这些,我们可以实现的SKI组合子组合逻辑的:

I -> II↦II
K -> II↦III↦II
S -> II↦III↦IIII↦II[IIII][III[IIII]]

这些语法的一个语法问题是优先级非常低,如果我们尝试将参数传递给这些组合器,这将是一个问题。通常,您可以通过将组合器C括在括号中来解决此问题(C),但我们没有括号。但是,我们可以使用I[]包装C其他一些具有足够高优先级的魔术,以便以后可以使用它:

I[C][[I[[]]I]]

最后,编写应用程序A x y z(这里A是一个组合子“parenthesised”如上所示,并且xyz可以或可以不被parenthesised,或者可以是更大的表达式),我们可以写出:

A[x][y][z]

剩下的问题是等价的括号实际上如何工作。我将尝试按照我提出的顺序对其进行大致解释。

因此,我们在语法上将某物分组的是方括号[]。括号在Mathematica中以两种方式出现。既可以作为函数调用f[x],也可以作为索引运算符f[[i]](实际上只是的简写形式Part[f, i])。特别是,这意味着有效语法[C]也不[[C]]是。我们需要在它前面的东西。从理论上讲,这可以是任何东西。如果我们使用,I我们已经可以得到I[C]例如。由于I它不是一元函数(根本不是一个函数),因此仍未进行评估。

但是现在我们需要某种方式C再次提取,因为否则,当我们尝试将参数传递x给它时,它实际上将不会被评估。

这是方便使用的地方,f[[i]]可用于任意表达式f,而不仅仅是列表。假定f自身为形式head[val1,...,valn],则f[[0]]给出headf[[1]]给出val1f[[-1]]给出valn等等。因此,我们需要获取1或再次-1提取C,因为I[C][[1]]I[C][[-1]]求和C

我们可以1从像这样的任意未定义符号中获得x,但是要做到这一点,我们需要另一个字符进行除法(x/x给出1undefined x)。乘法是我们可以执行的唯一没有任何附加字符的算术运算(原则上),因此我们需要一些可以相乘的值以赋予-11。这最终就是为什么我专门选择I了我们的标识符的原因。因为IMathematica本身就是虚构单元的内置符号。

但这留下了最后一个问题:我们实际上如何I自我繁殖?我们不能只写,II因为它被解析为单个符号。我们需要分离这些标记,而无需a)更改其值和b)使用任何新字符。

魔术的最后一点是未记录的行为:(f[[]]或等效地Part[f])是有效的语法并返回f自身。因此,而不是乘II,我们乘I[[]]I。插入方括号会使Mathematica之后寻找新令牌,并根据需要I[[]]I进行评估-1。这就是我们最终得到的结果I[C][[I[[]]I]]

请注意,我们不能使用I[]。这是对该函数的无参数调用I,但是正如我之前所说I的不是函数,因此将保持未评估的状态。


很棒的答案。
帕特里克·史蒂文斯

23

Python 2,8个字符

exc'%~-0

这些字符允许使用格式字符串和转换/执行任何Python程序exec。尽管图灵完备性不需要翻译任何程序,但我知道这是最少的字符,无论如何都使其成为TC。它是如此强大仅仅是一种奖励。

也可以使用双引号以及除零以外的任何一位数字。(现在我想想,1肯定是更好,从而缩短了程序,因为你可以使用111111,也是如此。)

这是程序print

exec'%c%%c%%%%c%%%%%%%%c%%%%%%%%%%%%%%%%c'%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0

在线尝试

基本程序要求

exec''

x添加到程序中的每个字符都需要(字符-计数):

  • % -- 2**(n-1)
  • c -- 1
  • - -- ord(x)*2
  • ~ -- ord(x)*2
  • 0 -- 1

例外是可以采取某些优化/快捷方式来缩短编码的程序,例如使用%'0'字符0代替48 -~等。

实际用途(又名打高尔夫球):我使用这种策略来解决此难题,而无需使用其他障碍因素。

归功于字符列表和编码器程序:此处

有关找到结果程序大小的下限(无优化)的信息,请参见此注释

所需的字节数增加了O(2**n),因此不建议在打高尔夫球时使用此方法。使用此源限制的奎因将非常长。


如果仅执行运算符优先级+-在之前执行%,我们可以删除一个字符。
mbomb007 '17

值得一提的是,对于图灵完备性而言,不必将每个Python程序都转换为精简的字符集。尽管我想不使用它就很难获得所需数量的控制流exec
Martin Ender

从技术上讲,这实际上甚至不是Turning Complete语言,是吗?它具有调用Turning Complete语言的解释器的能力,该语言是嵌入式Python解释器。只要它能够(例如)调用另一个解释器的shell命令,它就可以用任何语言工作,而不管它是否正在完成。
mmachenry

@mmachenry Python正在使用其自己的编译器和解释器。它没有使用其他单独的语言。并且已经在Python中创建了一个解释器,因此它是Turing Complete。使用该知识,您的论点是错误的。
mbomb007'2

@ mbomb007不,我的论证不是虚假的。显然,Python是Turning Complete语言。通过使用您希望内部调用使用的任何字符从Python调用Python解释器来完成计算。您指定程序所用的语言只是一种编码,而不是编程语言。使用此功能,通过使用字符0和1并以二进制形式查看源文件,实际上可以使每种编程语言图灵完成。问题的实质是找到实际语言的句法子集。
mmachenry

23

C(便携式),24 18 13个字符

aimn[]={};,1+

这涵盖了所有形式的程序

main[]={<sequence of constants>};

...其中常量序列(格式为1 + 1 + 1 ...)包含程序的机器代码表示。假设您的环境允许执行所有内存段(对于tcc [感谢@Dennis!]和某些没有NX位的计算机,显然是正确的)。否则,对于Linux和OSX,您可能必须在关键字前加上关键字const;对于Windows,您可能必须添加#pragma显式将该段标记为可执行文件。

例如,以下以上述样式编写的程序可Hello, World!在x86和x86_64的Linux和OSX上打印。

main[]={111111111+111111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+11111+11111+11111+11111+11111+11111+11111+11111+111+11+11+11+11+11+11+1+1,1111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+11111+11111+11111+11111+11111+11111+1111+1111+1111+111+111+111+111+111+111,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+11+11+1+1+1+1+1+1+1,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+111+111+111+111+111+111+11+11+11+11+11+11+1+1+1+1,111111111+111111111+111111111+111111111+111111111+1111111+1111111+1111111+1111111+111111+111111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+11+11+11+11+1+1+1+1,111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+1111+1111+1111+111+111+111+111+111+11+11+11+11+11+11+1+1+1+1+1+1,111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+111+111+11+11+11+11+11+11+11+1,1111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+1111+1111+1111+1111+1111+1+1+1+1+1,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+11+11+1+1+1+1+1+1+1+1+1,1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+111111+111111+111111+111111+11111+11111+1111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+11+11+1+1+1+1+1,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+111111+11111+11111+11111+11111+1111+1111+1111+1111+1111+111+11+1+1+1+1+1,1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+1+1+1+1+1+1+1,1111111111+1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+11+11+11+1+1+1,1111111111+111111111+111111111+111111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+111+111+111+111+1+1+1+1+1+1,111111+111111+11111+11111+11111+11111+11111+11111+11111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+11+11+11+11+11+11+11+11+11+11,11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+111+111+111+111+111+11+11+11+11+11+11+11+1+1+1,111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+111+111+11+11+11+1,111111111+111111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+11+11+1+1+1+1+1,11111+11111+11111+11111+1111+1111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+1+1+1+1+1};

在线尝试!


2
@GB:至少在x86机器代码中避免使用零是很容易的(这不是非常重要的指令),特别是因为只有在连续四个零字节的情况下才会出现此问题。

2
@GB在具有32位整数的计算机上0==1111111111+1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+111+11+11+11+11+11+11+11+1
ceilingcat '17

3
tcc让您无所不用其极consttio.run/nexus/…–
丹尼斯

8
@GB我刚刚意识到0的简短表示形式是1==11
ceilingcat '17

3
@ wizzwizz4,在任何情况下它都不是纯C语言,从任何意义上说,它都不使图灵完整。它没有C语义。无论如何,由于您始终依赖于编译器和执行环境的详细信息来获得可运行的所有内容,因此您最好允许使用任意命名的入口点。
约翰·博林格

20

视网膜,6个字符

$%`{¶

以及换行(0x0A)。

一方面,我很惊讶我能做到这么低。另一方面,我对加入感到非常不满。每个$`{都可重复用于两个或什至三个目的,但 一起只能用于一个目的。这使它们看起来相当浪费,并且稍微破坏了该方法的优雅性。我希望有一种方法可以击败这种结构。

关于证明。我将描述一种使用上述字符将循环标签系统转换为Retina 的简单方法。

首先,我们将使用`and {作为二进制字母,而不是0and 1。这些很方便,因为它们不需要在正则表达式中转义,但是它们对于Retina或替换语法具有含义。我正在使用`for 0{for 1,但是这个选择是任意的。此外,我们将反转内存中的字符串(和生成),因为使用最后一个字符可以使我们使用$$`而不是^and $',从而最大化了字符重用。

如果初始单词用表示,Si调用th(反向)乘积,则结果程序将如下所示:pi


S
{`

{$
¶p1$%``
``$

{$
¶p2$%``
``$

{$
¶p3$%``
``$

...

每次应用生产时,这种构造不可避免地会在内存中增长,并且不太可能终止-实际上,在循环标签系统终止的时候(当工作字符串变空时),最终的Retina程序的行为将变为基本上没有定义。

让我们看一下程序的功能:


S

我们首先将工作字符串初始化为初始单词。

{`

这会将程序的其余部分包装在运行中,直到无法更改结果字符串为止(嘿,天真的内置无限循环检测功能是免费的...)。确实没有必要使用两个换行符,但是它们可以将循环与单独的作品更清楚地分开。程序的其余部分遍历每个产品,由于循环的原因,我们一遍又一遍地有效地循环处理它们。

每个生产过程分为两个阶段。首先,我们处理前导(或在本例中为尾随)符号为的情况{,在这种情况下,我们使用生产:

{$
¶pi$%``

仅当字符串以结尾时,正则表达式才匹配{。如果是这种情况,我们将其替换为:

  • 换行符()。我们将只使用工作字符串的最后一行,因此到目前为止,这实际上有效地丢弃了工作字符串(这就是程序的内存使用量会越来越大的原因)。
  • 当前的生产(),我们在此之前添加到工作字符串(循环标签系统将其附加到工作字符串)的前面。pi
  • 上一个剩余的工作字符串($%`)。这就是为什么我们需要插入:来$%`拾取比赛剩下的所有内容,但只能在同一行上进行。因此,这看不到我们从早期作品中剩下的所有垃圾。这个技巧使我们能够在工作字符串的末尾匹配某些内容,从而在工作字符串的开头插入某些内容,而不必使用诸如(.+)$1这样的东西,这会大大消耗我们所需的字符数。
  • 单个反引号(`)。这有效地替换了我们用{1-symbol)匹配的`0-symbol),因此下一阶段无需知道我们是否已经处理了当前产品。

然后,每个产品的第二部分就是琐碎的情况,其中产品被跳过:

``$

我们只需删除尾随 `。我们`在第一行需要两个的原因是Retina认为第一个反引号是配置和正则表达式之间的分隔符。这只是给它一个空配置,以便我们可以在正则表达式本身中使用反引号。


20

Java 7、18、17个字符

\bcdefu0123456789

所有的Java源代码都可以简化为unicode代码点。不需要“ a”,因为它仅用于*:jJzZ。星号用于乘法或块注释。乘法只是重复的加法,您可以改为使用单行注释(或忽略它们)。冒号用于三元运算符,您可以使用if语句代替,foreach循环也可以用普通的for循环替换。j和z不是Java中任何关键字的一部分。

尝试删除任何其他字符都需要我们至少添加Java样板中要求的字符之一class a{public static void main(String[]a){}}。见下文:

1 -> a (which has already been removed)
2 -> r (required for "String")
3 -> S (required for "String")
4 -> t (required for "static")
5 -> S (required for "String")
6 -> v (required for "void")
7 -> g (required for "String")
8 -> ( (required for "main(String[]a)"
9 -> i (required for "static")
b -> { (required for "class a{")
c -> l (required for "class")
d -> } (required for "(String[]a){}}")
e -> n (required for "main")
f -> o (required for "void")

这是一个Hello World程序的示例,请在线尝试!

Java 8,16个字符

\bdefu0123456789

感谢ais523指出这一点。Java 8允许接口具有静态方法,这意味着我们可以删除“ c”,因为我们不需要“ class”中的“ l”。使用“ c”的原因是,,<lL\|与删除“ a”相比,我们最终会损失更多的Java功能,但是我们仍然有足够的钱来完成它。在线尝试!


3
当然,弄清楚哪些十六进制数字可以省略是用Java解决此问题的有趣部分?:)
Martin Ender

@MartinEnder绝对。我计划在有空的时候做更多的工作
Poke

6
和准备写点东西的我Java, 127 characters……Nice一口,戳;)
OlivierGrégoire17年

根据答案中的必需字符,认为无法删除任何其他十六进制数字。

3
如果您切换到Java 8,则可以在16年内完成;Java 8允许接口具有静态方法,允许您删除c(所有字母interface仍然可以使用no ac使用十六进制文字访问)。

19

迷宫,5个字符

~{}

加上换行符(0x0A)和空格(0x20)。

我将以Smallfuck的简化形式(使用1位单元的缩减的Brainfuck变量)形式草绘一个证明。请注意,Smallfuck本身不是图灵完备的,因为该语言指定其磁带必须是有限的,但是我们将假定Smallfuck的变体具有无限的磁带,那么它将是图灵完备的(因为迷宫没有存储空间受设计限制)。

整个减少过程中一个重要的不变性是,每个程序(或子程序)都将导致迷宫程序(或子程序)在同一行上开始和结束,并跨越偶数列。

迷宫有两个堆栈,这些堆栈最初填充有无限(隐式)的0s。{}在这些堆栈之间移动最高值。如果我们将主堆栈的顶部视为当前的“单元”,则可以将这两个堆栈视为Smallfuck使用的无限磁带的两个半无限半。但是,在堆栈中具有每个磁带值的两个副本将更加方便,以确保上述不变性。因此,<>将分别转换为{{}}(如果愿意,可以交换它们)。

而不是允许单元格值01,我们使用 0-1,可以在~(按位取反)之间进行切换。确切的值对于图灵完备性无关紧要。我们必须更改堆栈中值的两个副本,这又使我们获得了一个偶数长度的转换:*变为~}~{

剩下控制流程命令[]。迷宫没有明确的控制流程,但是控制流程由代码的布局确定。我们需要空格和换行符来进行布局。

首先,请注意,这~~是一个不可操作的选项,因为两者可以~有效地取消。我们可以使用它在代码中具有任意长的路径,只要它们的长度是偶数即可。现在,我们可以使用以下构造将其翻译AA[BB]CC为迷宫(我使用双字母,以使迷宫中每个片段的大小均等,如不变式所保证):

      ~~~~
      ~  ~~~
AA~~..~~~~ ~CC
   ~     ~
   ~     ~
   ~     ~
   ~~~BB~~

在这里,..是一个合适的数字,~其与的宽度匹配BB

再次注意,构造的宽度保持均匀。

现在,我们可以研究该循环的工作方式。通过输入密码AA。第一个~~什么都不做,让我们到达交汇处。这大致对应于[

  • 如果当前单元格值为零,则IP继续直接向前,最终将跳过BB。该..部分仍然是禁止操作的。然后,我们~在另一个路口到达一个路口。现在我们知道当前值不为零,因此IP确实向北旋转。它绕过顶部的弯头,直到六点后到达另一个交点~。因此,此时的当前值仍为非零,并且IP再次转弯以向东移向CC。需要注意的是三个~在之前CC返回当前值0,因为它应该是当循环被跳过。
  • 如果循环开始时的当前单元格值不为零,则IP向南转弯。它~在到达之前再运行六个BB(不执行任何操作),然后~在到达下一个结点之前再运行六个。这大致对应于]
    • 如果当前单元格为零,则IP继续向北移动。下一个~使该值不为零,以便IP使用第二个结点,该结点将路径与完全跳过循环的情况合并。同样,三者~在达到之前将值恢复为零CC
    • 如果当前单元格非零,则IP向西转。有~下一个路口,这意味着在这一点上的电流值为零,使得IP不断去西部之前。然后,~在IP再次到达初始结点之前,会有奇数个,因此返回该值,-1并且IP向南移动到下一个迭代中。

如果程序包含任何循环,则首先AA需要将其扩展到程序的顶部,以便IP找到正确的单元开始:

~     ~~~~
~     ~  ~~~
AA~~..~~~~ ~CC
   ~     ~
   ~     ~
   ~     ~
   ~~~BB~~

就是这样。请注意,由此减少而产生的程序将永远不会终止,但这不是图灵完备性要求的一部分(请考虑规则101或Fractran)。

最后,剩下的问题是这是否最优。就工作负载特征而言,我怀疑是否可以做得比三个命令更好。我可以看到基于Minsky机器的替代构造,该构造具有两个寄存器,但是这将需要=()or =-~,仅具有一个堆栈操作命令,然后具有两个算术命令。我很高兴被证明是错误的。:)

至于布局命令,我相信换行是必要的,因为在单行上不可能有有用的控制流程。但是,空间在技术上不是必需的。从理论上讲,可能有一种构想可以用~{}=()=-~)填充整个网格,或者使用参差不齐的布局(线的长度不尽相同)。但是,编写这样的代码非常困难,因为迷宫随后会将每个单元格都视为一个结,并且您需要非常小心,以防止在不希望出现代码时分支。如果有人可以证明或反对为图灵完备性省略空间是否可行,那么我很乐意为此提供巨额奖金。:)


19

Haskell,5个 7个字符

()\->=;

作为功​​能语言,Haskell当然具有lambda,因此模拟lambda演算很容易。lambdas的语法是,因此我们至少需要字符。 另外,我们需要数量不限的可变符号才能构建任意的lambda表达式。幸运的是,我们并不需要为这个新的字符,因为,,,...,都是有效的变量名。实际上,内部括号的每个组合都是有效的变量名称,除了just 和除外,后者保留用于lambda表达式,而and则用于开始行注释。(\variable->body)argument()\->
(>)(>>)(>>>)\->\->--

例子:

  • S = (\(>)(\\)(-)->(>)(-)((\\)(-)))类型(t2 -> t -> t1) -> (t2 -> t) -> t2 -> t1
  • K = (\(>)(-)->(>))类型t -> t1 -> t
  • I = (\(>)->(>))类型t -> t

编辑:但是,正如ais523在评论中指出的那样,此构造实现了类型化的lambda演算,其本身不是图灵完备的,因为它缺乏进入无限循环的能力。为了解决这个问题,我们需要一些执行递归的函数。到目前为止,我们使用了无法命名的未命名Lambda,因为它们没有名称。因此,我们必须添加字符=;实现一个fix功能:

(>)=(\(-)->(-)((>)(-)));   -- equivalent to: f =(\ x -> x ( f  x ));

有了这个声明,我们的lambda演算就完成了Turing,但是添加了=并且;,我们不再需要lambda了,正如您在nimi的答案中看到的那样,它只使用了()=;


从技术上讲,是否在没有编译时就将其删除main
PyRulez

4
简单键入的SKI组合器演算不是图灵完备的。为此,您需要一个无类型的lambda演算。不幸的是,正如您的演示所提到的,Haskell默认情况下在代码上添加了类型化的解释。

@PyRulez根据默认规则,我假设函数是可以接受的。
Laikoni '17

@ ais523 SKI组合器只是一个示例,使用给定的表示法,可以构建任意的lambda术语,例如,其上的教堂数字和功能。
Laikoni '17

@ ais523输入Lambda微积分需要多少个组合器?我认为您只需要y组合器,对吗?
PyRulez '17

18

CJam,3个字符

)根据Martin Ender的建议删除

'(~

与Python类似,作为示例。

使用'~您可以获得~角色。然后,使用(,您可以对其进行递减以获得所需的任何字符(~是最后一个可打印的ASCII字符)。~将任何字符串评估为普通的CJam代码。可以通过以下方式构造字符串:获取字符[(通过递减~),评估它,放置其他字符序列,然后评估该字符]。这样,您可以仅使用这三个字符来构建和执行任何CJam程序。

仅使用2 + 2计算 '(~


对于另一个挑战,有人制作了一个程序,该程序可以使用任何cjam程序并将其自动编译为该子集。我希望我能找到它
Zwei

1
我设法打败了2 + 2计划'~((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((~'~(((((((((((((((((((((((((((((((~'~(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((~
Zwei

@Zwei很好,很适合您的名字
Chromium

18

脑筋急转弯,6个字符

(){}[]

Brain-Flak是一种极简主义语言,只有8个可用字符。但是,可以证明存在一个Brain-Flak的子集,该子集也仅使用6个字符就可以完成图灵完成。

我们要做的第一件事是用一堆Brain-Flak实现一台Minsky机器。如果我们可以证明一台Minsky机器只有一堆是可能的,那么我们可以证明Brain-Flak在没有<>[]nilads的情况下是图灵完整的。这不会立即保存任何字符,但是将来在我们显示<...>不必要时会保存。

Minsky机器是Turing完整自动机的一种,具有有限数量的无界寄存器和两个指令:

  • 递增寄存器

  • 如果非零减量,则转移到指定指令

要在Brain-Flak中设置goto结构,我们可以使用以下代码段:

(({}[()])[(())]()){(([({}{})]{}))}{}{(([({}{}(%s))]{}))}{}

这将使计数器递减,%s如果为零则运行。这些链接在一起的一堆将使我们能够在堆栈上放置一个数字,该数字指示我们要转到哪一行。这些中的每一个都会减少堆栈的顶部,但是只有其中一个会实际运行代码。

我们将其用作所有Minsky Machine指令的包装。

在不切换堆栈的情况下,增加特定寄存器非常容易。可以使用以下字符串公式实现:

"({}<"*n+"({}())"+">)"*n

例如,要增加第三个寄存器,我们将编写以下代码:

({}<({}<({}<({}())>)>)>)

现在我们只需要执行第二个操作。在Brain-Flak中检查数字是否为零非常简单:

(({})){(<{}%s>)}{}

%s当TOS为零时才执行。这样我们就可以进行第二次操作了。

由于Minsky机器正在完成图灵处理,因此不使用<>[]操作,Brain-Flak也可以完成图灵处理。

但是,由于<...>[...]仍在使用中,我们尚未减少字符数。这可以通过简单的替换来解决。因为<...>实际上等于[(...)]{}所有情况。因此,不使用<>字符(加上所有非操作),Brain-Flak就是图灵完整的。


“因为<...>并且[...]仍在使用。” 但是,您没有删除[...]。请解决。
CalculatorFeline

问:[...]真的有必要吗?可以从头开始执行0 ({})(但它依赖于一个空堆栈,因此必须仔细地改写0)。主要问题是无法访问而无法进入堆栈<...>(不再可以模拟)
CalculatorFeline

16

> <>,3个字符

> <>可以在3中完成1p-,它可以:

1          Push 1
p          Pop y, x, c and put the char c in cell (x, y) of the codebox
-          Subtraction: pop y, x and push x-y

p提供反射,通过将字符放入代码箱来修改2D源代码。使用1-,您可以将任意数字压入堆栈,因为1-减1且111-1--x-(1-1-1) = x+1)加1。

一旦所有1p-命令执行完毕,指令指针就会回绕,从而使其能够执行“真实”代码。

计算斐波纳契数(从此答案)的示例程序为:

111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--11-11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--11-1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--11p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1-1-1-1--1p

在线尝试!1p-执行完所有命令后,代码框将如下所示:

01aa+v1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1- ...
@+&1->:?!;&:nao:

v第一行之后的所有内容外,这是标准的Fibonacci> <>程序。


13

bash,9个字符

01456\$ '

Bash具有$'\nnn'使用八进制ascii值输入字符的语法。我们可以eval以此格式输入命令$'\145\166\141\154'。我们首先将所需结果转换为八进制值。然后,我们使用0、1、4、5和6以外的数字将任何八进制值转换为使用$(())和减法对所述八进制值求值的表达式,并eval在前面附加一个。在最后一步中,我们添加了另一个eval并将括号和减号转换为它们的八进制值。使用此方法,我们可以执行任何bash命令,因此该子集已经完成。

例:

dc 变成

$'\144\143' 变成

$'\145\166\141\154' \$\'\\144\\$((144-1))\' 变成

$'\145\166\141\154' $'\145\166\141\154' $'\$\\\'\\\\144\\\\$\050\050144\0551\051\051\\\''


12

事件,2个字符

您选择哪个字符也没关系;两个八位位组的任何组合在事件中都是图灵完成的。

实际证明这比您想像的要困难得多,在撰写本文时,Esolang上有关事件讨论页面专门讨论了该问题。不过,我将尝试在下面给出最简单的已知证明的摘要。

证明之前,有一些背景知识。事件通过查看源代码来推断程序中使用的令牌(令牌是在源中正好出现三次的字符串,不是另一个令牌的子字符串,并且不与另一个潜在令牌重叠)。这样,通过更改标记的含义,可以将任何程序转换为几乎使用任何字符集。尽管很难编程,但该语言是图灵完备的(并且也具有I / O的完整性!),因此,您所需要的“全部”是一种编码令牌的方法,以便令牌只能使用两个字符。

现在,这里是证明的摘要(由Esolang的常驻数学家Ørjan找到)。这个想法是,我们使用一个字符(例如1)的两个副本在另一个字符(例如)的广阔海洋中对令牌进行编码01每个标记的s 之间的距离都不同,但始终是4的倍数。然后对于标记之间的填充,我们使用0s的一个额外列表,1中间带有a ,但是在a 的两边分别1是0 不是 4的倍数,而是一个特定于该程序特定事件的唯一数字,该数字不会出现在该程序的其他位置。这意味着每个1……1填充内只能出现两次,因此不会成为令牌的一部分;每个预期的令牌都恰好包含两个1,并且伪造的令牌不能包含多个1。然后,我们只需在侧面添加一些填充以通过添加至少四个副本来删除所有包含一个1或零个1s的令牌。


11

视网膜,3个字符

{`

和换行符。

首先,我们需要换行符才能进行替换(除非我们希望将整个程序放入一个正则表达式中(这需要更多字符),否则这是必需的);和`{是最小字符密集的方式做循环。事实证明,我们不需要任何其他东西。

我们要实现的目标语言是Thue的确定性变体(不确定性对于图灵完备性不是必需的;无论使用哪种评估顺序,都可以编写Thue程序以使其正常工作)。基本思想是编译pattern::=replacement

`pattern
replacement

(这是Thue的Retina直接翻译;或者,如果您了解Retina但不了解Thue,则可以将其用作学习Thue工作方式的方法);作为例外,{`相反,第一个模式是开头(为了将整个程序放入循环中; Thue程序将继续运行,直到不再可能进行替换为止,这将导致Retina以相同的方式工作)。

当然,这意味着我们需要通过模式,替换{`模式来证明Thue Turing-complete ,但这很简单;我们替换ASCII码字符ñ`ñ +1 {,和另一个`。一个模式显然不可能在字符边界之外的任何地方进行匹配,因此最终将完​​成与原始程序相同的操作。


1
“这些程序将继续运行,直到无法进行更多替换为止,这将导致Retina以相同的方式工作”,唯一的例外是,如果整个程序中的一次更改都无法更改字符串,则Retina会提前终止。因此,您甚至可以免费获得一些简单的无限循环检测。
Martin Ender

1
对啊 当然,这不会影响Turing的完整性(因为不会改变内部状态的无限循环不会对程序的计算类产生影响)。

10

Brachylog,5个字符

~×₁↰|

该字符子集使我们可以实现Fractran的版本,在该版本中,唯一可以出现的数字是repunits的乘积(即,只能使用数字1以十进制形式书写的数字的乘积)。(用整数作为下标)将当前值除以该整数,但前提是要精确地除以该整数(否则,它将“失败”并查找要运行的另一种情况;|将这些情况分开)。×让我们乘以一个整数。因此,使用~×₁|我们可以执行Fractran执行的一个步骤。然后让我们递归,以新的当前值再次运行整个程序。这是一个11111111111111111111111/111翻译成Brachylog 的非常简单的Fractran程序()的示例。

那图灵完成了吗?要使Fractran Turing完整,我们需要做的是提供足够数量的质数(足够用Fractran本身为Turing完整语言编写解释器)。有五种证实的和四种怀疑的除了很有可能还没有发现的repunit素数,在这种情况下,这实际上超出了我们的需求。该程序检查从左到右的可能性,因此我们可以使用一个素数作为指令指针,再使用两个素数作为计数器,仅使用三个素数就说明了图灵完备性(这也是一件好事,因为它使我们可以使用带有2、19的repunits和23位数字,而不必诉诸具有317或1031位数字的经过验证但令人讨厌的repunits,这将使源代码相当难以编写)。这样就可以实现带有两个计数器的Minsky机器(足够图灵完整性)。

这是编译的具体工作方式。我们将在Minsky机器的实现中使用以下两个命令(这是众所周知的Turing complete),每个命令将带有一个整数作为标签:

  • 标签L:如果计数器{A或B}为零,则转到X。否则递减,然后转到Y。
  • 标签L:递增计数器{A或B},然后转到Z。

我们通过在分母中放置11的幂(最高的优先)来选择要运行的命令。11的指数是命令的标签。这样,匹配的第一个分数将是当前正在执行的命令(因为以前的命令不能除以所有那些11s)。对于减量命令,我们还将分母1111111111111111111或11111111111111111111111111分别分配给计数器A或B,然后再执行另一个没有该因数的命令。“减”情况将由第一个命令实现,“零”情况将由第二个命令实现。同时,将在分子中使用适当的11的幂来处理“ goto”,并通过分子中的1111111111111111111或11111111111111111111111来“递增”。


不能使用成对共质素重复单元的任何特殊原因吗?
CalculatorFeline

@CalculatorFeline:不,但是直到我找到不需要它们的构造后,我才想到它们。使用此字符集编写的高尔夫程序肯定会对您有所帮助。

此外,所有大于1的repunits是成对的互质数(考虑一下)
CalculatorFeline

@CalculatorFeline:不,不是。111和111111都可以被3整除,这很明显。

*没有repunit划分另一个repunit
CalculatorFeline

10

Befunge-98,3个字符

据我所知,Befunge-98应该是完整的,所以我们只需要说明如何只用三个字符就可以生成任何Befunge-98程序。我最初的解决方案依靠以下四个字符:

01+p

通过将多个1值与+命令加在一起,我们可以将任何正整数都放入堆栈,对于零,我们只需使用0。一旦我们有能力输入所需的任何数字,我们就可以使用p(put)命令将任何ASCII值写入Befunge播放场中的任何位置。

但是,正如Sp3000所指出的,实际上您只需要三个字符即可完成操作:

1-p

可以通过以开头,1然后反复减去1(例如-3将是11-1-1-1-)来计算任何负数。然后,任何正数都可以通过从1中减去1-n来表示,其中1-n是我们已经知道如何处理的负数(例如4 = 1-(-3),应为111-1-1-1--)。

因此,我们可以使用三个字符来编写一种引导加载程序,该引导加载程序会缓慢生成我们要执行的实际代码。一旦该加载程序执行完毕,它将环绕到Playfield第一行的开头,这时应该保留我们新生成的代码的开头。

例如,这是一个引导加载程序,该引导加载程序生成将2 + 2相加并输出结果所需的Befunge代码: 22+.@

再举一个稍微复杂的例子,那就是“ Hello World”: "!dlroW olleH"bk,@


这是一个多语种,相同的字符可用于> <>及其派生词。干得好!
索尔(Sok)

2
Befunge-98是可行的在3 1p-以及
SP3000

@ Sp3000当然可以!我确定一定有办法将其减少到3个字符。谢谢。
James Holderness

9

Ruby,8个字符

eval"<1+

受Python答案启发

这个怎么运作

  • eval可用于执行任意字符串。
  • “ <1+是构建任何字符串所需的最少字符集

可以使用空字符串作为起点构建ruby中的字符串,并在其后附加ascii字符,例如:

eval ""<<111+1<<11+11+11+1<<111<<11+11+11+1

实际上相当于

eval ""<<112<<34<<111<<34

评估字符串

p"o"

8

OCaml,9个字符

fun->()`X

这些字符足以在OCaml中实现SKI组合器演算。值得注意的是,我们能够避免使用带有足够括号的空间。不幸的是,OCaml中的lambda表达式需要fun关键字,因此不可能有更简洁的解决方案。如果需要更复杂的lambda表达式,则可以使用相同的字母来构建任意变量名称。

S组合器:

fun(f)(u)(n)->f(n)(u(n)) 与类型 ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c

K组合器:

fun(f)(u)->u 与类型 'a -> 'b -> 'b

我组合器:

fun(f)->f 与类型 'a -> 'a

正如ais523指出的那样,仅对SKI进行编码是不够的。这是使用多态变体来操纵类型系统的Z编码。有了这个我的子集应该完成。

Z组合器:

fun(f)->(fun(`X(x))->(x)(`X(x)))(`X(fun(`X(x))y->f(x(`X(x)))y))

与类型 (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b


2
简单键入的SKI组合器演算不是图灵完备的。为此,您需要一个无类型的lambda演算。不幸的是,正如您的演示所提到的,默认情况下,OCaml在代码上放置类型化的解释。

1
然后,我只需要再输入几个字符就可以使用多态变体,从而可以对y组合器(和类似的z组合器)进行编码。
Devin Lehmacher '17

什么是Z组合器?
CalculatorFeline

@CalculatorFeline这是y-combinator的严格变体。在OCaml中这是必需的,因为OCaml并不懒惰。这是维基百科页面的链接:en.wikipedia.org/wiki/...
德文法国邮政

8

基于堆栈的连接语言,4个字符

欠载

():^

高尔夫脚本

{}.~

贾姆

{}_~

GS2

  • 退格键,制表符,@空格(我知道GS2经常使用不可打印的东西,但这很荒谬……)

dc(@seshoumara建议)

[]dx

仅使用():^(由于Esolang的常驻数学家Ørjan)就证明了欠负载图灵完备。该证明太长了,无法在这里解释,但是如果您有兴趣,可以在这里阅读。

有问题的命令是()(在堆栈上放置代码文字),:(重复的顶部堆栈元素)和^(评估堆栈顶部)。这些命令在基于堆栈的语言(尤其是串联语言)中相当常见,因此我在上面给出了一些它们的集合。出于与欠载相同的原因,这些语言都用4个字符完成了图灵完整。


我了解您可以使用这些操作执行堆栈操作,但是您是否不需要至少一些数字来填充该堆栈才能进行数学计算?还是使用4个字符之一在一元函数中完成这些操作?
seshoumara

1
@seshoumara:使用此方法时,数字(以及几乎所有其他数据存储)的实现非常间接。在获得可识别为算术的东西之前,有大约两,三个,甚至四个抽象层次。这种事情在像这样非常有限的系统的图灵完备性证明中很常见。

我当时正在考虑自己用dc也是基于堆栈的语言提交答案,但是使用了另一种涉及字符多于4的方法。dc没有串联运算符,但是确实有您提到的等效运算符:[] d x。dc可以放入您的清单吗?
seshoumara

@seshoumara:是的,它似乎具有所需的所有功能。我添加了它并记了您的功劳。

也许您可以查找FlogScript
mbomb007 '17


7

球拍(方案),4个字符

(λ)

仅使用λ,括号和空间,我们就可以直接在Scheme的Lambda微积分子集中进行编程。通过将所有标识符串联在一起,可以为所有标识符重用λ字符,以提供任意数量的唯一标识符。

例如,这是经典的Omega组合器,它会永远循环。

((λ (λλ) (λλ λλ)) (λ (λλ) (λλ λλ)))

6

Python 3,9个字符

exc('%1+)

有关基本说明,请参见我的Python 2答案。这个答案建立在那个答案的基础上。

除了()现在可以使用括号之外,我们还可以删除一个字符,而不是简单地使用与Python 2相同的字符并添加。程序仍将具有以下基本形状

exec('%c'%stuff)

但我们可以使用+代替来缩短程序长度-,然后可以~使用1代替来删除程序0。然后1,我们可以添加,11111以获取所需的ASCII值。

该程序print()的最短内容如下:

exec('%c%%c%%%%c%%%%%%%%c%%%%%%%%%%%%%%%%c%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%c%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%c'%(111+1)%(111+1+1+1)%(11+11+11+11+11+11+11+11+11+1+1+1+1+1+1)%(11+11+11+11+11+11+11+11+11+11)%(111+1+1+1+1+1)%'('%')')

在线尝试

您可能在想自己,一个人如何创建一个NUL字节而没有0?不要害怕,年轻的蚱hopper!因为我们也可以使用%数学功能,使用创建零1%1


为什么要在程序中使用NUL字节?
NieDzejkob

@NieDzejkob在此站点上,“为什么”的答案始终是“因为我们可以”。但是,在这种情况下,即使您给出错误,也不能完全实现Python。
mbomb007 '18

图灵完整性不需要NUL字节。BF口译员可以不用一个就可以写作
MilkyWay90

@ MilkyWay90是的,但是如果可以的话,为什么不解释呢?
mbomb007

6

PHP 7,6个字符

'().;^

这个想法是可以使用以下结构执行任意代码:

('create_function')('','<code>')();

eval 在这里不起作用,因为它是一种语言构造,无法使用变量函数来调用。

create_function 并且可以将代码编写为可用字符的按位XOR的串联:

(<char1_1>^<char1_2>^...).(<char2_1>^<char2_2>^...)...

使用().;^<charX_Y>,我们可以得到

()./:;<=JKLMXY^_bcdepqvw

和一些无法打印的字符。这还不够,但是现在我们也可以调用'eXp'()并获取一些数字字符:

''.'eXp'('eXp'('')) -> 1
''.'eXp'('eXp'('eXp'(''))) -> 2.718281828459
''.'eXp'('eXp'('eXp'('eXp'('eXp'(''))))) -> 3814279.1047602

它给了我们123(其他字符会被XOR被忽略,如果其他字符串是一个字符)。从().;^123我们现在可以生成所有的ASCII字符。

在线尝试


5

派克(Pyke),5个字符

0h.CE

这能够产生无限大的数字,将其转换为字符串,然后将其评估为Pyke代码。

代码说明:

0-将0加到堆栈中。这是开始输入号码所必需的

h-之前增加数字。通过任意多次重复此操作,您可以创建无限大的数字。Pyke支持使用Python编写的bignums,它将使用它们作为默认值。

.C-使用以下算法将数字转换成字符串:(Github link

def to_string(num):
    string = ""
    while num > 256:
        num, new = divmod(num, 256)
        string = chr(new) + string
    string = chr(num) + string
    return string

至此,我们可以在Pyke中使用任意值创建任意数量的字符串和自然数。可以以与正则表达式相对应的形式创建数字,0(h)*并可以使用0(h)*.C。它们可以相互交织在一起,以创建字符串和整数的任意混合。

E-将字符串评估为Pyke代码。这使用与已经运行的Pyke代码相同的环境,因此将共享诸如输入之类的内容。

尝试证明Pyke已完成Turing。

显示语言完整的最简单方法之一是在其中实现Brainf * ck。在Pyke中,这可能比许多其他语言要难得多,因为它的列表和字典操作几乎不存在,因为在Pyke设计用于的区域(中不需要它们。

首先,我们为Brainf * ck创建一个解释器,并使用上面的算法对其进行编码以创建一个数字,然后使用0和表示该数字h。然后,我们创建包含要以完全相同的方式运行的代码的字符串。如果我们将其留在那,我们将获得堆栈

string containing brainf*ck code
string containing brainf*ck interpreter

这意味着代码必须采用与Pyke堆栈先进先出的相反形式。

现在开始有趣的部分:高达216个字节的brainf * ck解释器!

Q~B"><ht.,".:=B;Z]1=L;W~Bo@D=c"ht"{I~c~LZ@EZ]1~LR3:=L)~c\,qIz.oZ]1~LR3:=L)~c\.qI~LZ@.CpK)~c"<>"{I~c"<>""th".:ZE=ZZ1_qI0=Z~L0"":0]10:=L)Z~LlqI~L~Ll"":1_]10:=L))~c\[qI~LZ@0qI\]~B~o>@~o+h=o))~c\]qI~o\[~B~o<_@-t=o)~o~BlN

在这里尝试!

如果您想尝试半完成但可编辑的形式的代码,请在此处尝试!

要将字符串转换为数字,可以使用以下Python代码:

def conv(string, t=0):
    t *= 256
    t += ord(string[0])
    if len(string) != 1:
        return conv(string[1:], t)
    return t

(几乎)最终解决方案可以在这里尝试!

Brainf * ck解释器的解释

首先,将程序分为几部分:

  • 初始化:

Q~B"><ht.,".:=B;Z]1=L; - The initialisation part
Q~B"><ht.,".:          - input.replace("><+-.,[]", "><ht.,")
                       - replace the characters in brainf*ck with some modified ones. 
                       - this means we can `eval` the add and subtract bits easily.
             =B;       - set `B` to this.
                       - The `B` variable contains the instructions
                Z]1=L; - set `L` to [0]
                       - `L` contains the stack, initialised with 0
  • 主循环:

​​ ​ ​

W~Bo@D=c !code! ~o~BlN - The main loop
W                      - do
 ~Bo@D=c               -  c=B[o++]
                       -  the c variable is used to store the current character.
                ~o~BlN - while
                ~o     -   o 
                     N -  ^ != V 
                  ~Bl  -   len(B)
                       -  this stops the program running once it's finished.
  • 说明
    • 递增/递减:+-

​​ ​ ​

"ht"{I~c~LZ@EZ]1~LR3:=L) - The bit that does incrementing and decrementing
"ht"{I                 ) - if c in "ht"
        ~LZ@             -  L[Z]
                         -  `Z` contains the current stack pointer
      ~c    E            -  eval current character with ^ as an argument
                         -  returns the contents of `Z` either incremented or decremented
             Z]1~LR3:=L  - L[Z] = ^
  • 输入,::

​​ ​ ​

~c\,qIz.oZ]1~LR3:=L) - The code for output 
~c\,qI             ) -  if character == ",":
      z.o            -    ord(input)
         Z]1~LR3:=L  -   L[Z] = ^
  • 输出.::

​​ ​ ​

~c\.qI~LZ@.CpK) - The code for input 
~c\.qI        ) - if c == ".":
      ~LZ@      -    L[Z]
          .C    -   chr(^)
            pK  -  print(^)
  • 向左/向右移动<>

​​ ​ ​

~c"<>"{I~c"<>""th".:ZE=Z - main part 
~c"<>"{I                 - if "<>" in c:
        ~c"<>""th".:     -  c.replace("<>", "th")
                    ZE=Z -  Z = eval(char, Z)

Z1_qI0=Z~L0"":0]10:=L) - lower bound check
Z1_qI                ) - if Z == -1:
     0=Z               -  Z = 0
        ~L0"":         -  L.insert("", 0)
              0]10:=L  -  L[0] = 0

Z~LlqI~L~Ll"":1_]10:=L) - upper bound check
Z~LlqI                ) - if Z == len(L):
        ~Ll"":          -  L.insert("", len(L))
      ~L      1_]10:=L  -  L[-1] = 0
  • 有条件的[

​​ ​ ​

~c\[qI~LZ@0qI\]~B~o>@~o+h=o)) - Code for `[`
~c\[qI                      ) - if c == "[":
      ~LZ@0qI              )  -  if L[Z] == 0:
               ~B~o>          -     B[o:]
             \]     @         -    ^.find("]")
                     ~o+h=o   -   o = o + ^ + 1

-并且]

​​ ​ ​

~c\]qI~o\[~B~o<_@-t=o) - Code for `]`
~c\]qI               ) - if c == "]":
          ~B~o<_       -    reversed(B[:o])
        \[      @      -   ^.find("[")
      ~o         -t=o  -  o = o - ^ -1

5

堆叠,5个字符

{!n:}

这很短。如果Stacked可以实现每个SKI组合,则说明Turing Complete。概括:

  • I 组合器-身份功能。 x -> x
  • K combinator-常数函数。 x -> y -> x
  • S combinator-替代函数。 (x, y, z) -> x(z)(y(z))

我组合器: {!n}

现在,针对堆叠的细节。{! ... }是一个nlambda。它是一元函数,其参数是隐式的n。然后,从函数返回最后一个表达式。因此,{!n}是一个接受参数n并产生的函数n

K组合器: {!{:n}}

现在,{:...}是一个不带任何参数并返回的函数...。将其与我们的n-lambda形式相结合,我们得到(为清楚起见添加空格):

{! { : n } }
{!         }   n-lambda. arguments: (n)
   { : n }     lambda.   arguments: ()
       n       yields n.

S组合器: {n!nn!nnn:nnn{!n}!nn!nnn{!n}!n!!}

好的,这看起来有点复杂。因此,lambda接受由非标识符字符分隔的参数。因此,标头中的lambda等效于:

{n nn nnn:nnn{!n}!nn!nnn{!n}!n!!}

这是一个lambda带有三个参数,nnn,和nnn。让我们用x,替换它们yz为清楚起见:

{x y z:z{!n}!y!z{!n}!x!!}

两者{!n}!只是身份功能,可以再次避免空格,即!“执行”。因此,再次减少:

{x y z:z y!z x!!}

附带说明:

{x y z:z y!z x!!}
{x y z:         }  three arguments
       z y!        apply `y` to `z` -- `y(z)`
           z x!    apply `x` to `z` -- `x(z)`
               !   apply `x(z)` to `y(z)` -- `x(z)(y(z))`

因此,这是S组合器。


{n nn nnn:nnn{!n}!nn!nnn{!n}!n!!}包含空格。
CalculatorFeline

@CalculatorFeline您是否读过之前的句子?好的,这看起来有点复杂。因此,lambda接受由非标识符字符分隔的参数。因此,标题中的lambda等效于:
Conor O'Brien

哦。(自我提示:别再傻了。)
CalculatorFeline
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.