Tôi đang suy nghĩ về cách thuyết phục bản thân rằng máy Turing là một mô hình tính toán chung. Tôi đồng ý rằng cách xử lý tiêu chuẩn của luận án Church-Turing trong một số sách giáo khoa tiêu chuẩn, ví dụ Sipser, là không đầy đủ. Dưới đây là một bản phác thảo về cách tôi có thể đi từ máy Turing sang ngôn ngữ lập trình dễ nhận biết hơn.
Hãy xem xét một ngôn ngữ lập trình có cấu trúc khối với if
và các while
câu lệnh, với các hàm và chương trình con được xác định không đệ quy , với các biến ngẫu nhiên boolean được đặt tên và các biểu thức boolean chung, và với một mảng boolean không liên kết duy nhất tape[n]
với một con trỏ mảng số nguyên n
có thể tăng hoặc giảm, n++
hoặc n--
. Con trỏ n
ban đầu bằng không và mảng tape
ban đầu đều bằng không. Vì vậy, ngôn ngữ máy tính này có thể giống như C hoặc Python, nhưng nó rất hạn chế trong các loại dữ liệu của nó. Trên thực tế, chúng bị giới hạn đến mức chúng ta thậm chí không có cách nào để sử dụng con trỏ n
trong biểu thức boolean. Giả sử rằngtape
chỉ vô hạn ở bên phải, chúng ta có thể khai báo một con trỏ dưới "lỗi hệ thống" nếu n
là âm. Ngoài ra, ngôn ngữ của chúng tôi có một exit
câu lệnh với một đối số, để đưa ra câu trả lời boolean.
Sau đó, điểm đầu tiên là ngôn ngữ lập trình này là ngôn ngữ đặc tả tốt cho máy Turing. Bạn có thể dễ dàng thấy rằng, ngoại trừ mảng băng, mã chỉ có nhiều trạng thái có thể có: Trạng thái của tất cả các biến được khai báo và dòng thực thi hiện tại và ngăn xếp chương trình con của nó. Cái sau chỉ có một lượng trạng thái hữu hạn vì các hàm đệ quy không được phép. Bạn có thể tưởng tượng một "trình biên dịch" tạo ra một máy Turing "thực tế" từ một mã thuộc loại này, nhưng các chi tiết về điều đó không quan trọng. Vấn đề là chúng ta có một ngôn ngữ lập trình với cú pháp khá tốt, nhưng các kiểu dữ liệu rất nguyên thủy.
Phần còn lại của quá trình xây dựng là chuyển đổi ngôn ngữ này thành ngôn ngữ lập trình dễ sống hơn với danh sách hữu hạn các chức năng thư viện và các giai đoạn tiền biên dịch. Chúng tôi có thể tiến hành như sau:
Với trình biên dịch trước, chúng ta có thể mở rộng kiểu dữ liệu boolean thành bảng chữ cái ký hiệu lớn hơn nhưng hữu hạn như ASCII. Chúng ta có thể giả định rằng tape
có các giá trị trong bảng chữ cái lớn hơn này. Chúng ta có thể để lại một điểm đánh dấu ở đầu băng để ngăn dòng chảy con trỏ và một điểm đánh dấu có thể di chuyển ở cuối băng để ngăn TM vô tình trượt đến vô cực trên băng. Chúng ta có thể thực hiện các hoạt động nhị phân tùy ý giữa các ký hiệu và chuyển đổi sang boolean cho if
và các while
câu lệnh. (Trên thực tế if
cũng có thể được thực hiện while
nếu không có sẵn.)
kkiik
Chúng tôi chỉ định một băng là "bộ nhớ" có giá trị ký hiệu và các băng khác là "các thanh ghi" hoặc "biến" có giá trị nguyên. Chúng tôi lưu trữ các số nguyên trong nhị phân nhỏ cuối cùng với các dấu chấm dứt. Trước tiên chúng tôi thực hiện bản sao của một thanh ghi và giảm nhị phân của một thanh ghi. Kết hợp điều đó với sự tăng giảm của con trỏ bộ nhớ, chúng ta có thể thực hiện tìm kiếm truy cập ngẫu nhiên của bộ nhớ ký hiệu. Chúng ta cũng có thể viết các hàm để tính toán cộng và nhân số nguyên. Không khó để viết một hàm cộng nhị phân với các phép toán bitwise và hàm nhân 2 với dịch chuyển trái. (Hoặc thay đổi thực sự đúng, vì nó là endian nhỏ.) Với các nguyên hàm này, chúng ta có thể viết một hàm để nhân hai thanh ghi bằng thuật toán nhân dài.
Chúng ta có thể sắp xếp lại băng nhớ từ mảng ký hiệu symbol[n]
một chiều thành mảng ký hiệu hai chiều symbol[x,y]
bằng công thức n = (x+y)*(x+y) + y
. Bây giờ chúng ta có thể sử dụng mỗi hàng của bộ nhớ để thể hiện một số nguyên không dấu trong hệ nhị phân với ký hiệu kết thúc, để có được bộ nhớ có giá trị một chiều, truy cập ngẫu nhiên, có giá trị nguyên memory[x]
. Chúng ta có thể thực hiện đọc từ bộ nhớ đến một thanh ghi số nguyên và ghi từ một thanh ghi vào bộ nhớ. Nhiều tính năng hiện có thể được thực hiện với các chức năng: Số học đã ký và dấu phẩy động, chuỗi ký hiệu, v.v.
Chỉ có một cơ sở cơ bản hơn yêu cầu nghiêm ngặt một bộ biên dịch trước, cụ thể là các hàm đệ quy. Điều này có thể được thực hiện với một kỹ thuật được sử dụng rộng rãi để thực hiện các ngôn ngữ được dịch. Chúng tôi gán cho mỗi hàm cấp cao, hàm đệ quy một chuỗi tên và chúng tôi sắp xếp mã cấp thấp thành một while
vòng lặp lớn duy trì một ngăn xếp cuộc gọi với các tham số thông thường: điểm gọi, hàm được gọi và danh sách các đối số.
Tại thời điểm này, việc xây dựng có đủ các tính năng của ngôn ngữ lập trình cấp cao mà chức năng tiếp theo là chủ đề của ngôn ngữ lập trình và trình biên dịch hơn là lý thuyết CS. Thật dễ dàng để viết một trình giả lập Turing-machine bằng ngôn ngữ phát triển này. Nó không chính xác dễ dàng, nhưng chắc chắn là tiêu chuẩn, để viết một trình biên dịch tự cho ngôn ngữ. Tất nhiên bạn cần một trình biên dịch bên ngoài để tạo TM bên ngoài từ một mã trong ngôn ngữ giống như C hoặc Python này, nhưng điều đó có thể được thực hiện bằng bất kỳ ngôn ngữ máy tính nào.
Lưu ý rằng việc triển khai phác thảo này không chỉ hỗ trợ cho luận điểm Church-Turing của các nhà logic cho lớp chức năng đệ quy, mà còn cả luận điểm Church-Turing mở rộng (nghĩa là đa thức) khi áp dụng cho tính toán xác định. Nói cách khác, nó có chi phí đa thức. Trên thực tế, nếu chúng ta được tặng một máy RAM hoặc (sở thích cá nhân của tôi) một TM băng cây, điều này có thể được giảm xuống trên tổng thể polylogarithmic để tính toán nối tiếp với bộ nhớ RAM.