Prologでのゴルフのヒント


16

Prologでゴルフをするための一般的なヒントは何ですか?私は、Prologに少なくともある程度固有の一般的なゴルフ問題のコードに適用できるアイデアを探しています(たとえば、1文字の変数は、プログラムのサイズを小さくするためにPrologに固有ではありません)。

Prologの実装に固有のものかどうかをヒントに明記してください(例:SWI-Prolog固有のビルトイン)

回答ごとに1つのヒントのみ、または同じメインアイデアに密接に関連するヒントのリストのみを投稿してください。


1
このprologタグは無意味です。Interlogt Prologチャレンジがない限り、それは必要ありません。

回答:


10

述語名に演算子を使用する

演算子が事前定義された演算子(ここにリストされている)のいずれかであり、述部としてまだ定義されていない限り、名前として述部演算子を与えることは可能です。これにより、演算子の述語を通常のname(arg1,arg2,etc..)形式で記述する必要がなく、演算子で期待されるように呼び出すことができるため、述語の定義と呼び出しの両方で数バイトを節約できます。

1つおよび2つの引数述語の場合、それぞれ単項演算子と二項演算子の名前を指定できます。より高いアリティの述語については、パターンマッチングを使用して括弧を回避できます。たとえば、述語がある場合A+B+C:-...、Prologは演算子の優先順位と結合規則を使用して(A+B)+C:-...、最初の引数がパターン一致する演算子述語に変換しA+Bます。あるいはA-B+C*D:-...となる(A-B)+(C*D)最初の引数は、に一致したパターンであるようにA-B、その第二のパターンに一致しますC*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.

出力は X = 5.

オンラインでお試しください!

帰結

以来DCGsは、彼らがあまりにも名前の演算子を与えることができ述語のためのシンタックスシュガーです。これは、DCGからDCGとして呼び出すか、DCGでphrase動作するように設計された述語またはその他を使用して、DCGとして呼び出すときに期待どおりに機能します。述語としてそれらを呼び出す場合、DCG述語は差分リストに追加の2つの引数を取るため、括弧が必要です(例:のA+B-->...ように呼び出す必要があります+(A,B,...))。演算子パターンマッチングを使用する3つ以上の引数を持つDCG演算子の場合、述語として呼び出すときに、パターンマッチング演算子が正しく分布していることを確認することが重要です。

追加の引数を取らないDCGに演算子名を付けると、プログラム内でそれらを呼び出す必要がある場合に役立ちます。それ以降は、括弧を使用せずに呼び出すことができます。括弧内に保存したものが、隣接する演算子を解析するために必要な追加のスペースを失う可能性があるため、注意が必要です。

/ -->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,[]).

出力は

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

オンラインでお試しください!

注意事項

単項演算子+およびを使用すると-、Prologはor 述語の呼び出しの代わりに、+20または-20数字として解釈します。単項または名前として指定された述部は、括弧(+/1-/1+-+(20)、、-(20))ます。括弧から余分なバイトを避けることが望ましいような他の単項演算子である場合\$など、代わりの名前として使用することができます。

パターンマッチングと述語という名前の演算子の組み合わせには、完全に欠陥がないわけではありません。名前と同じ演算子を持つ2つの述語があり、パターン一致で一方が他方より厳密に一般的である場合、より一般的なものが最初に呼び出されるか、より一般的でないものが失敗する場合があります(ソース内の順序に応じて) 。たとえば、上記の例ではA-B+C*D、入力の照合に失敗した場合、Prologはを呼び出しX+Yます。length/2require Yが整数である必要があるため、これはエラーになりますC*D。これは、2つの述語がその名前と同じ演算子を持たないようにするか、それが失敗した場合にカットとソースの注意深い順序付けを使用することで簡単に回避できます。


2
このゴルフがコードゴルフにどれほど役立つかは驚くべきことです。このヒントだけを使用して、回答のバイト数を16%削減しました。
DLosc

6

考えられるすべてのケースを単一のルールに入れるようにしてください

Prologでプログラムを作成するクリーンな方法は、同じ述語に対して複数のルールを宣言することです。たとえば、アキュムレータを使用してリストを逆にする述語は次のようになります。

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

Code-golfでは、最初のルールを削除し;、2番目のルールの最後にa を追加して、再帰の終了をコーディングできます。

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

私たちは最初の条件を知っています r(T,[H|Z],R)Tが空の場合、つまり再帰を終了する必要がある場合にが失敗するがその後にor句として終了を追加できます。

同じ原則が多くの状況で機能します。ただし、実際にこれを行うよりも、別のルールを宣言する方が短い場合があることに注意してください。


6

頻繁に役立つ1つのトリック:CLP(FD)制約を使用する整数演算にを使用して、複数の方向で自動的に使用できる述語を取得し、条件と専用の分岐およびバリアントを回避します。

B-PrologまたはGNU Prologを使用します。このような制約は、ライブラリをロードする必要なく、そのまま使用できます。


2
ここでチャレンジするとき、これは常にSWI-Prologで嫌いなものです。CLPFDを使用すると、いくつかのものがより簡潔で短くなりますが、それを機能させるために多くの余分なコードを追加する必要があります(インクルード自体はたくさんあります...)、通常は価値がありません。この答えで使ったことがあると思う。
16年

3
私もそれが嫌いで、SWI-Prologでもプリロードさlibrary(clpfd)れたライブラリまたは少なくとも自動ロードされたライブラリとして利用できるようになる時期が最終的に来ることは確かです。宣言的算術がすべてのユーザーに完全に理解され、評価されるまでには数年かかる場合があります。ユーザーは今や、時代遅れの低レベルの機能に関する長年の経験を蓄積しています。CLP(FD)をより多く使用し、提唱すればするほど、デフォルトで早く入手できます。それまでは、単純に置くことができ、あなたの中であなたはSWI-Prologのの「変異体」を使用していることを、ちょうど状態。:- use_module(library(clpfd)).~/.swiplrc
マット

6

タプルコンストラクターとコンスペアとして算術演算子を使用する

2つ以上の値で構成される単一の構造体を渡す必要がある場合、使用する最も明白なものはリストです。たとえば、 [A,B]です。しかし、それは本当に冗長です。

別の方法があります。プロローグ値は、評価されない、ほぼ任意のネスト構造を格納できます。これがどのように機能するかを示す例は次のとおりです。

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

member(A,B) この状況では単なる名前付きタプルであり、外部 member(関数呼び出し)はそれをそのように扱っています。

名前付きタプルは、ゴルフを使用しないPrologプログラミングではかなり有用ですが、リストアプローチよりもさらに冗長に見えるかもしれません。ただし、タプルコンストラクターの名前にはほとんど任意の文字を使用できます(適切に引用されている場合)。のようなかわいいmemberキャラクターやのような単一のキャラクターの代わりに、次のようなaことができます。

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

ここでは、私たちのタプルコンストラクタがある'-''/'。また、プリティプリンターがそれらを使用して何を行ったかに注目するのは興味深いことです。タプルに中置表記法を使用しています。これは本当に簡潔で、同等の算術演算と同じ方法で解析します。(これはまた、なぜ算術の用途を説明してisいない=A = 1+2統一うAタプル '+'(1,2)。ので、別の構文は、実際に未評価の算術式を評価するために必要とされる)タプルコンストラクタを呼び出す必要がありますので、何か、あなたにも簡潔を持つ文字を使用することができます構文(およびボーナスとして、-および/iループ変数として頻繁に使用されるのと同じように、意味のあるものではなく迅速なスロータプルコンストラクターが必要な場合、非ゴルフコードでも最も一般的な選択肢のいくつかは、あなたの中で使用するのに完全に合理的です何らかの理由でタプルが必要になった場合の入出力)。

'-'そして'/'、彼らはあなたが簡潔タプルリテラルを記述することができ、行儀と便利な優先度を持っているので、タプルコンストラクタのために良い選択です。ただし、プログラム内で中間値が生成される場合、優先順位について心配する必要はありません。プロローグは、タプルをソースコードとしてではなくツリーとして保存し、プリティプリンターはそれを明確に出力できます。

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

タプル構文は簡潔なので、ですので(f(A,B)よりも短くなっていませんf(A-B))、複数の述語引数をタプルに無料で置き換えることができます。つまり、述語が2つ以上の引数を別の述語に渡す必要がある場合、タプルを渡して、タプルを渡すだけです(ただし、タプルコンストラクターとコンマの適切な組み合わせを使用するには、述語自体に加えて、述語へのすべての呼び出しを変更する必要があります)。

この構文のもう1つの利点は、リストを内部で使用する必要がある場合(標準の述語と相互運用するのではなく)です。リストは基本的にネストされたコンスセルのセットであり、コンスセルはコンストラクターを含むタプル'.'です。

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

コードで「手動で」リストを使用する場合、のかさばらないタプルコンストラクターを使用する方が理にかなっています'.'。私の一般的な選択は、コンスセルを表す'/'(Tail,Head)ことです(文字を無駄にせずにデバッグ出力で取得できる最も読みやすいものだからです)。おそらく、独自の[]同等物も必要になることに注意してください。あなた使うことができます[]が、それは2バイトの長さ、そしてあなたが代わりに使用できることを1バイトの原子(すべて小文字)の多くがあります。

したがって、たとえば、次のリスト:

[1,2,3]

次のように、同じ文字数の手動表現に変換できます。

x/3/2/1

- [H|T]スタイルのパターンマッチをより簡潔に記述できるようになりT/H、空のリストに対するテストをxより長いものではなく単なるものとしてテストできるようになりました[]。(もちろん、これには、この表現では、などが機能しないという明らかな欠点がmemberありappendます。)


+1!-およびを使用する場合、単一引用符は必要ありません/。これらはすでに完全に正常な原子です。
マット

そして、.次の文字が%レイアウトでもなければ、を囲む単一引用符は必要ありません。

5

リストのリストのより短い構文とマップを宣言する方法

リストのリストにバイトを保存できます。リストがある場合[[1,2],[3,4]]、実際にとして宣言でき[1:2,3:4]ます。これにより、4つのブラケット= 4バイトが節約されます。:(たとえば^)以外のものを使用できることに注意してください。

1:2は実際にはその場合のリストではなく([1,2]以前は)、内部的にはとして表され:(1,2)ます。したがって、コロンを使用するサブリストのリストで機能する述語は使用できません。

このトリックは、主にマップ、つまり値が付加されたキーのリストを宣言するために使用されます。たとえば、M英語とフランス語の両方の数字のスペルを含むマップを宣言する場合、次のようなことができます。

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

次に、たとえばのような組み込みの述語を使用して、マップの要素を取得できますmember/2。たとえば、'Quatre'in Mに対応する数字と英語の単語が必要な場合は、次のようにします。

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

1
これを一般化できます。たとえば、あなたはwirteできる[[1,2],[3,4]]ように1*2+3*4なる、+(*(1,2),*(3,4))したがって、また、あなたがそうでなければ開閉括弧のために、2を必要とする1バイトのみを使用します。
マット

二重引用符のリストはさらに短いです:"abc"の代わりに[a,b,c]
偽の

5

巧妙なトリック:失敗する必要がある場合は、次のようにfalse / 0と同等の、しかしより短いものを使用し  ます

? -繰り返し、のwriteln(HI)、0 = 1

3
必須のプロローグの引用:「プロローグプログラミングでは(おそらく、一般的な生活とは対照的に)私たちの目標は、できるだけ早く失敗することです」。おもしろい事実:\+!実際にカットをトリガーしない3バイトの異常な失敗方法として使用することもできます!理由についてはこちらを参照してください)。3バイト未満で失敗する可能性はないと思います。
16年

私はこれを書いたときにその引用についても考えていました;
マット

2
両方のバージョンが必要です。\+!左に他のグラフィックキャラクターを0=1接着し、名前に左を接着します。
偽の

5

異なる呼び出しモードで述語を再利用します

たとえば、同じ述語を使用して構造を解析および出力できます。1回は可変引数を使用し、もう1回はグラウンドタームを使用します。Make the Stretchy Snakes Kissでこのアプローチを使用しました。もちろん、これはすべての課題で可能というわけではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.