Quining một thế giới nguyên sơ


16

Thử thách này dựa trên câu hỏi của Helka Homba Lập trình một thế giới nguyên sơ . Từ câu hỏi đó, định nghĩa của một chương trình nguyên sơ là:

Chúng ta hãy định nghĩa một chương trình nguyên sơ là một chương trình không có bất kỳ lỗi nào nhưng sẽ lỗi nếu bạn sửa đổi nó bằng cách xóa bất kỳ chuỗi con liền kề nào của N ký tự, trong đó 1 <= N < program length.

Ví dụ, chương trình ba ký tự Python 2

`8`

là một chương trình nguyên sơ ( cảm ơn, Sp ) bởi vì tất cả các chương trình do loại bỏ các chuỗi con có độ dài 1 gây ra lỗi (thực tế là lỗi cú pháp, nhưng bất kỳ loại lỗi nào cũng sẽ xảy ra):

8`
``
`8

và tất cả các chương trình dẫn đến việc loại bỏ các chuỗi con có độ dài 2 gây ra lỗi:

`
`

Ví dụ, `8nếu là một chương trình `8`không có lỗi thì sẽ không còn nguyên sơ vì tất cả các kết quả loại bỏ chuỗi con phải bị lỗi.

Ghi chú:

  • Trình biên dịch cảnh báo không được tính là lỗi.
  • Các chương trình con lỗi có thể nhận đầu vào hoặc đưa ra đầu ra hoặc làm bất cứ điều gì khác miễn là chúng không có vấn đề gì.

Nhiệm vụ của bạn là tạo ra một chương trình có độ dài khác không, in chính xác mã nguồn của chính nó, tuân theo các quy tắc cho một quine thích hợp và nguyên sơ.

Câu trả lời ngắn nhất tính theo byte cho mỗi ngôn ngữ sẽ thắng.


Tôi đang giả sử ngôn ngữ không có lỗi không thể cạnh tranh?
ATaco

@ATaco Thật không may là có. Các ngôn ngữ khác, chẳng hạn như lisp, có cấu trúc cú pháp theo cách mà làm cho một chương trình nguyên sơ hữu ích là không thể.
Shelvacu

RIP Thật / Nghiêm túc
ATaco

'Câu trả lời ngắn nhất tính theo byte cho mỗi ngôn ngữ sẽ thắng.' Tôi không chắc chắn về ngắn là biện pháp tốt nhất cho một chương trình nguyên sơ.
P. Siehr

@ P.Siehr Bạn muốn giới thiệu cái gì thay thế?
Shelvacu

Câu trả lời:


6

Haskell , 132 byte

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

Hãy thử trực tuyến!

Đây là một phần mở rộng của quine

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

hoạt động bằng cách nối chuỗi dữ liệu với một phiên bản được trích dẫn (bằng cách sử dụng show) của chính nó và in kết quả. Tuy nhiên, điều này không còn nguyên sơ vì bất kỳ ký tự nào trong chuỗi dữ liệu có thể được loại bỏ mà không bị lỗi và cũng có thể loại bỏ một phần $(++)<*>show$hoặc (++)<*>không bị phá vỡ chương trình.

Để khắc phục điều này, một chức năng in tùy chỉnh qđược xác định để kiểm tra độ dài của chuỗi đã cho và gọi failnếu nó ngắn hơn 132. Điều này bắt được việc loại bỏ bất kỳ chuỗi nào khỏi chuỗi dữ liệu và cả loại bỏ $(++)<*>show$hoặc (++)<*>, như trong cả hai trường hợp, kết quả là chuỗi được truyền đến qngắn hơn.

Trong qsố 132có thể được rút ngắn xuống còn 1, 13, 32hoặc 2, nhưng trong từng trường hợp một lần nữa failđược gọi.

Theo như tôi có thể nói, việc loại bỏ bất kỳ chuỗi con nào khác gây ra lỗi cú pháp hoặc kiểu, do đó chương trình thậm chí không biên dịch ở vị trí đầu tiên. (Hệ thống loại nghiêm ngặt của Haskell có ích ở đây.)

Chỉnh sửa: Cảm ơn Ørjan Johansen và Shelvacu đã chỉ ra một sai sót!


Tôi sợ fail[]|length x/=122có thể được gỡ bỏ. fail[]:[putStr x|length x==122]có thể làm việc tốt hơn
Ørjan Johansen

Argh, không, sau đó |length x==122có thể được gỡ bỏ. if length x==122 then putStr x else fail[]có lẽ?
Ørjan Johansen

@ RjanJohansen Bắt tốt, tôi đã có if then elsetrước đây nhưng nghĩ rằng tôi có thể rút ngắn nó.
Laikoni

2
putStr xcó thể trở thành p x, khi tôi thử hệ thống của mình chạy rất lâu trước khi tôi giết nó, tôi nghi ngờ đệ quy cuộc gọi đuôi được tối ưu hóa, vì vậy đó là một vòng lặp vô hạn. Tôi không biết đủ haskell để đưa ra bất kỳ đề xuất nào về cách khắc phục.
Shelvacu

@Shelvacu Rất tiếc. Đổi tên pthành qnên sửa nó.
Ørjan Johansen

4

Python 3 , 113 byte

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

Chúng tôi không thể dễ dàng sử dụng nhiều câu lệnh vì câu lệnh thứ hai có thể bị xóa, vì vậy chúng tôi bắt đầu bằng một câu lệnh biểu thức đơn:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

Để bảo vệ nó chống lại việc xóa chuỗi con, chúng tôi sử dụng open(1,"w").writethay vì print. Trong Python 3, writetrả về số lượng ký tự viết mà chúng tôi sẽ xác minh là 113để đảm bảo rằng không có phần nào của chuỗi bị xóa. Chúng tôi thực hiện điều này bằng cách tra cứu giá trị trả về trong từ điển {113:[]}và lặp lại kết quả với for[]in…:a, sẽ thất bại nếu chúng tôi không nhận được một lần lặp trống hoặc nếu forcâu lệnh bị xóa.


1
Bạn có thể đưa ra một lời giải thích về cách mã của bạn hoạt động?
Shelvacu

@Shelvacu Vâng, đã thêm.
Anders Kaseorg

3

Ruby, 78 byte

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

Tôi đã viết điều này khi tôi nghĩ về thử thách để đảm bảo nó có thể. Nó sử dụng cùng một "trình bao bọc" từ một trong những câu trả lời của tôi cho thử thách nguyên sơ ban đầu.

Giải trình:

  • eval(*[ expr ])

    Điều này đánh giá bất cứ mã nào trả về như một chương trình ruby. Điều này kiểm tra một cách hiệu quả rằng chuỗi mà trả về là một chương trình ruby ​​hợp lệ. Thuận tiện, các chương trình ruby ​​có thể để trống, hoặc chỉ bao gồm khoảng trắng.

    Toán tử "splat" *cho phép bạn sử dụng một mảng làm đối số cho hàm. Điều này cũng có nghĩa là nếu evalbị loại bỏ, chương trình kết quả là (*[ expr ]) , không phải là ruby ​​hợp lệ.

  • ($>.write( str )-78).chr

    $> là một biến ngắn cho STDOUT.

    $>.write(foo) ghi foo vào STDOUT và quan trọng là mã này sẽ trả về số byte được ghi.

    $>.write(foo)-78: Đây 78là độ dài của chương trình, và vì vậy nếu chương trình không được đọc sai, cũng sẽ là số byte được ghi. Do đó, trong trường hợp không thay đổi, điều này sẽ trả về số không.

    num.chrtrả về num dưới dạng ký tự, ví dụ 0.chrsẽ trả về một chuỗi chứa một byte null. Trong chương trình không bị thay đổi, điều này sẽ đưa ra một chuỗi có một byte null duy nhất eval, đây là một chương trình ruby ​​hợp lệ là không có op.

    Ngoài ra, chương trình có thể loại bỏ một chuỗi con sao cho nó chỉ eval(*[(78).chr])hoặc eval(*[(8).chr]), điều đó có nghĩa là hằng số không thể kết thúc với bất kỳ số nào (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95) vì chúng là mã ascii cho các chương trình ruby ​​một ký tự hợp lệ.

  • %{ str }

    Đây là một cú pháp ít được biết đến cho các chuỗi ký tự trong ruby. Lý do nó được sử dụng ở đây là các cặp cân bằng {}có thể được sử dụng trong chuỗi, có nghĩa là cú pháp này có thể chứa chính nó. Ví dụ, %{foo{bar}}giống như "foo{bar}".

  • (s=%{ dữ liệu })%s

    Điều này xác định biến slà dữ liệu của quine này, dưới dạng một chuỗi printf.

    Nhiệm vụ trong ruby ​​trả lại những gì đã được chỉ định, vì vậy điều này giống như việc gán đầu tiên svà sau đó chạys%s

    %trên một chuỗi là đường tổng hợp cho ruby ​​tương đương với nước rút. Dấu %shiệu cho thấy vị trí trong dữ liệu sẽ được nhúng vào.

    Bit mã này xác định phần dữ liệu của quine và nhúng nó vào trong chính nó để tạo mã đầy đủ.


3

ML chuẩn (MLton) , 204 182 189 byte

val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]

Hãy thử trực tuyến!

Đối với MLton, các chương trình SML đầy đủ là các biểu thức được phân tách và kết thúc bằng ;(ví dụ print"Hello";print"World";) hoặc khai báo bằng varfuntừ khóa (ví dụ var _=print"Hello"var _=print"World") trong đó _là một thẻ đại diện cũng có thể được thay thế bằng bất kỳ tên biến nào.

Tùy chọn đầu tiên là vô dụng đối với lập trình nguyên sơ bởi vì ;bản thân nó là một chương trình hợp lệ (không có gì, nhưng cũng không có lỗi). Vấn đề với cách tiếp cận thứ hai là các khai báo như var _=print"Hello"có thể được rút ngắn thành chỉ var _="Hello"(hoặc thậm chí var _=print) bởi vì khai báo có vartác dụng miễn là phía bên phải là một biểu thức hoặc giá trị SML hợp lệ (SML là ngôn ngữ chức năng, vì vậy các hàm có thể dùng làm giá trị quá).

Tại thời điểm này, tôi đã sẵn sàng tuyên bố lập trình nguyên sơ trong SML là không thể, khi tình cờ tôi tình cờ tìm thấy mô hình khớp trong các tuyên bố val. Nó chỉ ra rằng cú pháp khai báo không phải là , val <variable_name> = <expression>nhưng val <pattern> = <expression>trong đó một mẫu có thể bao gồm các tên biến, hằng và hàm tạo. Khi printhàm có kiểu string -> unit, chúng ta có thể sử dụng khớp mẫu trên unit-value ()để thực thi rằng hàm in thực sự được áp dụng cho chuỗi : val()=print"Hey". Với phương pháp này, loại bỏ một trong hai printhoặc "Hey"kết quả trong một Pattern and expression disagree-error.

Với cách in nguyên sơ này trong tay, bước tiếp theo là viết một quine, trước khi cuối cùng một số bảo vệ tiết kiệm hơn cần được thêm vào. Trước đây tôi đã sử dụng một kỹ thuật quine SML dễ dàng (xem lịch sử sửa đổi ), nhưng Anders Kaseorg đã chỉ ra một cách tiếp cận khác có thể lưu một số byte trong trường hợp của anh ta. Nó sử dụng String.toStringhàm tích hợp để xử lý thoát chuỗi và có dạng chung <code>"<data>", trong đó "<data>"là một chuỗi thoát codetrước đó:

val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"

Đây là một quine làm việc nhưng chưa nguyên sơ. Trước hết, Anders Kaseorg phát hiện ra rằng MLton chấp nhận một trích dẫn "là mã mà không tạo ra lỗi, điều đó có nghĩa là chúng ta không thể có mã kết thúc bằng một trích dẫn như trên. Cách ngắn nhất để ngăn chặn điều này là bọc mọi thứ sau val()=một cặp ngoặc đơn, tuy nhiên sau đó mã có thể được giảm xuống val()=(). Cách ngắn nhất thứ hai tôi tìm thấy là sử dụng val()=hd[ ... ], đó là chúng tôi gói mọi thứ vào một danh sách và trả về phần tử đầu tiên của nó để làm cho trình kiểm tra kiểu hài lòng.

Để đảm bảo rằng không có phần nào của chuỗi dữ liệu có thể bị xóa mà không bị chú ý, kết hợp khớp mẫu trong val-declarations lại có ích: Độ dài của chuỗi cuối cùng được in (và do đó độ dài chương trình) phải bằng 195, vì vậy chúng ta có thể viết let val t=... val 195=size t in print t endtrong phần fntrừu tượng thay vì print(...). Loại bỏ một phần của chuỗi kết quả có độ dài nhỏ hơn 189, do đó gây ra Bindngoại lệ.

Vẫn còn một vấn đề: toàn bộ val 195=size tkiểm tra có thể bị hủy bỏ. Chúng tôi có thể ngăn chặn điều này bằng cách mở rộng kiểm tra để khớp trên một tuple : val t=... val(216,u)=(n+size t,t)in print u end, ví dụ như loại bỏ kết quả kiểm tra trong một biến không liên kết u.

Nhìn chung, điều này mang lại giải pháp 195 byte sau đây:

val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]

Áp dụng thủ thuật đánh gôn bằng cách sử dụng các tên biến toán tử như !, $%thay vì n, tuđể tiết kiệm một số khoảng trắng (xem mẹo này ) dẫn đến phiên bản 182 byte cuối cùng.

Tất cả các loại bỏ chuỗi con khác mà không được nêu rõ trong phần giải thích sẽ dẫn đến lỗi cú pháp hoặc kiểu.

Chỉnh sửa 1: length(explode t) chỉ là size t.
Chỉnh sửa 2: Cảm ơn Anders Kaseorg vì một cách tiếp cận khác nhau và chỉ ra một "lỗ hổng".


−2 byte bằng cách viết "\""trực tiếp và sử dụng String.toStringđể thoát.
Anders Kaseorg

Đợi đã, điều này thật kinh khủng: MLton dường như chấp nhận chương trình ", tạo ra đầu ra trống ( TIO ).
Anders Kaseorg

@AndersKaseorg Huh, thật lạ. Tuy nhiên, có thể khắc phục vấn đề này bằng cách sử dụng một vấn đề khác let ... in ... end.
Laikoni

@AndersKaseorg Tôi đã khắc phục sự cố, hy vọng không giới thiệu "lỗ hổng" mới.
Laikoni

Trên thực tế tôi đã xem xét MLton chấp nhận "như một chương trình và có vẻ như lỗi đã được sửa trong cam kết này , vì vậy có thể 182 hoặc 180 của tôi vẫn ổn miễn là bạn chỉ định phiên bản Git chưa phát hành của MLton.
Anders Kaseorg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.