Câu trả lời này là một phần của sự hợp tác giữa tôi và 0 '. Cả hai chúng tôi cùng nhau làm việc này, lý do duy nhất tôi đăng nó là vì tôi đã giành được Rock, Paper, Kéo.
\Q-->{Q=1};"(",\N,")",\B,{findnsols(N,I,(between(2,inf,I),\+ (between(3,I,U),0=:=I mod(U-1))),L)->append(_,[Y],L),Q is Y*B}.
Hãy thử trực tuyến!
Giải trình
Câu trả lời này là một ví dụ hoàn hảo về những gì làm cho việc chơi golf trở nên thú vị.
Câu trả lời này sử dụng hệ thống mạnh mẽ của Prologs cho các ngữ pháp mệnh đề xác định. Đây là ngữ pháp của chúng tôi vô dụng một chút.
head(1)-->[].
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Quy tắc xây dựng đầu tiên là:
head(1)-->[].
Điều này nói với Prolog rằng chuỗi rỗng tương ứng với 1.
Quy tắc xây dựng thứ hai của chúng tôi là phức tạp hơn một chút.
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
Điều này cho chúng ta biết rằng bất kỳ chuỗi không rỗng nào đều chứa dấu ngoặc đơn xung quanh một mệnh đề có cùng các quy tắc này, ở bên phải của một mệnh đề có cùng các quy tắc này.
Nó cũng cho chúng ta biết rằng giá trị của mệnh đề này ( Q
) tuân theo quy tắc:
{prime(N,Y),Q is Y*B}
Phá vỡ điều này, Q
là sản phẩm của 2 số Y
và B
. B
chỉ là giá trị của mệnh đề bên trái và Y
là số N
nguyên tố thứ trong đó N
là giá trị của mệnh đề bên trong dấu ngoặc đơn.
Quy tắc này bao gồm cả hai quy tắc hình thành của cây nhân tố
- Nhân lên gấp bội
- Bao vây lấy số nguyên tố thứ n
Bây giờ cho các định nghĩa vị ngữ. Trong phiên bản không có người chơi, có hai vị từ đang chơi (trong mã thực tế của tôi, tôi đã chuyển tiếp các vị từ đi). Hai vị từ có liên quan ở đây là isprime/1
, khớp với một số nguyên tố, và prime/2
, được cho N
và Y
khớp với iff Y
là số N
nguyên tố thứ. Đầu tiên chúng ta có
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
Công trình này có định nghĩa khá chuẩn về tính nguyên thủy, chúng tôi khẳng định rằng không có số giữa 2 và I
, bao gồm 2 nhưng không I
chia I
.
Vị ngữ tiếp theo cũng khá đơn giản.
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Chúng tôi sử dụng findnsols
để tìm các N
số đầu tiên là số nguyên tố, sau đó chúng tôi trả về số cuối cùng. Mẹo ở đây là trong khi findnsols
không được đảm bảo để tìm các số nguyên tố nhỏ nhất N
, vì cách xử lý SWI, between
nó sẽ luôn tìm các số nguyên tố nhỏ hơn sớm hơn. Tuy nhiên, điều này có nghĩa là chúng ta phải cắt giảm để ngăn không cho nó tìm thêm số nguyên tố.
Sân gôn
Chúng tôi có thể chuyển tiếp lý do trong mã của chúng tôi hai lần. Vì isprime
chỉ được sử dụng một khi định nghĩa của nó có thể được di chuyển bên trong prime
. Việc tiếp theo là di chuyển prime
trực tiếp vào bên trong DCG, tuy nhiên vì chúng tôi sử dụng một biện pháp cắt giảm prime
để ngăn chặn việc findnsols
tạo ra quá nhiều số nguyên tố, chúng tôi có một chút vấn đề. Việc cắt, cắt toàn bộ DCG thay vì chỉ bit chúng ta muốn. Sau một chút đào tài liệu, chúng tôi thấy rằng once/1
có thể được sử dụng để cắt chỉ phần này chứ không phải toàn bộ DCG. Tuy nhiên, việc đào thêm tài liệu tiết lộ rằng ->
toán tử cũng có thể được sử dụng để thực hiện một nhiệm vụ tương tự. Các ->
nhà điều hành là tương đương với ,!,
vì vậy chúng tôi di chuyển cắt của chúng tôi sang phía bên kia của append/3
và thay thế nó với ->
.
Trong các vị từ SWI-Prolog (và quy tắc) có thể được đặt cho các toán tử dưới dạng tên cho phép chúng ta bỏ dấu ngoặc đơn thông thường. Qua đó chúng ta có thể lưu 6 byte bằng cách gọi quy tắc \
.