像 Coq 这样的非图灵完备语言有哪些实际限制?

发布于 2024-09-14 16:08:18 字数 253 浏览 2 评论 0原文

由于存在非图灵完整的语言,并且鉴于我没有在大学学习计算机科学,有人可以解释一些图灵不完整的语言(例如 Coq) 不能做什么?

或者完整性/不完整性是否没有真正的实际兴趣(即在实践中没有多大区别)?

编辑 - 我正在寻找答案,由于 X,您无法用非图灵完整语言构建哈希表,或类似的东西!

As there are non-Turing complete languages out there, and given I didn't study Comp Sci at university, could someone explain something that a Turing-incomplete language (like Coq) cannot do?

Or is the completeness/incompleteness of no real practical interest (i.e. does it not make much difference in practice)?

EDIT - I'm looking for an answer along the lines of you cannot build a hash table in a non-Turing complete language due to X, or something like that!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

獨角戲 2024-09-21 16:08:18

首先,我假设您已经听说过丘奇-图灵论文,其中指出任何我们所说的“计算”都可以用图灵机(或任何其他等效模型)来完成。因此,图灵完备的语言是一种可以表达任何计算的语言。相反,图灵不完备语言是一种无法表达某些计算的语言。

好吧,这不是很有信息。让我举个例子。在任何图灵不完整语言中都不能做一件事:您无法编写图灵机模拟器(否则您可以在模拟图灵机上对任何计算进行编码)。

好吧,这仍然信息量不大。真正的问题是,什么有用程序不能用图灵不完备的语言编写?好吧,没有人提出“有用程序”的定义,其中包括某人出于有用目的而编写的所有程序,并且不包括所有图灵机计算。所以设计一种图灵不完备的语言,可以用它编写所有有用的程序仍然是一个非常长期的研究目标。

现在有几种非常不同的图灵不完备语言,它们的不同之处在于它们不能做什么。然而,有一个共同的主题。如果您正在设计一种语言,有两种主要方法可以确保该语言是图灵完备的:

  • 要求该语言具有任意循环 (while) 和动态内存分配 (< code>malloc)

  • 要求语言支持任意递归函数

让我们看一些非图灵完备语言的示例,尽管有些人可能将其称为编程语言。

  • FORTRAN 的早期版本没有动态内存分配。您必须提前计算出您的计算需要多少内存并进行分配。尽管如此,FORTRAN 曾经是使用最广泛的编程语言。

    明显的实际限制是您必须在运行程序之前预测其内存需求。如果输入数据的大小没有预先限制,这可能很困难,并且可能是不可能的。当时,提供输入数据的人通常是编写程序的人,所以这没什么大不了的。但对于今天编写的大多数程序来说,情况并非如此。

  • Coq 是一种为证明定理而设计的语言。现在证明定理和运行程序密切相关,所以你可以在 Coq 中编写程序就像你证明一个定理一样。直观上,定理“A 蕴涵 B”的证明是一个以定理 A 的证明为参数并返回定理 B 的证明的函数。

    由于系统的目标是证明定理,所以不能让程序员编写任意函数。想象一下,该语言允许您编写一个愚蠢的递归函数,该函数只是调用自身(选择使用您最喜欢的语言的行):

    theorem_Boom(theorem_Aa){returnboom(a); }
    让rec繁荣(a:定理_A):定理_B =繁荣(a)
    def 繁荣(a): 繁荣(a)
    (定义(繁荣a)(繁荣a))
    

    你不能让这样一个函数的存在让你相信 A 蕴含 B,否则你将能够证明任何东西而不仅仅是真正的定理!因此 Coq(以及类似的定理证明者)禁止任意递归。当您编写递归函数时,您必须证明它总是终止,这样每当您在定理 A 的证明上运行它时,您就知道它将构造定理 B 的证明。

    Coq 的直接实际限制是您无法编写任意递归函数。由于系统必须能够拒绝所有非终止函数,因此停止问题的不确定性(或者更一般地说赖斯定理)确保也存在被拒绝的终止函数。另一个实际困难是您必须帮助系统证明您的函数确实终止。

    有很多正在进行的研究,旨在使证明系统更像编程语言,同时又不影响其保证,即如果您有一个从 A 到 B 的函数,那么它与 A 暗示 B 的数学证明一样好。扩展系统接受更多的终止函数是研究课题之一。其他扩展方向包括处理输入/输出和并发等“现实世界”问题。另一个挑战是让这些系统对普通人来说是可以访问的(或者也许让普通人相信它们实际上是可以访问的)。

  • 同步编程语言是为实时系统编程而设计的语言,即系统程序必须在少于n个时钟周期内做出响应。它们主要用于关键任务系统,例如车辆控制或信号发送。这些语言对程序运行所需的时间以及可以分配的内存量提供了强有力的保证。

    当然,与这种强有力的保证相对应的是,您不能编写无法提前预测内存消耗和运行时间的程序。特别是,您不能编写内存消耗或运行时间取决于输入数据的程序。

  • 有许多专门的语言甚至不尝试成为编程语言,因此可以轻松地远离图灵完整性:正则表达式,数据语言,大多数标记语言,......

顺便说一下,Douglas Hofstadter 写了几本关于计算的非常有趣的科普书籍,特别是 哥德尔、埃舍尔、巴赫:永恒的金辫。我不记得他是否明确讨论了图灵不完备性的局限性,但阅读他的书肯定会帮助你理解更多的技术材料。

First, I assume you've already heard of the Church-Turing thesis, which states that anything we call “computation” is something that can be done with a Turing machine (or any of the many other equivalent models). So a Turing-complete language is one in which any computation can be expressed. Conversely, a Turing-incomplete language is one in which there is some computation that cannot be expressed.

Ok, that wasn't very informative. Let me give an example. There is one thing you cannot do in any Turing-incomplete language: you can't write a Turing machine simulator (otherwise you could encode any computation on the simulated Turing machine).

Ok, that still wasn't very informative. the real question is, what useful program cannot be written in a Turing-incomplete language? Well, nobody has come up with a definition of “useful program” that includes all the programs someone somewhere has written for a useful purpose, and that doesn't include all Turing machine computations. So designing a Turing-incomplete language in which you can write all useful programs is still a very long-term research goal.

Now there are several very different kinds of Turing-incomplete languages out there, and they differ in what they can't do. However there is a common theme. If you're designing a language, there are two major ways to ensure that the language will be Turing-complete:

  • require that the language has arbitrary loops (while) and dynamic memory allocation (malloc)

  • require that the language supports arbitrary recursive functions

Let's look at a few examples of non-Turing complete languages that some people might nonetheless call programming languages.

  • Early versions of FORTRAN did not have dynamic memory allocation. You had to figure out in advance how much memory your computation would need and allocate that. In spite of that, FORTRAN was once the most widely used programming language.

    The obvious practical limitation is that you have to predict the memory requirements of your program before running it. That can be hard, and can be impossible if the size of the input data is not bounded in advance. At the time, the person feeding the input data was often the person who had written the program, so it wasn't such a big deal. But that's just not true for most programs written today.

  • Coq is a language designed for proving theorems. Now proving theorems and running programs are very closely related, so you can write programs in Coq just like you prove a theorem. Intuitively, a proof of the theorem “A implies B” is a function that takes a proof of theorem A as an argument and returns a proof of theorem B.

    Since the goal of the system is to prove theorems, you can't let the programmer write arbitrary functions. Imagine the language allowed you to write a silly recursive function that just called itself (pick the line that uses your favorite language):

    theorem_B boom (theorem_A a) { return boom(a); }
    let rec boom (a : theorem_A) : theorem_B = boom (a)
    def boom(a): boom(a)
    (define (boom a) (boom a))
    

    You can't let the existence of such a function convince you that A implies B, or else you would be able to prove anything and not just true theorems! So Coq (and similar theorem provers) forbid arbitrary recursion. When you write a recursive function, you must prove that it always terminates, so that whenever you run it on a proof of theorem A you know that it will construct a proof of theorem B.

    The immediate practical limitation of Coq is that you cannot write arbitrary recursive functions. Since the system must be able to reject all non-terminating functions, the undecidability of the halting problem (or more generally Rice's theorem) ensures that there are terminating functions that are rejected as well. An added practical difficulty is that you have to help the system to prove that your function does terminate.

    There is a lot of ongoing research on making proof systems more programming-language-like without compromising their guarantee that if you have a function from A to B, it's as good as a mathematical proof that A implies B. Extending the system to accept more terminating functions is one of the research topics. Other extension directions include coping with such “real-world” concerns as input/output and concurrency. Another challenge is to make these systems accessible to mere mortals (or perhaps convince mere mortals that they are in fact accessible).

  • Synchronous programming languages are languages designed for programming real-time systems, that is, systems where the program must respond in less than n clock cycles. They are mainly used for mission-critical systems such as vehicle controls or signaling. These languages offer strong guarantees on how long a program will take to run and how much memory it may allocate.

    Of course, the counterpart of such strong guarantees is that you can't write programs whose memory consumption and running time you're not able to predict in advance. In particular, you can't write a program whose memory consumption or running time depends on the input data.

  • There are many specialized languages that don't even try to be programming languages and so can remain comfortably far from Turing completeness: regular expressions, data languages, most markup languages, ...

By the way, Douglas Hofstadter wrote several very interesting popular science books about computation, in particular Gödel, Escher, Bach: an Eternal Golden Braid. I don't recall whether he explicitly discusses limitations of Turing-incompleteness, but reading his books will definitely help you understand more technical material.

≈。彩虹 2024-09-21 16:08:18

不适合像 Coq 这样的语言的一类重要问题是那些终止被推测或难以证明的问题。你可以在数论中找到很多例子,也许最著名的是 Collat​​z 猜想

function collatz(n)
  while n > 1
    if n is odd then
      set n = 3n + 1
    else
      set n = n / 2
    endif
 endwhile

这个限制导致必须在 Coq 中以不太自然的方式表达此类问题,例如,添加一个“fuel”参数,如下所示,这样您就可以表达“collat​​z up to n steps”。

An important class of problems that are a bad fit for languages such as Coq is those whose termination is conjectured or hard to prove. You can find plenty of examples in number theory, maybe the most famous is the Collatz conjecture

function collatz(n)
  while n > 1
    if n is odd then
      set n = 3n + 1
    else
      set n = n / 2
    endif
 endwhile

This limitation leads to have to express such problems in a less natural way in Coq, for example, adding a "fuel" parameter as done below so you can express "collatz up to n steps".

谜兔 2024-09-21 16:08:18

最直接的答案是:非图灵完备的机器/语言不能用来实现/模拟图灵机。这来自图灵完备性的基本定义:如果一种机器/语言能够实现/模拟图灵机,那么它就是图灵完备的。

那么,有什么实际意义呢?嗯,有证据表明任何可以证明是图灵完备的东西都可以解决所有可计算的问题。根据定义,这意味着任何不完整的事物都存在一些它无法解决的可计算问题的缺陷。这些问题是什么取决于缺少哪些功能导致系统非图灵完备。

例如,如果一种语言不支持循环或递归,或者隐式循环不能是图灵完备的,因为图灵机可以被编程为永远运行。因此,该语言无法解决需要循环的问题。

另一个例子是,如果一种语言不支持列表或数组(或者允许您使用文件系统来模拟它们),那么它就无法实现图灵机,因为图灵机需要对内存进行任意随机访问。因此,该语言无法解决需要任意随机访问内存的问题。

因此,缺少将语言归类为非图灵完备的功能实际上限制了该语言的实用性。所以答案是,这取决于:什么使该语言成为非图灵完备的?

The most direct answer is: a machine/language that is not Turing complete cannot be used to implement/simulate Turing machines. This comes from the basic definition of Turing completeness: a machine/language is turing complete if it can implement/simulate Turing machines.

So, what are the practical implications? Well, there is a proof that anything that can be shown to be turing complete can solve all computable problems. Which by definition means that anything that is not turing complete has the handicap that there are some computable problems that it can't solve. What those problems are depends on what features are missing that makes the system non-Turing complete.

For example if a language doesn't support looping or recursion or implicitly loops cannot be Turing complete because Turing machines can be programmed to run forever. Consequently that language cannot solve problems requiring loops.

Another example is if a language doesn't support lists or arrays (or allow you to emulate them for example using the filesystem) then it cannot implement a Turing machine because Turing machines require arbitrary random access to memory. Consequently that language cannot solve problems requiring arbitrary random access to memory.

So, the feature that is missing that classifies a language to be non-Turing complete is the very thing that practically limits the usefulness of the language. So the answer is, it depends: what makes the language non-Turing complete?

心在旅行 2024-09-21 16:08:18

您无法编写模拟图灵机的函数。您可以编写一个函数来模拟图灵机的 2^128(或 2^2^2^2^128 步骤)并报告图灵机是否接受、拒绝,或跑的时间超过允许的步数。

由于“在实践中”,在计算机能够模拟图灵机 2^128 步骤之前,您已经走得很远了,因此可以公平地说,图灵不完备性在“实践中”并没有产生太大的影响。

You can't write a function that simulates a Turing machine. You can write a function that simulates a Turing machine for 2^128 (or 2^2^2^2^128 steps) and reports whether the Turing machine accepted, rejected, or ran for longer than the allowed number of steps.

Since "in practice" you will be long gone before your computer can simulate a Turing machine for 2^128 steps, it's fair to say that Turing incompleteness does not make much of a difference "in practice".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文