Progetta un computer con un set di istruzioni!


31

Avviso: sono disposto a dare una generosità a qualsiasi risposta che trovo interessante.

La tua sfida è progettare un computer con set di istruzioni (OISC) completo di Turing :

Un OISC è una macchina astratta che utilizza solo un'istruzione - ovviando alla necessità di un codice operativo del linguaggio macchina. Con una scelta giudiziosa per la singola istruzione e risorse infinite, un OISC è in grado di essere un computer universale allo stesso modo dei computer tradizionali che hanno più istruzioni.

Ecco alcuni esempi di singoli comandi che rendono un OISC completo di Turing.

Regole:

È necessario fornire una sua interpretazione o prova

Devi fornire un interprete per la tua lingua. Questo interprete dovrebbe essere limitato solo dalla memoria / dal tempo (ad es. Non deve avere restrizioni imposte dall'utente). Se non fornisci un interprete per la tua lingua (per qualsiasi motivo diverso dalla pigrizia) devi dimostrare che è possibile scrivere. Un interprete deve essere possibile .

Devi dimostrare la sua completezza di Turing

Devi includere una prova formale che la tua lingua è Turing completa. Un modo semplice per farlo è dimostrare che può interpretare o avere lo stesso comportamento di un altro linguaggio completo di Turing. Il linguaggio più semplice da interpretare sarebbe Brainf ** k .

Ad esempio, un linguaggio normale che ha tutti gli stessi comandi di Brainf ** k (e la stessa mancanza di restrizioni di memoria imposte dall'utente) è Turing completo perché tutto ciò che può essere implementato in Brainf ** k può essere implementato nella lingua .

Ecco un elenco di lingue complete di Turing molto semplici da implementare.

Requisiti OISC aggiuntivi

  • Questo OISC dovrebbe avere solo un'istruzione - non può avere più istruzioni con una di esse che lo rende Turing completo.

  • Il tuo OISC può utilizzare qualsiasi sintassi che ti piace. Dovresti definire nella tua risposta cosa sono le istruzioni, quali sono i dati e cosa è un no-op (es. Spazi bianchi). Essere creativo!

  • Gli argomenti non devono solo essere numeri interi. Ad esempio, /// è un bellissimo esempio di OISC completo di Turing.

  • Come e se l'input e l'output sono presi e dati sono lasciati a te. La maggior parte degli OISC implementa l'I / O tramite posizioni di memoria specifiche, ma potrebbero esserci altri modi per farlo e si è incoraggiati a trovarne uno.

  • Una risposta valida deve fornire un codice di esempio nel tuo OISC, includendolo nel post o collegandolo a una semplice sfida risolta nella lingua.

Voto

Elettori, ricordatevi di non votare proposte noiose. Esempi:

  • lenguage -equivalents
  • Un'implementazione di un OISC esistente (risponditori, crearne uno tuo!)
  • Un "OISC" in cui il primo argomento specifica un comando da chiamare ( esempio )

Tuttavia, dovresti valutare proposte interessanti e creative, come:

  • Un OISC basato su un'equazione matematica
  • Uno ZISC completo di Turing basato su una rete neurale
  • Un OISC in cui l'I / O di output avviene in modi diversi da determinati percorsi di memoria

vincente

Come nel caso , vince la risposta con il maggior numero di voti! In bocca al lupo!


10
Che cos'è un'istruzione? E come li contiamo?
Wheat Wizard

1
@NoOneIsHere vorrei sapere abbastanza per votare xD
Brian H.

2
Ho declassato questo. Penso che sia un'idea molto interessante, ma non spieghi esattamente cosa sia un OISC e come confermare qualcosa. Ho fatto di BF un OISC, ma questo è chiaramente contro lo spirito della domanda, ma tecnicamente valido.
NoOneIsHere

1
@MDXF non credo che ottieni ///: ha un comando di sostituzione e ha comandi di stampa, che non sono solo un effetto collaterale del comando di sostituzione
Limone distruttibile

1
@NoOneIsHere Perché il concorso di popolarità . Sì, è valido, ma ha un punteggio scarso (votazione), quindi non vincerà.
user202729,

Risposte:


20

XOISC

Questo OISC si basa sul combinatore X di Fokker che è definito come segue:

X=λf .f (λg h x .g x (h x)) (λa b c .a)

Se riconosciamo il fatto che il calcolo SKI è Turing completo, anche il suddetto combinatore è Turing completo. Questo perché S , K e I possono essere scritti in termini di X , in questo modo:XSKIX

S=X (X X)K=X XI=S K K=X (X X) (X X) (X X)

Come funziona XOISC

Internamente XOISC ha uno stack (inizialmente vuoto), da lì l'istruzione che prende come argomento fa quanto segue:n

  • Pop elementi (funzioni f 1f N ) dallo stack, premi f 1 ( f 2 ( ( f N X ) ) )nf1fNf1 (f2 ((fN X)))

Una volta che non sono rimaste più istruzioni XOISC invierà tutti gli argomenti della riga di comando (se presenti) nello stack, ad esempio:

[s1,, sMstack before, a1,, aNarguments]

Il calcolo finale sarà .((((s1 s2)) sM) a1))aN


Poiché l'unica istruzione in XOISC accetta solo un argomento (offset della memoria), non vi è motivo di utilizzare un nome per tale istruzione. Quindi un file sorgente valido costituirà solo numeri interi separati da newline o spazi bianchi, come ad esempio:

0 0 2 0 1 0 1

Provalo online!

Esempio

Facciamo l'esempio sopra (stack che cresce verso destra):

0pop 0 and apply (ie. push single X):[X]0again simply push X:[X, X]2pop 2 (a,b) and push a (b X):[X (X X)]0simply push X:[X (X X), X]1pop 1 (a) and push a X:[X (X X), X X]0simply push X:[X (X X), X X, X]1pop 1 (a) and push a X:[X (X X), X X, X X]

Infine valuta lo stack: o con meno parentesi X ( X X ) ( X X ) ( X X ) che riconosciamo come la buona vecchia identità S K K funzione.((X (X X)) (X X)) (X X)X (X X) (X X) (X X)S K K

Completezza di Turing

Idea di prova

Per completare il Turing di XOISC, dobbiamo essere in grado di tradurre qualsiasi interfogliatura (valida) di parentesi e combinatori Questo è possibile perché quando si apre, si applica e si spinge lo fa in modo associativo a destra (l'applicazione della funzione è associativa a sinistra).X

Per tradurre qualsiasi espressione , esiste un modo semplice per farlo: fai apparire sempre tanti elementi in modo tale che dall'inizio del livello corrente tra parentesi rimanga solo un elemento.X

Ad esempio, l'espressione precedentemente utilizzata: ((X (X X)) (X X)) (X X)

  • per ottenere , abbiamo semplicemente bisogno di unX0
  • dopo ci troviamo tra un nuovo livello di parentesi, quindi abbiamo di nuovo solo bisogno di un 0
  • ora due parentesi chiuse, quindi abbiamo bisogno di pop 2 elementi: 2
  • di nuovo siamo tra un nuovo livello di parentesi, quindi abbiamo bisogno di un 0
  • due parentesi, quindi richiudere a 2
  • e lo stesso di nuovo

Quindi finiamo con un programma XOISC diverso (ma semanticamente equivalente):

0 0 2 0 2 0 2 Provalo online!

Se rimaniamo con questa strategia, possiamo facilmente trasformare qualsiasi espressione composta da combinatori in un programma XOISC che lascia solo una singola funzione nello stack.X

Prova formale

Dato che il calcolo SKI è Turing completo, dobbiamo mostrare due cose:

  1. l' -combinator è una base per il calcolo SKIX
  2. XOISC è in grado di rappresentare qualsiasi espressione formata con il combinatore X

La prima parte - dimostrando le tre uguaglianze nell'introduzione - è molto noiosa e richiede spazio, ma non è molto interessante. Quindi, invece di inserirlo in questo post, puoi trovarlo qui * .

La seconda parte può essere dimostrata dall'induzione strutturale , anche se è più facile dimostrare un'affermazione leggermente più forte: vale a dire, per qualsiasi espressione formata da -binatori c'è un programma che lascerà quella espressione come singola espressione nello stack:X

Ci sono due modi di costruire una tale espressione, sia essa la X per sé o è f g per alcune espressioni f e g :XXf gfg

Il primo è banale in quanto 0lascerà nello stack come una singola espressione. Supponiamo ora che ci siano due programmi ( F 1 ... F N e G 1 ... G K ) che lasceranno f e g come singola espressione nello stack e dimostrino che l'istruzione vale anche per f g :XF1FNG1GKfgf g

Il programma genererà prima f sullo stack e quindi genererà g ma invece di far apparire solo parti di g , farà apparire anche f e lo applicherà, tale da lasciare la singola espressione f g nello stack. ∎F1FN G1GK1 (GK+1)fggff g

Interprete

ingressi

Dal momento che il calcolo lambda non tipizzato ci richiede di definire i nostri tipi di dati per tutto ciò che vogliamo e questo è ingombrante, l'interprete è a conoscenza dei numeri della Chiesa - questo significa che quando fornisci input trasformerà automaticamente i numeri nel loro corrispondente numero della Chiesa.

Ad esempio, ecco un programma che moltiplica due numeri: provalo online!

Puoi anche fornire funzioni come argomenti usando gli indici di De Bruijn , ad esempio il Scombinatore \\\(3 1 (2 1))(o λλλ(3 1 (2 1))). Tuttavia riconosce anche il S, K, Ie, naturalmente, Xcombinatore.

Produzione

Per impostazione predefinita, l'interprete verifica se l'output codifica un numero intero, in caso contrario produrrà il numero corrispondente (oltre al risultato). Per comodità c'è la -bbandiera che dice all'interprete di provare invece ad abbinare un booleano (vedi l'ultimo esempio).

assembler

Ovviamente qualsiasi linguaggio di basso livello ha bisogno di un assemblatore che converta in esso un linguaggio di alto livello, puoi semplicemente usare qualsiasi input (vedi sopra) e tradurlo in un programma XOISC usando il -aflag, provalo online! **


* Nel caso in cui il collegamento non sia attivo, in questo post è presente una copia come commento HTML.

** Questo si traduce in un programma che verifica la primalità, provalo online!


1
C'è un motivo per cui hai scelto il combinatore X anziché il combinatore Iota?
Esolanging Fruit,

1
@EsolangingFruit: Sì, ci sono anche molte altre opzioni, alla fine ho optato per quella perché usa il minor numero di applicazioni per costruire SK. Sembrava che avrebbe funzionato al meglio (ma non ho fatto un confronto da solo).
ბიმო

1
Btw. c'è un bel confronto tra diversi combinatori nel documento collegato se sei interessato.
ბიმო

19

Disegnare

Draw è un OISC che agisce su una griglia 2D, contrassegnando i quadrati in modo simile alla macchina B Wang. Tuttavia, per mantenere la lingua il più semplice e OISC-y possibile, tutte le istruzioni (di cui ce ne sono un totale complessivo) segnano il quadrato appena calpestato e, per essere in grado di fermarsi, calpestare un quadrato contrassegnato termina il programma.

Il programma è costituito da una sequenza di linee contenenti un identificatore di linea (stringa arbitraria che non include # o spazio bianco), due numeri interi ( xe y) e altri due identificatori di linea ( ae b).

Il programma viene eseguito come segue:
A partire dalla linea di identificata come startcon il puntatore che indica la posizione (0, 0), spostare il puntatore dalla quantità data dal xe ye segnano la piazza il puntatore è ora in poi (a meno che la piazza è già contrassegnata, nel qual caso l'esecuzione termina). Quindi, saltare alla linea ase almeno uno dei quadrati direttamente adiacenti è contrassegnato, e allineare baltrimenti.

Gli interpreti sono incoraggiati a produrre il risultato finale della griglia come una sorta di immagine, tela, ecc.

Turing-Completezza

Draw è Turing-complete in quanto è possibile compilare una versione modificata (chiamata Alternate) di una macchina Minsky nella lingua.

Alternate agisce in modo simile a una macchina Minsky a due contatori, ma c'è una grande limitazione posta sui comandi: i comandi devono alternare tra il primo e il secondo contatore. Per ovviare a questa modifica, è stato aggiunto un ulteriore comando: nop. Questo comando non modifica affatto il contatore target, il che rende possibile "sommare" le modifiche consecutive a un contatore, soddisfacendo la restrizione descritta sopra. Questo significa anche che il registro che deve essere modificato non deve essere dato e, per ogni dato comando, può essere dedotto direttamente dalle istruzioni da cui l'esecuzione può saltare ad esso.

Esempio: questa macchina Minsky

1 inc A 2
2 inc A 3
3 dec A 3 4
4 halt

si trasforma in questo programma alternativo:

1 inc 2
2 nop 3
3 inc 4
4 nop 5
5 dec 6 8
6 nop 5
7 halt
8 halt

Questa limitazione è necessaria a causa del modo in cui l'eventuale programma Draw gestisce i registri, vale a dire che non differisce affatto tra loro. Al contrario, il programma Draw copia semplicemente il registro che non è stato modificato dall'istruzione precedente, modificandolo in base all'istruzione in esecuzione.

Quindi, il programma alternativo viene tradotto direttamente in Draw come segue:

Il programma inizia con questo blocco.

start 0 0 a a
a 3 0 b b
b -3 1 c c
c 3 0 d d
d -3 2 e e
e 3 0 f f
f 3 -3 i1_a i1_a

inc, decE nopsono tradotti in quasi nello stesso modo come l'altro. In tutti i casi, non vi è alcuna differenza tra la modifica del primo o del secondo registro (come spiegato sopra). Ecco un incremento, equivalente a inc 2:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 0 i2_z i2_y
i1_f 5 0 i2_z i2_y

Modificare i numeri nelle i1_xparti nell'indice dell'istruzione corrente e nelle i2_xparti nell'indice dell'istruzione successiva da eseguire.

L' nopistruzione può essere tradotta come tale:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 -2 i2_z i2_y
i1_f 5 -2 i2_z i2_y

Questo è un decremento:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 -2 i3_z i3_y
i1_f 5 -4 i2_z i2_y

i3_x si riferisce all'istruzione da chiamare se il contatore è già 1.

halt:

i1_y 0 0 0 0
i1_z 0 0 0 0

Cambia le etichette in modo appropriato e semplicemente catena tutto insieme. Facendo questo per l'esempio dall'alto, si ottiene il programma Draw nel repository dall'alto.

interpreti

Attualmente ci sono due interpreti, entrambi scritti in Python. Si possono trovare sul repository GitHub di Draw .

  1. draw.py : questo interprete è pensato per la riga di comando e prende l'origine del programma come argomento. Dopo ogni passaggio, emette il comando che è stato eseguito e la posizione del puntatore dell'istruzione; dopo l'arresto del programma, stampa il numero di celle contrassegnate.
  2. draw_golly.py : Questa versione utilizza Golly per l' output grafico più semplice per lo scopo sbagliato , prendendo l'origine tramite una finestra popup all'avvio dello script. Golly può essere un po 'schizzinoso con Python, quindi assicurati di aver installato Python 2 (e non mescolare Golly a 32 bit con Python a 64 bit o viceversa). L'output viene fornito tramite la griglia di celle incorporata di Golly.

L'immagine seguente è un esempio per l'output del secondo interprete. L'esecuzione del programma di esempio nel repository fornisce questo (o simile):


1
Stupefacente! Complimenti per aver trovato un modo davvero unico di affrontare la sfida.
MD XF,

La tua lingua non ha bisogno di fermarsi affatto per essere completa. La regola 110 non termina, ma è comunque completa.
Akangka,

+1 per Golly il miglior simulatore di automi cellulari di sempre.
Altamente radioattivo il

14

-3

Ecco il senso.

Memoria

La memoria è una mappa di nastri, in cui le chiavi sono stringhe e i valori sono numeri interi di dimensioni arbitrarie.

Inoltre, esiste un set di etichette in cui il programma può passare.

C'è uno stack, che contiene gli operandi, che sono stringhe.

C'è un offest, che controlla a quale dei nastri della memoria può accedere.

L'unica istruzione

-. Innanzitutto, fa LABELuscire una stringa dallo stack. Se ciò LABELnon è definito come etichetta, definisce l'etichetta e cancella la fonte di quell'etichetta (cioè da dove è stata spinta) e l'istruzione corrente. In caso contrario, esegue il seguente calcolo, utilizzando i primi due valori Ae B:

if mem[A] < mem[B]:
    jump to LABEL
if mem[A] != mem[B]:
    mem[A]--
else:
    mem[B]++

Nota che se ci sono argomenti in eccesso o argomenti insufficienti, il programma si guasta, mostrando lo stato del programma.

L'offset può essere modificato accedendo al valore di ..

Codice di esempio

X-

i i X-
i i X-
i i X-
i i X-
i i X-
i i X-
i i X-

Questo imposta la variabile isu 7, aumentando i 7tempi.

X-

i i X-
i i X-
i i X-
LOOP-
    a a X-
    a a X-
    j i LOOP-

Questo si moltiplica i+1per la costante 2.

Prova della completezza di Turing

Ignorando le dimensioni int di C ++ (ovvero, supponendo che siano infinite), -3 è Turing Complete mediante riduzione a Brainfuck a 3 celle . Non posso ignorare questa dimensione perché può essere scritto un interprete per -3 su un computer con memoria infinita che ha celle arbitrariamente grandi.

Credo anche che qualsiasi BCT possa essere scritto come un programma -3.


Dato che amo migliorare il mio contenuto, per favore, una spiegazione riguardante il voto negativo sarebbe apprezzata
Conor O'Brien,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.