Độ chính xác 0.10, được tối ưu hóa cho kích thước mã nguồn (1944 byte)
Ban đầu tôi đã đọc sai câu hỏi và giải thích nó như một môn đánh gôn. Đó có lẽ là điều tốt nhất, vì việc viết một câu hỏi với mã nguồn ngắn dễ dàng hơn nhiều so với mã đối tượng ngắn theo các hạn chế trong câu hỏi; điều đó làm cho câu hỏi đủ dễ để tôi cảm thấy mình có thể đưa ra câu trả lời một cách hợp lý và có thể hoạt động như một bước đệm trên đường đến một câu trả lời tốt hơn. Nó cũng nhắc tôi sử dụng ngôn ngữ cấp cao hơn cho đầu vào, nghĩa là tôi cần thể hiện ít hơn trong chính chương trình. Tôi đã không tạo Verity như một ngôn ngữ chơi gôn cho phần cứng (tôi thực sự được thuê để tạo ra nó một thời gian trước trong một bối cảnh hoàn toàn khác), nhưng có một sự hồi tưởng ở đó (ví dụ như nó ở cấp độ cao hơn so với HDL thông thường, và nó có ít nồi hơi hơn, nó cũng dễ mang hơn nhiều so với HDL thông thường).
Tôi khá chắc chắn rằng giải pháp chính xác cho mã đối tượng ngắn liên quan đến việc lưu trữ dữ liệu trong một loại cấu trúc cây nào đó, cho rằng câu hỏi không cho phép sử dụng ROM khối, đó là nơi bạn thường lưu trữ nó trong một chương trình thực tế; Tôi có thể bắt đầu viết một chương trình sử dụng nguyên tắc này (không chắc ngôn ngữ nào, có thể là Verity, có thể là Verilog; VHDL có quá nhiều bản tóm tắt để có thể tối ưu cho loại vấn đề này) vào một lúc nào đó. Điều đó có nghĩa là bạn sẽ không cần phải truyền từng bit mã nguồn cho mỗi bit của "ROM được tạo thủ công" của bạn. Tuy nhiên, trình biên dịch Verity hiện đang tổng hợp cấu trúc của đầu ra dựa trên mức độ ưu tiên và kết hợp của đầu vào, nghĩa là nó đại diện hiệu quả cho con trỏ lệnh (do đó là chỉ mục cho bảng tra cứu) trong unary,
Chương trình chính nó:
import <print>new x:=0$1296in(\p.\z.\a.new y:=(-a 5-a 1-a 1-a 2-a 4-a 2-a 3-a 2-a 6-a 2-a 0-a 3-a 0-a 4-a 4-a 7-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 6-a 7-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 4-a 3-a 2-a 7-a 5-a 7-a 0-a 6-a 4-a 4-a 1-a 6-a 2-a 6-a 1-a 7-a 6-a 6-a 5-a 1-a 2-a 2-a 0-a 5-a 0-a 0-a 4-a 2-a 6-a 5-a 0-a 0-a 6-a 3-a 6-a 5-a 0-a 0-a 5-a 0-a 6-a 5-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 5-a 3-a 2-a 7-a 5-a 7-a 0-a 5-a 5-a 5-a 1-a 4-a 4-a 3-a 1-a 5-a 5-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 4-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 5-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 4-a 7-a 3-a 6-a 2-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 6-a 3-a 3-a 5-a 1-a 7-a 2-a 6-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 6-a 3-a 1-a 5-a 3-a 7-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 5-a 7-a 5-a 7-a 4-a 6-a 5-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 5-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 4-a 1-a 7-a 7-a 6-a 3-a 7-a 4-a 2-a 0-a 4-a 3-a 6-a 2-a 6-a 3-a 7-a 4-a 2-a 0-a 5-a 4-a 6-a 0-a 7-a 2-a 0-a 1-a 4-a 5-a 3-a 4-a 4-a 4-a 4-a 3-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 3-a 7-a 4-a 2-a 0-a 4-a 4-a 6-a 5-a 6-a 3-a 7-a 5-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 5-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 1-a 5-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 7-a 2-a 7-a 1-a 5-a 1-a 4-a 2-a 3-a 7-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 6-a 6-a 1-a 5-a 1-a 5-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 0-a 5-a 1-a 4-a 4-a 3-a 4-a 4-a 4-a 4-a 6-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 0-a 5-a 0-a 0-a 0-a 1-a 6-a 5-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 2-a 0-a 0-a 1-a 4-a 7-a 4-a 7-a 1-a 6-a 2-a 6-a 1-a 7-a 3-a 6-a 3-a 7-a 0-a 6-a 1-a 5-!x)in while!x>0do(p(if z<32then z+92else z);if z==45then while!y>0do(p 97;p 32;p(48^!y$$3$$32);p 45;y:=!y>>3)else skip;x:=!x>>6))print(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
Dễ đọc hơn:
import <print>
new x := 0$1296 in
(\p.\z.\a.
new y := (-a 5-a 1-
# a ton of calls to a() omitted...
-a 1-a 5-!x) in
while !x>0 do (
p(if z<32 then z+92 else z);
if z==45
then while !y>0 do (
p 97;
p 32;
p(48^!y$$3$$32);
p 45;
y:=!y>>3 )
else skip;
x:=!x>>6
)
)(print)(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
Ý tưởng cơ bản là chúng tôi lưu trữ toàn bộ dữ liệu trong biến x
. (Như thường lệ đối với quine, chúng tôi có phần mã và phần dữ liệu; dữ liệu mã hóa văn bản của mã và cũng có thể được sử dụng để tạo lại văn bản của dữ liệu.) Thật không may, Verity hiện không cho phép rất lớn Các hằng số được ghi trong mã nguồn (nó sử dụng các số nguyên OCaml trong quá trình biên dịch để biểu diễn các số nguyên trong nguồn, điều này rõ ràng không đúng trong ngôn ngữ hỗ trợ các loại số nguyên rộng tùy ý) - và bên cạnh đó, nó không cho phép các hằng số được chỉ định trong bát phân - vì vậy chúng tôi tạo ra giá trị của x
thời gian chạy thông qua các cuộc gọi lặp lại cho một hàma
. Chúng ta có thể tạo một hàm void và gọi nó liên tục dưới dạng các câu lệnh riêng biệt, nhưng điều đó sẽ khiến việc xác định nơi bắt đầu xuất văn bản của phần dữ liệu trở nên khó khăn. Vì vậy, thay vào đó, tôi đã a
trả về một số nguyên và sử dụng số học để lưu trữ dữ liệu (Verity đảm bảo rằng số học đánh giá từ trái sang phải). Phần dữ liệu được mã hóa x
bằng một -
dấu hiệu duy nhất ; khi điều này gặp phải trong thời gian chạy, nó được mở rộng đến toàn bộ -a 5-a 1-
, v.v., thông qua việc sử dụng y
.
Khởi tạo y
như một bản sao x
khá tinh tế ở đây. Bởi vì a
trả về 0 một cách cụ thể, hầu hết số tiền chỉ bằng 0 trừ 0 trừ đi và tự hủy. Chúng tôi kết thúc bằng !x
(tức là "giá trị của x
"; trong Verity, như trong OCaml, tên của một biến hoạt động giống như một con trỏ hơn bất kỳ thứ gì khác và bạn phải xác định rõ ràng để lấy giá trị của biến đó. Các quy tắc của Verity cho phép trừ đơn nguyên là một chút phức tạp - phép trừ đơn nguyên v
được viết là (-v)
- do đó (-0-0-0-!x)
phân tích thành (-(0-0-0-!x))
, bằng !x
, và cuối cùng chúng ta khởi tạo y
như một bản sao của x
. (Cũng đáng lưu ý rằng Verity không phải làgọi theo giá trị, nhưng thay vào đó cho phép các hàm và toán tử chọn thứ tự họ đánh giá mọi thứ; -
sẽ đánh giá đối số bên trái trước đối số bên phải và đặc biệt, nếu đối số bên trái có tác dụng phụ, chúng sẽ hiển thị khi đối số bên phải được đánh giá.)
Mỗi ký tự của mã nguồn được biểu diễn bằng hai chữ số bát phân. Điều này có nghĩa là mã nguồn được giới hạn ở 64 ký tự khác nhau, vì vậy tôi phải tạo một bảng mã riêng để sử dụng nội bộ. Đầu ra là ASCII, vì vậy tôi cần chuyển đổi nội bộ; đây là những gì (if z<32 then z+92 else z)
dành cho. Đây là bộ ký tự tôi đã sử dụng trong biểu diễn bên trong, theo thứ tự số (nghĩa là \
có mã số 0, ?
có mã số 63):
\]^_`abcdefghijklmnopqrstuvwxyz{ !"#$%&'()*+,-./0123456789:;<=>?
Bộ ký tự này cung cấp cho chúng ta hầu hết các nhân vật quan trọng đối với Verity. Các ký tự đáng chú ý bị thiếu là }
(có nghĩa là chúng ta không thể tạo một khối bằng cách sử dụng {}
, nhưng may mắn là tất cả các câu lệnh đều là biểu thức để chúng ta có thể sử dụng()
thay thế); và |
(đây là lý do tại sao tôi phải sử dụng độc quyền thay vì bao gồm HOẶC khi tạo giá trị x
, nghĩa là tôi cần khởi tạo nó thành 0; tuy nhiên, tôi cần phải xác định mức độ lớn của nó). Một số ký tự quan trọng mà tôi muốn đảm bảo có trong bộ ký tự là <>
(đối với nhập, cũng thay đổi), ()
(rất khó để viết chương trình có thể được phân tích cú pháp mà không có các ký tự này), $
(đối với mọi thứ phải làm với băng thông) và \
( đối với lambdas, về mặt lý thuyết chúng ta có thể giải quyết vấn đề này vớilet…in
nhưng nó sẽ dài dòng hơn nhiều).
Để làm cho chương trình ngắn hơn một chút, tôi đã viết tắt print
và !x$$6$$32
(ví dụ: "6 bit dưới cùng !x
, được sử dụng để print
thư viện) thông qua việc liên kết chúng với các đối số lambda.
Cuối cùng, có vấn đề về đầu ra. Verity cung cấp mộtprint
thư viện dành cho đầu ra gỡ lỗi. Trên một trình giả lập, nó in mã ASCII thành đầu ra tiêu chuẩn, hoàn toàn có thể sử dụng để kiểm tra chương trình. Trên một bảng mạch vật lý, nó phụ thuộc vào một print
thư viện đã được viết cho chip và bảng cụ thể xung quanh nó; có một print
thư viện trong bản phân phối Verity cho một bảng đánh giá mà tôi có quyền truy cập để in đầu ra trên màn hình bảy đoạn. Cho rằng thư viện cuối cùng sẽ chiếm không gian trên bảng mạch kết quả, có thể đáng để sử dụng một ngôn ngữ khác cho một giải pháp tối ưu hóa cho vấn đề này để chúng ta có thể xuất các bit của đầu ra trực tiếp trên dây.
Nhân tiện, chương trình này là O (n²) trên phần cứng, có nghĩa là nó tệ hơn nhiều trên một trình giả lập (tôi nghi ngờ O (n⁴); tuy nhiên không chắc chắn, nhưng nó đủ khó để mô phỏng rằng dường như không thể là khối lập phương và dựa trên cách thời gian phản ứng với những thay đổi của tôi khi tôi đang viết chương trình, chức năng dường như phát triển rất nhanh). Trình biên dịch Verity cần 436 lượt tối ưu hóa (rất nhiều, nhiều hơn mức thường sử dụng) để tối ưu hóa chương trình và thậm chí sau đó, việc mô phỏng nó rất khó cho máy tính xách tay của tôi. Quá trình biên dịch và mô phỏng hoàn chỉnh mất thời gian sau:
real 112m6.096s
user 105m25.136s
sys 0m14.080s
và đạt đỉnh ở mức 2740,02 kibibytes của bộ nhớ. Chương trình cần tổng cộng 213646 chu kỳ đồng hồ để chạy. Nó làm việc, mặc dù!
Dù sao, câu trả lời này không thực sự đáp ứng câu hỏi vì tôi đang tối ưu hóa cho điều sai, nhưng vì chưa có câu trả lời nào khác, đây là câu trả lời tốt nhất theo mặc định (và thật tuyệt khi thấy một câu đố chơi golf sẽ như thế nào một ngôn ngữ phần cứng). Hiện tại tôi không chắc chắn liệu tôi có làm việc trên một chương trình nhằm tạo ra ouptut được tối ưu hóa hơn trên chip hay không. (Nó có thể sẽ lớn hơn rất nhiều về nguồn, vì mã hóa dữ liệu O (n) sẽ khá phức tạp hơn so với mã nguồn được thấy ở đây.)