Mẹo viết quines


30

Một là một chương trình tạo ra đầu ra giống hệt với mã nguồn của chương trình. Trên trang web này, chúng tôi thường chỉ quan tâm đến các giới hạn phù hợp (tại thời điểm viết, định nghĩa hiện tại là "một phần của đầu ra được mã hóa bởi một phần khác của chương trình").

Bạn có lời khuyên nào cho việc viết các câu hỏi thích hợp, hoặc các chương trình có đặc tính giống như quine? Như thường lệ, mỗi mẹo nên ở một câu trả lời khác nhau.

tips  quine 

Câu trả lời:


14

Sử dụng evalđể giảm nhu cầu sao chép mã

Phần lớn các quines yêu cầu hai bản sao của mã; một để được thực thi, một như dữ liệu. Điều này có thể kết thúc gấp đôi độ dài của mã nguồn, khiến cho việc duy trì khó khăn hơn và làm xấu đi điểm số nếu bạn đang viết bài kiểm tra cho một cuộc thi .

Hợp nhất hai bản sao có nghĩa là một phần thông tin cần được sử dụng cho hai mục đích. Cố gắng coi mã của bạn là dữ liệu thường là không thể, và thường được coi là gian lận khi có. Tuy nhiên, việc xử lý dữ liệu dưới dạng mã có thể được thực hiện bằng nhiều ngôn ngữ thông qua việc sử dụng nội dung, thường được gọi là eval. Như vậy, về cơ bản, quine của bạn bao gồm việc lưu trữ phần chính của quine của bạn trong một biến (để bạn có thể tham chiếu đến nó nhiều lần), sau đó đánh giá biến đó.

Đây là một ví dụ về cách thức hoạt động của nó (ví dụ này được viết bằng Python, nhưng một cái gì đó tương tự hoạt động trong nhiều ngôn ngữ khác):

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

2
@QPaysTaxes: Tôi đặt mục tiêu làm cho mã của mình ở đây có thể đọc và duy trì được khi điều kiện chiến thắng cho phép. Thật không may, điều đó vẫn không thể phân biệt được với tiếng ồn dòng ASCII (hoặc chỉ là tiếng ồn đường truyền thông thường nếu tôi đang sử dụng Jelly) từ những người không quen sử dụng ngôn ngữ này.

14

Tận dụng định dạng chuỗi

Một trong những cách dễ nhất để tạo ra một quine là xác định một chuỗi, sau đó đặt chuỗi bên trong chính nó với định dạng chuỗi.

s='s=%r;print s%%s';print s%s

Vì vậy, trong ví dụ này, câu lệnh Python, chúng tôi khai báo một chuỗi có phần đầu tiên bằng với bất kỳ chuỗi nào trước chuỗi s=, sau đó chúng tôi cho phép chuỗi được chèn với định dạng %rvà cuối cùng chúng tôi đặt chuỗi sau chuỗi in và định dạng chuỗi. . Dòng mới theo dõi là bởi vì printin một dòng mới.

Vì vậy, mẫu thực sự là cái này, trong Python:

<A>'<A>%r<B>'<B>

Để mở rộng quine hiện có với nhiều mã hơn:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>

9

Hàm chuỗi

Trong một số ngôn ngữ, các đối tượng hàm (hoặc các cấu trúc tương đương) hoàn toàn lưu trữ mã nguồn của chúng và sẽ trả về nó khi được chuyển đổi thành một chuỗi. Điều này cho phép quines nhỏ gọn mà không cần sử dụng chuỗi eval . Một ví dụ đáng chú ý của một ngôn ngữ như vậy là JavaScript:

function f(){console.log(f+"f()")}f()

Mã này xác định và gọi một hàm fmà khi được gọi sẽ in mã nguồn của chính nó theo sau là một lệnh gọi đến chính nó. Phần duy nhất của chương trình cần được nhân đôi là lệnh gọi hàm f(). Tất nhiên, phần thân hàm có thể bao gồm một "tải trọng" mã tùy ý cũng sẽ được thực thi khi hàm được gọi.


Một phiên bản nhỏ gọn hơn của cùng một thủ thuật hoạt động trong các ngôn ngữ chơi gôn GolfScript :

{".~"}.~

CJam :

{"_~"}_~

Mỗi trong số các quine này trước tiên định nghĩa một khối mã ẩn danh (được đặt trong dấu ngoặc nhọn), hoạt động giống như một đối tượng hàm trong JavaScript: nó có thể được thực thi và nếu được xâu chuỗi, nó sẽ trả về mã nguồn của nó. Phần còn lại của mã ( .~trong GolfScript hoặc _~trong CJam) sau đó thực thi khối, trong khi để lại một bản sao của nó trên ngăn xếp. Mã bên trong khối sau đó đẩy một chuỗi lên ngăn xếp lặp lại mã bên ngoài khối. Khi trình thông dịch thoát, nó sẽ tự động xâu chuỗi và in mọi thứ còn lại trên ngăn xếp. Như với ví dụ JavaScript, các khối mã này cũng có thể được tạo để thực hiện và thực thi một tải trọng tùy ý của mã bổ sung mà không phải sao chép nó.


9

Sử dụng các dấu phân cách chuỗi mà lồng mà không thoát

Thông thường, một trong những phần khó nhất khi viết quine là bước thoát. Điều này là cần thiết trong hầu hết mọi quine; vấn đề là bạn đang lưu trữ dữ liệu bằng cách nào đó và bạn cần sao chép mã lưu trữ dữ liệu trong đầu ra của quine. Mã đó sẽ chứa một dạng dữ liệu thoát, vì vậy chương trình sẽ thấy một dạng không thoát, và bạn sẽ cần phải thoát lại nó.

Cách dễ nhất để xử lý bước không xác định là nếu các hình thức thoát và không thoát của dữ liệu chỉ khác nhau khi có hoặc không có các dấu phân cách chuỗi. Do đó, thoát là một vấn đề đơn giản của việc thêm một cặp dấu phân cách chuỗi mới xung quanh chuỗi. Thật không may, điều này rõ ràng chỉ có thể hoạt động nếu bản thân các dấu phân cách chuỗi có thể được thể hiện trong dữ liệu mà không thoát.

Perl là một ví dụ điển hình về ngôn ngữ mà thủ thuật này hoạt động. Mặc dù các dấu phân cách chuỗi thông thường của nó là "…"hoặc '…', các tổ ít được sử dụng q(…), cho phép loại quine này được viết:

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

Đây là một mã + dữ liệu quine. s///là một hoạt động thay thế chuỗi regex; chúng tôi sử dụng 0làm điểm đánh dấu và khớp nó trong regex là \d("bất kỳ chữ số nào"), để tránh sử dụng điểm đánh dấu nhiều lần (mặc dù là một tối ưu hóa khác, chúng tôi thực sự có thể đã sử dụng 0lại, bởi vì Perl s///chỉ thay thế lần xuất hiện đầu tiên bằng mặc định). Lưu ý rằng không có bước thoát rõ ràng cần thiết ở đây, vì các q(…)dấu phân cách chỉ có thể được bao gồm theo nghĩa đen trong chuỗi dữ liệu.


8

Mã + dữ liệu hạn chế

Cấu trúc chung nhất cho một quine trông giống như mã giả này:

data = " một phiên bản thoát của toàn bộ chương trình,
        với chuỗi này được thay thế bằng một điểm đánh dấu "
chương trình = data.replace (
  một biểu thức đánh giá đến điểm đánh dấu nhưng không đề cập đến nó ,
  thoát (dữ liệu))
chương trình in;

Cấu trúc này có thể được sử dụng để viết một câu đố (khá ngây thơ) trong hầu hết các ngôn ngữ. Tuy nhiên, nó có xu hướng ghi điểm khá tệ trên hầu hết các hệ thống tính điểm, bởi vì bạn phải viết toàn bộ chương trình hai lần. Tuy nhiên, hầu hết các cấu trúc quine có thể được coi là tối ưu hóa của cái này.

Có một số sự tinh tế cho điều này. Trong một số ngôn ngữ, phần khó nhất khi thực hiện thao tác này là viết mã thoát; trong nhiều ngôn ngữ, việc tạo ra điểm đánh dấu mà không đề cập đến tên của nó là khó khăn; và trong một số ngôn ngữ bí truyền, bạn sẽ phải phát minh ra loại chuỗi ký tự của riêng bạn. Tất cả ba hoạt động có xu hướng không gây ra quá nhiều rắc rối, mặc dù.

Ví dụ: chúng ta có thể viết một câu đố Python thoát khỏi một chuỗi bằng cách sử dụng chuỗi repr2 ký tự x"(có thể biểu thị là "x\"", tức là không sử dụng chuỗi x"trong biểu diễn chuỗi của chính chuỗi đó) làm điểm đánh dấu:

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

2
Có thể đáng chú ý (có thể trong một câu trả lời khác) rằng việc chèn chuỗi vào vị trí của điểm đánh dấu thường rất tốn kém trong esolang và có thể đáng để cấu trúc mã sao cho chính chuỗi đó là điều đầu tiên hoặc là điều cuối cùng (có thể được tách ra khỏi kết thúc bằng một hoặc hai ký tự mà bạn có thể mã hóa cứng) để bạn biết nơi nó phải đi.
Martin Ender

@MartinEnder: Tôi đồng ý rằng điều đó đáng được đề cập, nhưng có lẽ đó là một câu trả lời khác (thay vì một nhận xét hoặc chỉnh sửa trong câu trả lời này). Hầu hết các mẹo về quine là sửa đổi cấu trúc chung này, vì vậy tôi muốn lấy nó ra làm đầu tiên, vì nhiều người không biết nên bắt đầu viết từ đâu.

Một thay thế cho một điểm đánh dấu là sử dụng hai chuỗi, tôi đã làm điều đó cho Glass .
Ørjan Johansen

4

Khai thác mã nguồn gói

Trong khá nhiều ngôn ngữ (chủ yếu là ngôn ngữ 2D), mã nguồn có thể bao quanh; trong một số trường hợp nhất định (ví dụ như trong Befunge-98, nếu chương trình của bạn là một lớp lót) đi qua phần cuối của chương trình sẽ đưa bạn trở lại bắt đầu chương trình. Loại hành vi phi tuyến này có nghĩa là bạn có thể viết mã bên trong và bên ngoài một chuỗi ký tự cùng một lúc; một chuỗi không khớp "(hoặc bất kể dấu phân cách chuỗi là gì) sẽ cung cấp cho bạn một chuỗi có chứa toàn bộ phần còn lại của chương trình (ngoại trừ "chính nó).

Một vấn đề khi sử dụng thủ thuật này là bạn sẽ có được chuỗi như được nhìn từ quan điểm của ", thay vì từ đầu chương trình (như bạn muốn). Như vậy, có thể dễ dàng nhất để sắp xếp lại chương trình để "xuất hiện ở đầu hoặc cuối. Điều này thường có nghĩa là cắt chương trình của bạn thành nhiều phần và sử dụng bất kỳ lệnh điều khiển luồng thú vị / bất thường nào mà ngôn ngữ của bạn có (hầu hết các ngôn ngữ cho phép các chuỗi ký tự bao quanh chương trình có một lựa chọn tốt trong số này).

Một ví dụ điển hình là @ Justin Befunge-98 quine :

<@,+1!',k9"

Phần chưa từng có "ở cuối chương trình kết thúc toàn bộ chương trình theo một chuỗi ký tự, vì vậy (chạy từ phải sang trái do <bắt đầu), tất cả những gì chúng ta phải làm là xuất chương trình ( 9k), sau đó xuất ra trích dẫn kép ( '!1+,) và thoát ( @). Điều này tiết kiệm cần hai bản sao của chương trình (một dưới dạng mã, một dưới dạng dữ liệu); hai bản sao là cùng một đoạn mã, chỉ được diễn giải theo nhiều cách khác nhau.


4

Ghi nhớ cấu trúc của một quine

Tôi thích nghĩ về quines là ba phần, thay vì 2:

  • Phần 1: Tạo biểu diễn dữ liệu của phần 2 và 3.
  • Phần 2: Sử dụng dữ liệu để in thuật toán lại phần 1.
  • Phần 3: Giải mã biểu diễn để in phần 2 và 3.

Điều này có thể làm cho nó dễ dàng hơn để suy nghĩ về quines. Đây là một quine Python, với mỗi dòng tương ứng với một phần:

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

Đôi khi, bạn sử dụng một evalhoặc tương tự để loại bỏ trùng lặp, nhưng nói chung tôi đã thấy rằng điều này giúp trong việc viết các câu hỏi đơn giản.

Chúng ta hãy nhìn vào hai quines Underload khác nhau. Đây là lần đầu tiên:

(:aSS):aSS

Phần đầu tiên là (:aSS), tạo ra biểu diễn dữ liệu. Thứ hai là :aS, in (:aSS). Phần thứ ba là S, in :aSS.

Đây là câu hỏi thứ hai:

(:aS(:^)S):^

Lúc đầu, điều này dường như không phù hợp. Nhưng nếu bạn mở rộng quine, bạn sẽ nhận được:

(:aS(:^)S):aS(:^)S

Bây giờ (:aS(:^)S)là phần 1, :aSlà phần 2 và (:^)Slà phần 3.

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.