Questa risposta fa parte di una collaborazione tra me e 0 '. Entrambi abbiamo lavorato insieme su questo, l'unica ragione per cui sto postando è perché ho vinto Rock, Paper, Scissors.
\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}.
Provalo online!
Spiegazione
Questa risposta è un perfetto esempio di ciò che rende divertente il golf nel prologo.
Questa risposta utilizza il potente sistema Prologs per grammatiche definite in clausole. Ecco la nostra grammatica ungolfed un po '.
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),!.
La prima regola di costruzione è:
head(1)-->[].
Questo dice a Prolog che la stringa vuota corrisponde a 1.
La nostra seconda regola di costruzione è un po 'più complessa.
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
Questo ci dice che qualsiasi stringa non vuota contiene parentesi attorno a una clausola con queste stesse regole, a destra di una clausola con queste stesse regole.
Ci dice anche che il valore di questa clausola ( Q
) segue la regola:
{prime(N,Y),Q is Y*B}
Abbattendo questo, Q
è il prodotto di 2 numeri Y
e B
. B
è solo il valore della clausola a sinistra ed Y
è il N
primo punto in cuiN
è il valore della clausola tra parentesi.
Questa regola copre entrambe le regole di formazione dell'albero dei fattori
- La concatenazione si moltiplica
- La recinzione prende l'ennesimo numero primo
Ora per le definizioni del predicato. Nella versione ungolf ci sono due predicati in gioco (nel mio codice attuale ho incatenato via i predicati). I due predicati rilevanti qui sono isprime/1
, che corrisponde a un numero primo e prime/2
, che, dato N
e Y
, corrisponde a iff Y
è il N
primo numero. Per prima cosa abbiamo
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
Funziona con una definizione piuttosto standard di primalità, insistiamo sul fatto che non esiste un numero compreso tra 2 e I
, incluso 2 ma I
che non divideI
.
Anche il prossimo predicato è piuttosto semplice
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Usiamo findnsols
per trovare i primi N
numeri che sono primi, quindi restituiamo l'ultimo. Il trucco qui è che, sebbene findnsols
non sia garantito trovare i numeri primi più piccoli N
, a causa del modo in cui SWI gestisce between
, troverà sempre i numeri primi più piccoli prima. Ciò significa tuttavia che dobbiamo tagliare per impedirgli di trovare più numeri primi.
I golf
Possiamo inoltrare la ragione nel nostro codice due volte. Poiché isprime
viene utilizzato solo una volta che la sua definizione può essere spostata all'interno di prime
. Il prossimo è quello di spostarsi prime
direttamente all'interno del DCG, tuttavia poiché utilizziamo un cut-in prime
per evitare findnsols
di produrre troppi numeri primi, abbiamo un piccolo problema. Il taglio, taglia l'intero DCG invece del solo bit che vogliamo. Dopo un po 'di documentazione, abbiamo scoperto che once/1
poteva essere usato per tagliare solo questa parte ma non l'intero DCG. Tuttavia, ulteriori ricerche sulla documentazione hanno rivelato che l' ->
operatore potrebbe anche essere utilizzato per eseguire un'attività simile. L' ->
operatore è approssimativamente equivalente a ,!,
così abbiamo spostato il nostro taglio dall'altra parte append/3
e sostituito con ->
.
In SWI-Prolog i predicati (e le regole) possono essere dati agli operatori come nomi che ci permettono di eliminare le parentesi normalmente richieste. In questo modo possiamo salvare 6 byte chiamando la regola \
.