每个表达式都有意义的编程语言


23

根据建议,我将从Stack Overflow重新发布。

最近,我一直在考虑以下问题。

考虑标准“ Hello world!”的代码。程序:

main()
{
    printf("Hello World");

}

现在,此代码中的几乎任何更改都将使其完全无用,实际上,几乎所有更改都会阻止代码进行编译。例如:

main(5
{
    printf("Hello World");

}

现在到实际的问题。是否存在一种编程语言,其中每种符号的可能组合(即每种表达式)都有意义?我尝试考虑某种解决方案,并提出了两种解决方案:

  1. 后缀数量有限。基本上,在编写任何代码之前,所有变量都已经定义好了,您只需要使用它们。从理论上讲,您可以通过形成许多简单程序的链来执行任意数量的操作,每个简单程序将结果馈给其他程序。代码可以用后缀符号表示为一系列字符。

  2. “ Postfix”带有一堆变量。变量存储在堆栈中;每个操作从顶部开始取两个变量,并将结果放在它们的位置。程序在到达最后一个操作或变量时结束。

我个人讨厌这两个。它们不仅局限,而且优雅。它们甚至不是真正的解决方案,更像是变通方法,本质上是将某些工作“外包”到外部流程中。

有谁有其他想法如何解决这个问题?


48
由于编译器,创建一个新的编译器,其工作原理如下:给定的源,把它传递给。如果对它感到满意并生成了一个可执行文件,那么就是这样,但是如果抱怨,则输出一个打印出可执行文件的可执行文件。编译器接受每个字符串作为有效程序。C 'C C C C 'CCsCCCYou are a bimbo.C
Andrej Bauer

1
BF需要一个匹配的[ ]命令(根据Wiki页面)。我的想法是查看CPU操作码。但是即使那样,某些模式也可能会产生问题(例如,如果操作码是3位,但是您的程序只有2位。)除了这个可能会填充一些额外的0位的问题外,任何人都可以想到带有满足要求“每个字符串都是有效程序”的完整操作码集。也许没有意义,但仍然有效。
Ran G.

1
让您的硬件成为具有64k RAM的Z-80 CPU。编写一个仅将ASCII编码的源代码复制到64k内存中的编译器(必要时将其截断或填充为零)。该编译器从不给出语法错误。
Ben Crowell

1
@RanG。我认为,处理任何位流并将其固定为给定处理器的目标代码有效位的“编译器”将满足OP的要求。即使对于具有复杂指令集(如x86)的系统,这也不会很困难。几年前,我读过一篇关于随机字节作为x86程序的有效性的论文,发现x86实际上比作者最初预期的要健壮得多。
otakucode

2
没有更多条件,这个问题就很无聊:安德烈(Andrej)的评论和戴维(David)的回答给出了“平凡的”答案。您必须更精确地确定想要的内容。
拉斐尔

Answers:


31

Redcode是Codewar背后的汇编语言,被明确编写为几乎没有停止指令,因为代码通常在最终发布之前就被弄乱了,并且停止的机会越多,游戏的趣味性就越小。

在实践中,您几乎看不到这样的语言,因为我们不仅希望程序运行,而且希望程序以期望的方式运行。如果您可以打错字并更改程序的运行方式,则它必须可以接受地接近原始的预期行为,否则程序员会感到沮丧。

通过使用自然语言而不是形式语言来进行此类操作有一定的先例,但是当您将其与形式语言的使用进行比较时,这并不是我所说的大领域。如果您对这样的编程语言感兴趣,自然语言处理社区就是我想要的地方。

您可以研究的另一个领域是遗传学。几乎没有完全无效的遗传序列。其中很多在复制方面不是很有效,但无效的复制品却很少。


1
遗传学似乎不是一个很好的例子。说到有效还是无效,您是在谈论复制吗?因为当然,对于唯一可能的指令是的语言,每个字符串都是有效的程序replicate this string。但是,它并不是真正有意义的编程语言,因为它离Turing Complete还差得很远。
2015年

2
@tel:Cort可能在谈论通过mRNA而不是复制进行蛋白质合成。几乎所有的基因序列都可以被转录,然后放入蛋白质合成机制中:产生的蛋白质是否足够稳定,以至于在构建完成时尚未降解,如果可以的话,它是否对有机体是另一回事...
Steve Jessop

3
遗传密码不是自我复制的密码。(通常)是蛋白质的代码。该蛋白质是否有用通常是一个不同的问题。当然,它变得更加有趣。遗传序列中的某些“代码”最终更像是一条“沿着下面几行编码的指令”,您有时应该忽略它。有各种各样很酷的“程序”,细胞和病毒已经写成可以互相对抗。
乔尔2015年

TECO是另一个真实示例。
cjm 2015年

1
@cjm哇。“ API的完成不是在您添加完所有内容之后,而是在您完成所有内容提取之后。” 除非您是TECO,否则在用尽所有字符来分配含义时就已经完成了。
Cort Ammon-恢复莫妮卡2015年

16

通用图灵机的思想就是使用这种“编程语言”:将图灵机编码为自然数,例如以二进制表示,这样每个自然数都表示图灵机,即程序。用这种语言,每个零和一的字符串都是一个程序。

nn

我敢肯定,还有一些深奥的编程语言,其中每个字符串都是程序。但是,如果您只是想索取这些清单,我认为您的问题不在这里。


13

扩展编程语言,使每个表达式都有意义,这始终是可能的,但并不有趣。例如,您可以将含义“不做任何事”分配给原始语言拒绝的任何表达式。

设计一种编程语言,使每个表达式都以“可以执行它”的方式有意义。一种好的编程语言不仅是猴子可以在键盘上打字并编写有效程序的语言,而且是程序员可以轻松编写其打算编写的程序的语言。编写有效的程序不是编程的困难部分:困难的部分是编写执行预期功能的程序。在这方面,拒绝明显不正确的程序非常有帮助。

解决此问题的另一种方法是完全定义所有可能输入的语义,包括指定应该为每个输入生成哪些编译时,加载时或运行时错误(如果有)。也就是说,“ Syntax error at line 42在标准错误流上打印后中止程序”是该语言定义的语义的一部分。每个表达“都有道理”,因为它具有定义的含义。这是有用的意思吗?也许-毕竟,如果程序显然是错误的,则拒绝它很有用。


12

请查看Jot,这是一种基于组合逻辑的图灵完备语言,其中每个0和1(包括一个空序列)序列都是有效程序。


2
这不是计算机科学的答案。
拉斐尔

2
@Abdulrhman直接定义二进制字符串和自然数之间的双射是很简单的。因此,您可以根据需要将任何程序编码为自然数。
CodesInChaos

7
@Raphael请详细说明或提出答案的改进,如果您提出批评的理由,我们将很乐意加以改进。
PetrPudlák2015年

+1,对于基于自然数的虚拟编程语言,我将给出类似的答案,但这是相似的。据我所知是没有编程(实际使用),它有这个功能,但可以使用只是数字一个构建,可以说,其中每组合具有意义(充当运营商以及操作数),这是关键的
尼科斯·M.

8

一个很好的例子是空白。在适当的语言中,运算符的任何组合均有效。运算符是空格,制表符和换行符(特别是“ \ n”)。所有其他字符均视为注释

这个答案,实际上是您的问题(以及整个网页)都是有效的空白程序的示例(尽管它们可能没有做任何特别有趣的事情)。


我只是在发布我的脑力激荡答案之后才考虑这个问题(因为它是正确的,所以您会更好),但是我想知道-空程序还是程序吗?(即,如果整个文件流中缺少这三个字符)。-就像,如果我的汽车缺少所有使它成为汽车的东西,那它还会是汽车吗?
BrainSlugs83

这不是计算机科学的答案。(另外,“每个空白字符串”!=“每个字符串”。)
拉斐尔

2
@Raphael:但是所有可能的字符串(包括不包含空格的字符串)都是有效的空格程序-请注意,不是空格的任何字符都只是空格编程语言中的注释
slebetman 2015年

2
@slebetman您从字面上解释了我的括号中的评论。我说的是不成对的循环令牌。空格中的一些类似问题可能是:在没有事先调用的情况下返回是否有效?(编码为[LF][Tab][LF])如果弹出一个空堆栈会怎样?如果跳到未定义的标签会发生什么?如果定义重复标签会怎样?
CodesInChaos

7

我想谈谈许多海报提供的想法,即这种语言将是“无用的”。也许人类为了解决某些特定的任务而手动书写是没有用的。但是,尽管它是编程语言的大多数用例,但肯定不是唯一的用例。我想到了几种使用这种语言的用例,我们可以在这些领域中找到这种语言的示例。

首先科特阿蒙的典故遗传学现货:在问题(代程序变换)5)可以被看作是一个突变。这种操作在进化计算领域很普遍; 特别是遗传算法字符串进行这种转换,而遗传编程则转换程序。无论哪种情况,我们通常都希望为每种可能性分配含义,因为这将产生最紧凑的搜索空间。

遗传算法依赖某种形式的字符串评估函数。如果我们使用编程语言解释器作为评估函数,那么就会遇到一种情况,在这种情况下,为所有可能的字符串分配含义的编程语言很有用。在基因编程中,假设我们的评估函数是一种编程语言解释器,但是我们可以为程序选择各种表示形式;例如,许多系统都在抽象语法树上运行。如果我们选择字符串作为表示形式,那么我们将恢复与遗传算法相同的方案。

我们可能希望每个字符串都是有效程序的另一种情况是枚举程序时。这与CodesInChaos提到的双射有关,但是出于以下几个原因,我们可能更喜欢对字符串进行操作而不是对自然数进行操作:

  • 语言是否有某种结构,例如 我们可以为子字符串分配含义,当翻译成自然数时,这可能会丢失。在这种情况下,我们可能更喜欢使用字符串,以便在本地推理和转换子字符串,而不是将整个程序表示为一个数字。这类似于当每个位具有各自的含义时,我们可能更喜欢在int上使用按位运算而不是算术表达式。这基本上是进化方案的概括。
  • 我们可能想按需生成程序;例如,我们可能会开始执行一个完全不确定的程序,并且仅在(如果有)指令指针到达时(例如随机地)生成各个指令(例如字符)。这在算法信息论中很常见,该程序是图灵机磁带,其目的是表征随机生成的程序的行为。例如,我们可以将Solomonoff先于任意字符串,用具有随机磁带的通用图灵机输出该字符串的概率表示。

在示例语言方面,许多进化计算系统都基于堆栈语言,例如Push系列。这些倾向于允许任意标记流(我们可以将其表示为单个字符)。有时(例如与BrainSlugs83的Brainfuck示例一样),在平衡括号方面存在一些限制。但是,我们可以将其与自定界程序相关联,因为类似这样的字符串[可能不是有效程序,但它有效程序前缀。如果我们想象一个编译器/解释器从stdin读取源代码,那么它不会拒绝类似的字符串[,它只会等待更多输入后才能继续。

诸如二进制组合逻辑和二进制Lambda演算之类的语言直接源于算法信息论的研究。来自http://tromp.github.io/cl/cl.html

简约通用计算机的这种设计是出于我希望提出具体的Kolmogorov复杂度定义的目的,该定义研究单个对象的随机性。


2

真正的编程语言是为了向人们而不是计算机传达含义。由于很多有趣的文本,几乎都随机散乱地排列在“表演”周围,所以人们可以阅读乱七八糟的东西,并从中弄清楚,即使没有明显地注意到乱码。只需回想一下在文本中发现错别字和其他此类错误有多么困难。

像您所要求的那样的编程语言会使人们理解他们想要阅读的内容,而不是写下来的内容。在法律声明有限的语言中进行调试,而在这种情况下不会产生太多歧义,这已经足够困难。良好的语言会减少可能的解释,例如由于转置符号或错别字。出于同样的原因,自然语言也因其冗余而臭名昭著。


0

Brainfuck编程语言中几乎所有可能的二进制表达式都可以解释为程序。-也就是说,您可以使用一个完全好的程序,在其中键入一堆垃圾,并且仍然可以编译/解释,而不会出现任何问题。

编辑:原来,您必须匹配左方括号和右方括号,但是上面的情况是正确的。)

它通过以下两种简单方法来实现:

  1. 它理解的所有命令都是单个字节(例如,在BF中,它们都是单个ASCII字符*)。

  2. 它不理解的所有字符,将其作为注释丢弃。

编程语言是图灵完整的(意味着,它可以执行任何其他语言可以执行的任何操作)。

*: 事实证明并非所有BF命令都是单个ASCII字节-即必须匹配括号-因此,该语言不符合那里的第一个条件。-但是同时满足这两个标准的任何语言都可以满足OP的要求。


2
这不仅不能回答问题,也不是计算机科学的答案。
拉斐尔

1
您可以重新定义语言以某种明智的方式处理这些语言。例如,通过在程序的开头插入足够的开括号,并在程序的末尾插入括号以使其平衡。直接编写一个解释器来处理程序,就好像这些括号存在而无需实际重写程序一样。当然,用开括号来启动Brainfuck程序是没有用的,因为它会忽略所有匹配的闭括号。
CodesInChaos

1
OP的@Raphael问题是“是否存在一种编程语言,其中每种可能的符号组合(即每种表达式)都有意义?” -我的回答是“是的,这是一个非常接近的例子,这是背后的理论 ”。-除了为符合OP要求的一类语言制定确切的规则外,我不确定这里还有多少科学空间。您能否举一个例子或链接到您确切希望在这里看到的资源? - 谢谢。
BrainSlugs83

2
David和Gilles给出了计算机科学答案。他们探索原理,而不仅仅是说“ X语言(几乎)做到了”。如果阅读了他们的答案,您将了解到后一种形式的答案也很无聊。那不是你的错,而是OP的问题-作为计算机科学的问题很无聊。有一种错综复杂的感觉。
拉斐尔

可以很容易地“修复” BF,以便接受任何字符串:您只是假装]在源末尾有足够的字符来匹配所有不匹配的[s,而[在开头就有足够的字符来匹配所有不匹配的]。的语义[]可以很容易地改变,使他们等同于这一点。(例如,如果不匹配,][如果数据指针处的字节为零,则暂停执行。]在类似情况下,仅跳至程序的开始。)结果语言将是图灵完成的,并且可以接受任何字符串。
纳撒尼尔(Nathaniel)2013年
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.