Giải thích lang của bạn, nhưng bản thân bạn thì không?


21

Có nhiều thách thức nói "giải thích X", trong đó X là một ngôn ngữ đơn giản. Theo tôi, đó là cách quá nhàm chán. Để cung cấp cho tất cả những người chần chừ trên internet một điều thú vị để làm, bạn có thể thử thực hiện thử thách này:

Thử thách

Chọn một ngôn ngữ $LANG. $LANGcó thể là bất kỳ ngôn ngữ lập trình hoàn chỉnh nào hoặc một tập hợp con hoàn chỉnh của ngôn ngữ lập trình. Xin lưu ý rằng nếu bạn bỏ qua một tính năng của ngôn ngữ $LANGđể giải thích, bạn cũng không được sử dụng nó cho chương trình của riêng mình, vì bài nộp của bạn cũng phải được viết $LANG.

Viết một trình biên dịch / thông dịch cho $LANGbằng văn bản $LANG. Bạn có thể sử dụng tất cả các phương tiện (bao gồm evalvà bạn bè) ngôn ngữ của bạn có sẵn để viết trình biên dịch này. Để thực hiện nhiệm vụ khó khăn hơn, có một hạn chế: Chương trình của bạn phải có khả năng diễn giải / biên dịch tất cả các chương trình hợp lệ $LANGngoại trừ chính trình thông dịch / trình biên dịch của bạn. Nếu chương trình được giải thích / biên dịch là trình thông dịch hoặc trình biên dịch của bạn (bất kể tên tệp), chương trình của bạn sẽ làm một cái gì đó hoàn toàn không liên quan đến chức năng của trình thông dịch hoặc trình biên dịch (chẳng hạn như barfing hoặc in Hello, world!).

Để làm cho nhiệm vụ này thậm chí phức tạp hơn, chương trình của bạn không được đọc nguồn của chính nó khi biên dịch hoặc giải thích.

Thông số kỹ thuật

  • Nhiệm vụ này là mã golf. Việc gửi với các ký tự ít nhất là đúng sẽ thắng. Trong trường hợp hòa, giải pháp được gửi đầu tiên sẽ thắng.
  • Chương trình / tập lệnh của bạn nên đọc chương trình sẽ được diễn giải từ một tập tin. Bạn có thể mã hóa đường dẫn và tên của nó. Khi tệp được đọc, bạn có thể biên dịch tệp sang tệp khác (Điều đó phải được thực thi trên hệ thống của bạn) hoặc chạy trực tiếp. Nếu $LANGthiếu khả năng đọc tệp, bạn có thể chọn một cách khác để đọc mã phù hợp $LANG. Bạn không thể chọn $LANGlàm tập hợp con của ngôn ngữ khác nhưng đã loại bỏ các capoverites đọc tệp.
  • Quy tắc golf thông thường áp dụng. Đó là: ngôn ngữ thú cưng cá nhân của bạn mà bạn tạo ra chỉ để giải quyết thách thức này bị cấm, nếu giải pháp trở nên tầm thường khi sử dụng nó (Chẳng hạn như xác định một chương trình char duy nhất thực hiện chính xác giải pháp). Lạm dụng các quy tắc được khuyến khích.

Chúng ta có được phép định nghĩa một ngôn ngữ cho việc này không, miễn là nó hoàn thành?
Cruncher

@Cruncher Vâng, bạn có. Xem điểm đạn cuối cùng của thông số kỹ thuật để biết thêm chi tiết.
FUZxxl

Câu trả lời:


8

Ruby, 63

b=$<.read
t="b=$<.read\nt=%p\nb!=t%%t&&eval(b)"
b!=t%t&&eval(b)

Câu trả lời được chấp nhận miễn là không có giải pháp nhỏ hơn.
FUZxxl

11

Perl, 89 ký tự, không gian lận

$_=q($_=q(Q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q

Lưu ý rằng mã này cực kỳ kén chọn về những gì được coi là "chính nó". Cụ thể, nó sẽ không nhận ra chính nó nếu có bất kỳ dòng mới nào hoặc khoảng trắng bổ sung khác trong đầu vào. Để kiểm tra nó, hãy lưu nó vào một tệp có tên (ví dụ) unquine.plvà làm điều này:

$ perl unquine.pl unquine.pl
Died at unquine.pl line 1, <> line 1.

Hãy nhớ rằng, unquine.pltệp phải dài chính xác 89 byte, không hơn, không kém. Chạy nó với một số tập lệnh Perl khác làm đầu vào chỉ thực thi tập lệnh khác, vì nó nên:

$ perl unquine.pl hello.pl
Hello, world!

Như tên có thể gợi ý, việc triển khai dựa trên một câu hỏi - cụ thể, cái này:

$_=q($_=q(Q);s/Q/$_/);s/Q/$_/

Mã này đặt $_bằng chính nó; phần còn lại của chương trình (tất nhiên, phải được sao chép bên trong $_) chỉ so sánh $_với đầu vào, chết nếu chúng khớp và đánh giá đầu vào khác.


Bạn có thể thay thế &&/ ;cặp đó bằng một ternary (một char tắt, nhân đôi bằng cách bỏ qua). Ý tưởng tuyệt vời và thực hiện!
JB

@JB: Bắt tốt! Xuống tới 89 ký tự bây giờ.
Ilmari Karonen

5

GolfScript, 30 ký tự

{`".~"+"#{$<.read}".@=!{~}*}.~

Chương trình này đọc nội dung của một tệp có tên trên dòng lệnh và, nếu nó không chính xác bằng mã ở trên, hãy hiểu nó là GolfScript. Nếu đầu vào chính xác bằng mã ở trên, đơn giản là nó sẽ được in không thay đổi (ngoại trừ một dòng mới được nối vào cuối).

Đây là một sự thích ứng khá đơn giản của chương trình tự nhận dạng này . Đặc biệt:

  • { } là một khối mã theo nghĩa đen trong GolfScript.
  • .~, áp dụng cho một khối mã, sao chép khối và thực hiện sao chép.

Bên trong khối mã:

  • ` xâu chuỗi các bản sao của khối mã.
  • ".~"+nối các ký tự .~vào nó, thu được một chuỗi chứa mã nguồn của chương trình.
  • "#{$<.read}"là một tài liệu hack cho phép thực thi mã Ruby trong GolfScript. Trong trường hợp này, nó thực thi câu lệnh Ruby $<.read(bị đánh cắp một cách đáng xấu hổ từ giải pháp Ruby của Lowjack ), nó đọc và trả về nội dung của bất kỳ tệp nào được chỉ định trên dòng lệnh. Việc hack này là cần thiết vì bản thân GolfScript không cung cấp khả năng I / O tệp rõ ràng.
  • .@ sao chép và xáo trộn các phần tử trên đầu ngăn xếp để ngăn xếp chứa hai bản sao của nội dung tệp theo sau là mã nguồn của chương trình này.
  • =! so sánh hai mục trên cùng của ngăn xếp (tức là nội dung tệp và nguồn), trả về 1 nếu chúng khác nhau và 0 nếu chúng giống nhau.
  • {~}*đánh giá bản sao còn lại của nội dung tệp dưới dạng mã GolfScript, nhưng chỉ khi kết quả so sánh là 1. (Về mặt kỹ thuật, nó thực thi khối mã {~}nhiều lần như được đưa ra bởi số trên ngăn xếp, tức là 0 hoặc 1 lần. khối, ~là toán tử eval GolfScript.)

Thi thiên Nếu việc đọc mã để thực thi từ stdin được cho phép, thử thách này có thể được giải quyết trong 21 ký tự mà không cần phải trình bày với Ruby:

{`".~"+1$=!{""\~}*}.~

Chương trình này sẽ đọc một chuỗi đầu vào từ stdin và, nếu nó không khớp với nguồn của chính nó, sẽ thực thi nó (với một đầu vào trống). Giống như chương trình trên, đầu vào không khớp với nguồn chỉ đơn giản được lặp lại.


Có vẻ tốt, nhưng có vẻ như bạn không đọc đầu vào từ một tệp.
FUZxxl

Đã sửa lỗi, bây giờ nó đọc từ một tệp (chính xác) như giải pháp của Lowjack.
Ilmari Karonen

5

Python, 167 130 118 byte

Đây là nỗ lực đầu tiên của tôi khi chơi golf, vì vậy hãy đến đây! Nó diễn giải bất kỳ chương trình nào ngoại trừ chính nó

Phiên bản cải tiến:

i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)

Nếu nó được chính nó thì nó barfs với:

Traceback (most recent call last):
  File "pygolf.py", line 1, in <module>
    i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)
NameError: name 'a' is not defined

Tôi nghĩ giải pháp này hoạt động khá giống với cách của Ilmari Karonen, ý tưởng cơ bản là:

input = read_some_file()
if input == some_quine()
    barf()
interpret(input)

Câu hỏi tôi sử dụng được dựa trên cái này:

(lambda x: x + repr((x,)))('(lambda x: x + repr((x,)))',)

Nhưng tôi đã nhận ra một câu hỏi ngắn hơn nhiều là:

q='q=%s;q%%repr(q)';q%repr(q)

Và nó thậm chí có thể ngắn hơn nếu bạn cho phép trình bao python tương tác, trong trường hợp đó bạn có thể làm:

'%s;_%%repr(_)';_%repr(_)

Vì python không có cách nào ngắn để lấy dòng lệnh args, tôi đã đi với raw_input () (vẫn còn khá dài, nhưng không dài bằng

import sys;sys.argv[1]

Cách sử dụng là:

echo "foobar.py" | python quinterpretter.py

hoặc là

python quinterpretter.py
<type filename and hit enter>

Tôi đã tìm thấy một quine ngắn hơn để sử dụng, nhưng đây là phiên bản cũ của tôi (cho hậu thế):

i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)('i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)', ' else 1;exec(i)\n') else 1;exec(i)

Thay thế% s bằng% r và xóa repr. % r có nghĩa là thô và về cơ bản, đó là điều tương tự.
Loovjo

4

Tôi không thể đọc chính xác từ một tệp bằng Javascript (ok, tôi có thể, sử dụng điều HTML5 FileReader, nhưng điều đó làm cho mọi thứ phức tạp hơn nhiều so với tôi cần). Vì vậy, đây là một hàm chấp nhận một chương trình Javascript dưới dạng một chuỗi và chạy nó.

Đây có lẽ không phải là golf như nó có thể, nhưng dù sao thì đây là:

Javascript, 252

function c(p){q='\"';s='\\';a="function c(p){q='\"';s='\\';a=%;a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=q+a.replace('%',q+a+q)+q;alert(a);}";a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=a.replace('%',q+a+q);alert(a);if(p!=a)eval(p)}

Hãy cho tôi biết nếu có ai biết một kỹ thuật tốt hơn để tạo một quine trong Javascript.


1
Tôi đã đăng một giải pháp JS 135 ký tự bên dưới, dựa trên mã của bạn và giải pháp Perl của tôi. +1 cho cảm hứng!
Ilmari Karonen

2
read p<p;read c<c;[ "$p" = "$c" ]||. ./c

45 ký tự của sh (vỏ POSIX). Mã được chạy phải có trong tệp ./c.

Mã cho trình thông dịch phải nằm trong tệp ./p, vì vậy tôi đoán rằng tôi đã bị lừa, mặc dù thử thách dường như không cấm nó. Hay điều này sẽ khiến "ngôn ngữ" của tôi không còn là "ngôn ngữ lập trình hoàn chỉnh"?

Sử dụng một công cụ thường là một thực thi bên ngoài, nhưng về mặt lý thuyết có thể được tích hợp vào trình bao, mã có thể được rút ngắn:

cmp -s p c||. ./c

Đó là 18 ký tự, và -sbit chỉ là để chặn một dòng thường được in cho các chương trình hợp lệ (không phải tự).

Và sau đó, bạn luôn có thể xây dựng một phiên bản của ngôn ngữ shell thực hiện ở trên với cú pháp ngắn gọn hơn.

Và sau đó bạn luôn có thể xây dựng một chương trình, khi đầu vào bao gồm một '.' - hay chết tiệt, chuỗi rỗng-- đánh giá nội dung của một tệp khác là mã thông thường và gọi đây là ngôn ngữ lập trình. Vì vậy, chuỗi trống sẽ là giải pháp cho thách thức của bạn, bằng ngôn ngữ bạn đã xây dựng. Trên thực tế, đây là một thông dịch viên cho một ngôn ngữ như vậy:

read code; if [ "$code" ]; then eval "$code"; else . ./othercode; fi

Sử dụng ngôn ngữ của đoạn script trên, giải pháp là chuỗi rỗng. Và vị trí mã không cần phải được mã hóa nữa.

Vấn đề?


2
Thách thức nói rằng "chương trình của bạn không được đọc nguồn riêng".
Ilmari Karonen

Chết tiệt, lãng phí thời gian rồi. Và tôi thấy nó thậm chí còn nói rằng bạn không được sử dụng các tính năng mà bạn sẽ bỏ qua. Điều này sẽ đi ngược lại với tính năng chuỗi trống. Sau đó, một lần nữa, trình thông dịch phải bỏ qua / thay đổi chức năng nếu mã cho trình biên dịch / trình thông dịch tự gây ra hành vi khác nhau trong ngôn ngữ mới. Trong mọi trường hợp, tôi đã vui vẻ viết ngụy biện.
TaylanUB

@TaylanUB Vâng, bạn thực sự phải diễn giải tất cả các chương trình $ lang hợp lệ trừ chính trình thông dịch.
FUZxxl

@FUZxxl Có, ngôn ngữ "sh + chuỗi rỗng" tương đương với sh (nếu mã không phải là chuỗi trống) và chương trình chuỗi trống được viết trong đó cũng diễn giải mã sh (phải được đặt vào ./othercode) không có gì khi mã là chuỗi rỗng. Tôi không nên gọi tệp ./thercode, nó gây hiểu nhầm; đó chỉ là mã mà trình thông dịch viết bằng ngôn ngữ chuỗi trống sẽ diễn giải.
TaylanUB

2

JavaScript, 135 ký tự

function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}

Giải pháp JavaScript của Peter Olson đã truyền cảm hứng cho tôi để thử chuyển giải pháp Perl của mình sang JS. Giống như giải pháp của anh ta, mã này xác định một hàm cchấp nhận một chuỗi và loại bỏ nó nếu nó không bằng mã ở trên.

Phải mất một thời gian tôi mới tìm ra một cách tốt để đối phó với sự vắng mặt của các dấu phân cách chuỗi cân bằng trong JavaScript, cho đến khi tôi tìm thấy những gì trong nhận thức muộn là giải pháp rõ ràng : unescape().

Thuận tiện, mã của tôi không chứa bất kỳ dấu gạch chéo ngược hoặc dấu ngoặc kép nào, vì vậy nó có thể được lưu trữ an toàn trong một chuỗi trích dẫn kép. Điều này giúp dễ dàng kiểm tra:

e = "function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}"
h = "alert('Hello, world!')"

eval(e)  // defines the function c()

c(h)     // evaluates h
c(e)     // does not evaluate e, alerts "undefined" instead

Bạn có thể thay thế alert()bằng 0để làm cho nó không làm gì thay vì cảnh báo undefinedvà tiết kiệm 13 ký tự.
Peter Olson

@PeterOlson: Vâng, nhưng nhiệm vụ nói rằng "chương trình của bạn nên làm một cái gì đó hoàn toàn không liên quan" nếu nó tự phát hiện ra. Tôi hiểu điều đó có nghĩa là nó nên làm một cái gì đó - tốt nhất là một cái gì đó hiển thị cho người dùng, tôi đoán. Bên cạnh đó, tôi chỉ thích nó tốt hơn theo cách này. :) (Ps. Yay, ngoài trời có tuyết! Cuối cùng thì mùa đông cũng đến!)
Ilmari Karonen

1
@Ilmari Không làm gì liên quan đến việc diễn giải Javascript IMHO.
FUZxxl

Bạn có thể đi p=>...thay vìfunction c(p)
FireCubez

2

Lisp thường gặp, 59

#+~ #.(#:a)(defun L(p)(compile-file p))(push :~ *features*)
  • Trong REPL Lisp mới, biên dịch tệp của bạn (ví dụ sbcl --load)
  • Bây giờ bạn có một hàm L, có thể biên dịch các tệp Lisp thông thường
  • Tuy nhiên, nếu bạn gọi (L <your file>), một lỗi sẽ được báo hiệu trong khi đọc tệp.

Tại sao?

Bởi vì lần đầu tiên, bạn đã đẩy :~từ khóa vào *features*. Bây giờ, môi trường của bạn biết về ~tính năng và macro người đọc #+, khi đánh giá ~ biểu thức tính năng , sẽ thành công và đọc biểu mẫu sau thay vì bỏ qua nó như lần đầu tiên. Trong tệp của bạn, mẫu dưới đây là #.(#:a), yêu cầu đánh giá (#:a)tại thời điểm đọc và sử dụng giá trị kết quả làm mã được đọc. Nhưng (#:a)gọi hàm liên quan đến biểu tượng không liên kết #:a. Vì không #:aliên quan, nó là một biểu tượng mới không bị ràng buộc với bất kỳ chức năng nào (tức là không fboundp). Lỗi.


1

Lược đồ, 48 hoặc 51 ký tự

Đề án là một ngôn ngữ với nhiều triển khai khác nhau. Mặc dù việc triển khai phải tuân thủ RnRS mới nhất, tiêu chuẩn làm việc mới nhất (R6RS) vẫn chưa được ưa chuộng do thiếu sự tối giản. R7RS sẽ sớm được phát hành như một biện pháp khắc phục, trong khi phân tách ngôn ngữ trong 2. Ngôn ngữ đầu tiên mạnh mẽ và tối giản và thứ hai, siêu ngôn ngữ đầu tiên dự định cung cấp các phần mở rộng tính năng cho khả năng tương tác giữa các lần triển khai. Cho đến lúc đó, chúng tôi dựa vào SRFI (Yêu cầu thực hiện lược đồ), cung cấp (nếu được triển khai trong triển khai máy chủ hoặc theo cách thủ công (như thông thường trong sơ đồ)) một phương tiện để hoàn thành một cách hợp lý các nhiệm vụ chung. Tất cả điều này để nói rằng đoạn mã đầu tiên (51 ký tự), trong khi vẫn có thể di động hết mức có thể, dựa vào SRFI-22 (thực thi các tập lệnh lược đồ trong UNIX) để truy cập vào các đối số dòng lệnh:

(define(main x y)(case y(x => error)(else => load)))

hoặc dễ đọc hơn:

(define (main current-file arg)
  (case arg
    [current-file => error]
    [else => load]))

Thứ hai (48 ký tự) là một phương tiện không có tệp để giải thích mà không thể tự đánh giá (trong môi trường null):

(define(e)(write(eval(read)null-environment))(e))

hoặc dễ đọc hơn:

(define (interpret)
  (write (eval (read) null-environment))
  (interpret))

Mã của bạn không hoạt động nếu bạn sao chép trình thông dịch của bạn.
FUZxxl

1
Để lại cho câu trả lời lược đồ để chứa các ngoặc đơn lồng nhau trong văn xuôi của nó.
Cyoce

1

Groovy, 13 byte

{Eval.me(it)}

Điều này sẽ giải thích một tập hợp con của Groovy.

trường hợp thử nghiệm:

p={Eval.me(it)}

p'''
    (0..37).each{println"1234567890JIHGFEDCBAKLMNOPQRST!?,.ZYXWVU"[it..it+2]}
'''

p'''
    {Eval.me(it)}
'''

Thật không may trong khi nó chắc chắn là barfs, nó làm như vậy theo một cách hoàn toàn giống như thông dịch viên, và nó làm điều đó cho khá nhiều đầu vào.


Trong dòng nào bạn đọc chương trình sẽ được giải thích? Mã của bạn rất thú vị mặc dù nó không phải là một đệ trình hợp lệ cho nhiệm vụ này.
FUZxxl

Tôi giả sử lỗi là một cái gì đó như "vượt quá giới hạn đệ quy"?
Ilmari Karonen

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.