Lời khuyên cho việc chơi golf trong Prolog


16

Bạn có lời khuyên chung nào cho việc chơi golf trong Prolog? Tôi đang tìm kiếm những ý tưởng có thể áp dụng cho các vấn đề về golf nói chung ít nhất là cụ thể đối với Prolog (ví dụ: một biến chữ cái không đặc trưng cho Prolog để giảm quy mô chương trình).

Vui lòng cho biết trong các mẹo của bạn nếu nó cụ thể đối với việc triển khai Prolog (ví dụ: tích hợp cụ thể SWI-Prolog)

Vui lòng chỉ đăng một mẹo cho mỗi câu trả lời hoặc danh sách các mẹo có liên quan chặt chẽ đến cùng một ý chính.


1
Các prologtừ khóa là kinda vô dụng. Trừ khi chúng tôi có một thử thách Prolog phiên dịch, chúng tôi không cần nó.
con mèo

Câu trả lời:


10

Sử dụng toán tử cho tên dự đoán

Có thể đặt các toán tử biến vị ngữ làm tên miễn là toán tử là một trong các toán tử được xác định trước (được liệt kê ở đây ) và chưa được xác định là một biến vị ngữ. Điều này tiết kiệm một vài byte cả khi xác định và gọi biến vị ngữ vì các biến vị ngữ toán tử không cần phải được viết ở name(arg1,arg2,etc..)dạng bình thường và có thể được gọi như người ta có thể mong đợi với các toán tử.

Đối với một và hai vị từ đối số, chúng có thể được đặt tên tương ứng của các toán tử đơn nguyên và nhị phân. Đối với các vị từ arity cao hơn, chúng ta vẫn có thể tránh các dấu ngoặc đơn bằng cách sử dụng khớp mẫu. Ví dụ: nếu chúng ta có một biến vị ngữ A+B+C:-..., Prolog sẽ sử dụng các quy tắc ưu tiên và kết hợp toán tử của nó để biến đổi nó thành (A+B)+C:-...một biến vị ngữ toán tử trong đó đối số đầu tiên được khớp với mẫu A+B. Hoặc A-B+C*D:-...trở thành (A-B)+(C*D)đối số đầu tiên của nó là mẫu khớp với A-Bvà đối số thứ hai của nó là mẫu khớp với C*D.

Ví dụ

_+_+_.
A-B+C*D:-between(A,B,C),C+D.
\X:-X>1,X<10.
X+Y:-length(Y,X),member(X,Y).



5+[10,5,3,2,5],a+b+c,0-20+X*[2,4,6,5,40],\9.

Đầu ra sẽ là X = 5.

Hãy thử trực tuyến!

Hệ quả

DCG là đường cú pháp cho các vị từ nên chúng cũng có thể được đặt cho các toán tử cho tên. Điều này hoạt động như mong đợi khi gọi chúng là DCG hoặc từ DCG hoặc sử dụng các phrasevị từ hoặc các biến khác được thiết kế để hoạt động với DCG. Khi gọi chúng là dấu ngoặc đơn vị ngữ được yêu cầu (ví dụ: A+B-->...phải được gọi như thế +(A,B,...)) vì các vị từ DCG có thêm hai đối số cho danh sách khác biệt của chúng. Đối với các toán tử có tên DCG có nhiều hơn hai đối số sử dụng khớp mẫu toán tử thì điều quan trọng là phải đảm bảo khi gọi nó như là một biến vị ngữ mà các toán tử khớp mẫu được phân phối chính xác.

Đặt tên toán tử cho các DCG không có đối số bổ sung có thể hữu ích nếu bạn cần gọi chúng trong chương trình của mình kể từ đó bạn có thể làm như vậy mà không cần sử dụng dấu ngoặc đơn. Cần thận trọng vì có thể là những gì bạn lưu trong ngoặc đơn bạn có thể mất để thêm khoảng cách cần thiết để phân tích các toán tử liền kề.

Ví dụ

/ -->a+b+X,X+d+e.
A+B+C-->[A],[B],[C].


X/[],member(c,X),phrase(f+o+o,Y),+(b+a,r,Z,[]).

Đầu ra sẽ là

X = [a, b, c, c, d, e],
Y = [f, o, o],
Z = [b, a, r].

Hãy thử trực tuyến!

Hãy cẩn thận

Với các toán tử đơn nguyên +-, Prolog sẽ diễn giải +20hoặc -20dưới dạng số thay vì gọi đến một vị ngữ +/1hoặc -/1vị ngữ. Vị ngữ được đặt tên đơn +hoặc -tên vẫn có thể được gọi theo số bằng cách sử dụng dấu ngoặc đơn ( +(20), -(20)). Nếu tránh các byte thêm từ ngoặc là mong muốn khai thác unary khác như \, $, vv có thể được sử dụng như tên thay thế.

Sự kết hợp giữa khớp mẫu và các biến vị ngữ có tên toán tử không hoàn toàn không có sai sót. Nếu bạn có hai biến vị ngữ có cùng một toán tử với tên của chúng và với một mẫu khớp với nhau thì chung chung hơn một biến chung thì một biến chung có thể được gọi trước hoặc nếu một biến kém chung hơn (tùy theo thứ tự của chúng trong nguồn) . Ví dụ trong ví dụ trên nếu A-B+C*Dkhông khớp với đầu vào của nó thì Prolog sẽ thử gọi X+Y. Điều này sẽ dẫn đến một lỗi vì length/2yêu cầu Yphải là một số nguyên mà nó sẽ không phải là vì nó sẽ ở dạng C*D. Điều này có thể tránh được đơn giản bằng cách đảm bảo không có hai biến vị ngữ nào có cùng toán tử với tên của chúng hoặc nếu thất bại thì sử dụng các phép cắt và sắp xếp cẩn thận nguồn.


2
Thật đáng ngạc nhiên khi thủ thuật này hữu ích cho môn đánh gôn. Tôi chỉ giảm 16% số câu trả lời của câu trả lời bằng cách sử dụng mẹo này!
DLosc

6

Cố gắng đặt mọi trường hợp có thể vào một quy tắc duy nhất

Cách rõ ràng để lập trình trong Prolog là khai báo nhiều quy tắc cho cùng một vị từ. Ví dụ: một vị từ để đảo ngược danh sách với bộ tích lũy sẽ trông như thế này:

r([],Z,Z).
r([H|T],Z,R):-r(T,[H|Z],R).

Trong Code-golf, chúng ta có thể loại bỏ quy tắc đầu tiên và thêm quy tắc ;ở cuối quy tắc thứ hai để mã kết thúc đệ quy:

r([H|T],Z,R):-r(T,[H|Z],R);R=[H|Z].

Chúng tôi biết rằng điều kiện đầu tiên r(T,[H|Z],R)sẽ thất bại nếu T trống, tức là nếu đệ quy cần kết thúc và do đó chúng tôi có thể thêm chấm dứt của chúng tôi dưới dạng một mệnh đề hoặc sau nó.

Nguyên tắc tương tự hoạt động trong rất nhiều tình huống. Tuy nhiên, lưu ý rằng đôi khi thực sự ngắn hơn để khai báo quy tắc khác thay vì thực hiện điều này.


6

Một mẹo thường hữu ích: Sử dụng các ràng buộc CLP (FD) cho số học số nguyên để có được các vị từ có thể được sử dụng tự động theo nhiều hướng, do đó tránh các điều kiện và các nhánh và biến thể chuyên dụng.

Sử dụng B-Prolog hoặc GNU Prolog, trong đó các ràng buộc như vậy có sẵn ngoài hộp, mà không cần tải bất kỳ thư viện nào.


2
Đây luôn là điều mà tôi ghét với SWI-Prolog khi thực hiện các thử thách ở đây. Sử dụng CLPFD sẽ làm cho một số thứ gọn gàng hơn và ngắn hơn, nhưng bạn phải thêm rất nhiều mã bổ sung để làm cho nó hoạt động (bao gồm rất nhiều ...) thường không làm cho nó có giá trị. Tôi nghĩ rằng tôi chỉ từng sử dụng nó trong câu trả lời này .
Gây tử vong vào

3
Tôi cũng ghét điều đó và chắc chắn thời gian cuối cùng sẽ đến khi library(clpfd)có sẵn dưới dạng thư viện được tải sẵn hoặc ít nhất là tự động tải trong SWI-Prolog. Có thể mất vài năm cho đến khi số học khai báo được hiểu và đánh giá đầy đủ bởi tất cả người dùng, những người hiện đã tích lũy được hàng thập kỷ kinh nghiệm với các tính năng cấp thấp, lỗi thời. Bạn càng sử dụng và ủng hộ CLP (FD), chúng tôi sẽ càng sớm nhận được nó theo mặc định. Cho đến lúc đó, bạn có thể chỉ cần đưa :- use_module(library(clpfd)).vào ~/.swiplrcvà chỉ ra rằng bạn đang sử dụng "biến thể" đó của SWI-Prolog.
chiếu

6

Sử dụng các toán tử số học làm các hàm xây dựng và cặp khuyết điểm

Nếu bạn cần vượt qua một cấu trúc duy nhất bao gồm hai hoặc nhiều giá trị, điều rõ ràng nhất để sử dụng là một danh sách, ví dụ [A,B]. Điều đó thực sự dài dòng, mặc dù.

Có một sự thay thế. Các giá trị Prolog có thể lưu trữ một cấu trúc lồng nhau tùy ý, không được đánh giá. Đây là một ví dụ cho thấy cách thức hoạt động:

| ?- member(member(A,B),C).
C = [member(A,B)|_] ? ;
C = [_,member(A,B)|_] ? ;
(etc.)

member(A,B)chỉ là một tuple được đặt tên trong tình huống này và bên ngoài member(là một lời gọi hàm) đang xử lý nó như vậy.

Mặc dù các bộ dữ liệu được đặt tên khá hữu ích trong lập trình Prolog không chơi gôn, chúng có thể thậm chí còn dài dòng hơn so với cách tiếp cận danh sách. Tuy nhiên, chúng ta có thể sử dụng khá nhiều ký tự tùy ý trong tên của hàm tạo tuple (giả sử chúng được trích dẫn chính xác); thay vì một cái gì đó dễ thương như memberhoặc một nhân vật như thế a, chúng ta có thể làm một cái gì đó như thế này:

| ?- A = '-'('/'(1,2), '/'(3,4)).
A = 1/2-3/4

Ở đây, các nhà xây dựng tuple của chúng tôi là '-''/'. Và thật thú vị khi lưu ý những gì máy in đẹp đã làm với họ; đó là sử dụng ký hiệu infix cho các bộ dữ liệu. Điều này thực sự ngắn gọn và phân tích giống như cách hoạt động số học có thể so sánh được. (Điều này cũng giải thích tại sao sử dụng số học iskhông =; A = 1+2có thể thống nhất Avới các tuple '+'(1,2) , vì vậy cú pháp riêng biệt là cần thiết để thực sự đánh giá biểu thức số học unevaluated.) Bởi vì một constructor tuple phải được gọi là một cái gì đó , bạn cũng có thể sử dụng một nhân vật có một ngắn gọn cú pháp (và như một phần thưởng, -/cũng là một số lựa chọn phổ biến nhất trong mã không chơi gôn khi họ muốn một hàm tạo tuple nhanh chóng thay vì một cái gì đó có ý nghĩa, theo cách tương tự ithường được sử dụng như một biến vòng lặp, vì vậy chúng hoàn toàn hợp lý để sử dụng trong bạn đầu vào và đầu ra nếu bạn tình cờ muốn một tuple ở đó vì một số lý do).

'-''/'là những lựa chọn tốt cho các nhà xây dựng tuple bởi vì họ có quyền ưu tiên ứng xử tốt và hữu ích, cho phép bạn viết tuple nghĩa đen một cách chặt chẽ. Tuy nhiên, lưu ý rằng bạn không cần phải lo lắng về quyền ưu tiên khi các giá trị trung gian được tạo ra trong chương trình. Prolog giữ các bộ dữ liệu được lưu trữ dưới dạng cây chứ không phải là mã nguồn và các máy in đẹp có thể xuất ra một cách rõ ràng:

| ?- A = '-'('-'(1,2), '-'(3,4)).
A = 1-2-(3-4)

Vì cú pháp tuple rất ngắn gọn ( f(A,B)không ngắn hơn f(A-B)), nên bạn có thể thay thế nhiều đối số dự đoán bằng các tuple miễn phí, nghĩa là nếu một vị từ cần truyền hai hoặc nhiều đối số của nó cho một vị từ khác, bạn có thể thường xuyên tạo chúng thành một tuple và chỉ vượt qua tuple (mặc dù điều này sẽ yêu cầu thay đổi tất cả các cuộc gọi thành vị ngữ, ngoài chính vị ngữ đó, để sử dụng một hỗn hợp thích hợp của các hàm tạo tuple và dấu phẩy).

Một ưu điểm khác của cú pháp này là nếu bạn cần sử dụng danh sách nội bộ (thay vì tương tác với các vị từ chuẩn); một danh sách về cơ bản chỉ là một tập hợp các ô khuyết điểm lồng nhau và một ô khuyết chỉ là một bộ dữ liệu với hàm tạo '.', như có thể thấy ở đây:

| ?- Q = '.'('.'(A,B),'.'(C,D)).
Q = [[A|B],C|D]

Nếu mã của bạn sử dụng danh sách "thủ công", việc sử dụng một hàm xây dựng tuple ít cồng kềnh hơn sẽ có ý nghĩa hơn nhiều '.'. Một lựa chọn phổ biến đối với tôi là đại diện cho một ô khuyết điểm '/'(Tail,Head)(vì đó là phần dễ đọc nhất mà bạn có thể nhận được trong đầu ra gỡ lỗi mà không lãng phí các ký tự). Lưu ý rằng bạn cũng có thể muốn []tương đương của riêng bạn ; bạn có thể sử dụng []nhưng nó dài hai byte và có rất nhiều nguyên tử một byte (tất cả các chữ cái viết thường) mà bạn có thể sử dụng thay thế.

Vì vậy, ví dụ, danh sách sau đây:

[1,2,3]

có thể được chuyển đổi thành biểu diễn thủ công trong cùng một số ký tự như thế này:

x/3/2/1

trong khi đạt được lợi thế, các [H|T]mẫu khớp kiểu giờ đây có thể được viết chặt chẽ hơn T/H, và một bài kiểm tra đối với danh sách trống xthay vì dài hơn []. (Tất nhiên, điều này đi kèm với những bất lợi rõ ràng rằng member, appendvv, sẽ không hoạt động trên đại diện này.)


+1! Không cần báo giá đơn khi sử dụng -/. Đây là những nguyên tử hoàn toàn bình thường rồi.
chiếu

Và, không cần trích dẫn đơn xung quanh ., với điều kiện các ký tự sau đây không %phải là bố cục.
sai

5

Cú pháp ngắn hơn cho danh sách danh sách và cách khai báo bản đồ

Bạn có thể lưu byte vào danh sách danh sách. Nếu bạn có một danh sách [[1,2],[3,4]], bạn thực sự có thể khai báo nó dưới dạng [1:2,3:4], giúp tiết kiệm 4 ngoặc = 4 byte. Lưu ý rằng bạn có thể sử dụng một cái gì đó khác hơn :(ví dụ, ^).

1:2thực tế không phải là một danh sách trong trường hợp đó (trong khi đó [1,2]), nó được thể hiện trong nội bộ như :(1,2). Do đó, bạn không thể sử dụng các vị từ hoạt động trên danh sách trên các danh sách phụ sử dụng dấu hai chấm.

Thủ thuật này chủ yếu được sử dụng để khai báo bản đồ, tức là danh sách các khóa có giá trị được đính kèm. Ví dụ: nếu bạn muốn khai báo một bản đồ Mchứa chính tả của một chữ số bằng cả tiếng Anh và tiếng Pháp, bạn có thể làm một cái gì đó như thế này:

M=[0:'Zero':'Zéro',1:'One':'Un',2:'Two':'Deux', ... ]

Sau đó, bạn có thể truy xuất các phần tử của bản đồ với một vị từ được tích hợp sẵn member/2. Ví dụ: nếu bạn muốn chữ số và từ tiếng Anh tương ứng với 'Quatre'in M, bạn có thể làm:

member(Digit:Name:'Quatre',M).

1
Bạn có thể khái quát điều này. Ví dụ, bạn có thể wirte [[1,2],[3,4]]như 1*2+3*4, đó là +(*(1,2),*(3,4))và do đó cũng chỉ sử dụng một byte mà bạn nếu không sẽ cần hai, mở và ngoặc đóng.
chiếu

Danh sách trích dẫn kép thậm chí còn ngắn hơn: "abc"thay vì[a,b,c]
sai

5

Một mẹo nhỏ: Khi bạn cần thất bại , hãy sử dụng một cái gì đó tương đương với  false / 0 , nhưng ngắn hơn, chẳng hạn như:

? - lặp lại, viết danh sách (hi), 0 = 1 .

3
Buộc The Art of Prolog quote: " lập trình Trong Prolog (ngược lại, có lẽ, với cuộc sống nói chung) Mục tiêu của chúng tôi là thất bại càng nhanh càng tốt ". Sự thật thú vị: bạn cũng có thể sử dụng \+!như một cách kỳ lạ 3 byte để không thực sự kích hoạt việc cắt giảm !(xem điều này để biết tại sao ). Tôi không nghĩ có thể thất bại dưới 3 byte.
Gây tử vong vào

Tôi cũng đã suy nghĩ về câu nói đó khi tôi viết bài này ;-)
mat

2
Chúng tôi thực sự cần cả hai phiên bản, \+!dán bên trái cho các nhân vật đồ họa khác, trong khi 0=1dán bên trái cho tên.
sai

5

Sử dụng lại một vị ngữ với các chế độ gọi khác nhau

Ví dụ: bạn có thể phân tích cú pháp và in một cấu trúc với cùng một vị từ, một lần với một đối số biến và lần khác với một thuật ngữ cơ bản. Tôi đã sử dụng phương pháp này trong Make the Stretchy Snakes Kiss . Điều này là không thể trong tất cả các thách thức, tất nhiên.

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.