Ruby 2.0, 65 76 164 nhân vật
eval r="gets p;$<.pos=0;`ruby -c 2>&0`;p$?==0&&$_!='eval r=%p'%r"
Điều này sử dụng trình kiểm tra cú pháp tích hợp ( ruby -c
) của Ruby để kiểm tra cú pháp của đầu vào, có nghĩa là mã sẽ không được đánh giá.
Ví dụ sử dụng cơ bản:
ruby syntax.rb <<< foo
true
ruby syntax.rb <<< "'"
false
ruby syntax.rb < synxtax.rb # assumes the file was saved without trailing newline
false
Giải trình
Giải pháp này là (đã) dựa trên tiêu chuẩn Ruby quine:
q="q=%p;puts q%%q";puts q%q
%p
là định dạng định dạng cho arg.inspect
, có thể được so sánh với uneval
: khi eval
ing chuỗi được trả về bởi arg.inspect
, bạn (thường) lấy lại giá trị ban đầu. Do đó, khi định dạng q
chuỗi với chính nó làm đối số, %p
bên trong chuỗi sẽ được thay thế bằng chính chuỗi được trích dẫn (nghĩa là chúng ta có một cái gì đó giống như "q=\"q=%p;puts q%%q\";puts q%q"
).
Tổng quát hóa loại quine này dẫn đến một cái gì đó như sau:
prelude;q="prelude;q=%p;postlude";postlude
Cách tiếp cận này có một nhược điểm rất lớn (ít nhất là trong môn đánh gôn ): Tất cả các mã cần được sao chép. May mắn thay, eval
có thể được sử dụng để có được xung quanh này:
eval r="some code;'eval r=%p'%r"
Điều xảy ra ở đây là mã được truyền cho eval được lưu trữ bên trong r
trước khi eval
được gọi. Kết quả là, mã nguồn đầy đủ của eval
câu lệnh có thể được lấy bằng 'eval r=%p'%r
. Nếu chúng tôi thực hiện điều này bên trong eval
mã d và đảm bảo rằng mức cao nhất của chúng tôi chỉ bao gồm một eval
câu lệnh, thì biểu thức đó thực sự mang lại cho chúng tôi mã nguồn đầy đủ của chương trình của chúng tôi, vì bất kỳ mã bổ sung nào được chuyển đến eval
đều được lưu trữ bên trong r
.
Lưu ý bên lề: Cách tiếp cận này thực sự cho phép chúng ta viết một quine Ruby trong 26 ký tự: eval r="puts'eval r=%p'%r"
Bây giờ, trong giải pháp này, mã bổ sung được thực thi bên trong eval
bao gồm bốn câu lệnh:
gets p
Đầu tiên, chúng tôi đọc tất cả đầu vào từ STDIN và ngầm lưu nó vào $_
.
$<.pos=0
Sau đó, chúng tôi tua lại STDIN để đầu vào có sẵn một lần nữa cho quy trình con mà chúng tôi bắt đầu trong bước tiếp theo.
`ruby -c 2>&0`
Điều này khởi động Ruby trong chế độ kiểm tra cú pháp tích hợp, đọc mã nguồn từ stdin. Nếu cú pháp của tập lệnh được cung cấp (tên tệp hoặc stdin) là ổn, nó sẽ in Syntax OK
ra thiết bị xuất chuẩn của nó (được xử lý bởi quá trình cha), nhưng trong trường hợp có lỗi cú pháp, một mô tả về lỗi sẽ được in ra stderr - sẽ được in thay vào đó có thể nhìn thấy, vì vậy chúng tôi chuyển hướng điều đó thành niết bàn ( 2>&0
).
p$?==0&&$_!='eval r=%p'%r
Sau đó, chúng tôi kiểm tra mã thoát của quy trình con $?
, là 0 nếu cú pháp ổn. Cuối cùng, đầu vào mà chúng ta đọc trước đó ( $_
) được so sánh với mã nguồn của chính chúng ta (mà như tôi đã mô tả trước đó, có thể thu được bằng 'eval r=%p'%r
).
Chỉnh sửa: Đã lưu 14 ký tự nhờ @histocrat!