Linguaggio di programmazione in cui ogni espressione ha un senso


23

Per raccomandazione lo sto ripubblicando da Stack Overflow .

Di recente ho pensato al seguente problema.

Considera il codice per un "Hello world!" Standard programma:

main()
{
    printf("Hello World");

}

Ora quasi ogni cambiamento in questo codice lo renderà completamente inutile, infatti quasi ogni cambiamento impedirà la compilazione del codice. Per esempio:

main(5
{
    printf("Hello World");

}

Ora alla domanda reale. Esiste un linguaggio di programmazione in cui ogni possibile combinazione di simboli - cioè ogni espressione - ha senso? Ho provato a pensare a una sorta di soluzione e ne sono usciti due:

  1. Postfix con un numero limitato di variabili. Essenzialmente tutte le variabili sono già definite prima di scrivere qualsiasi codice e devi lavorare solo con loro. Teoricamente è possibile che eseguire un numero arbitrario di operazioni formando una catena di molti semplici programmi, ognuno dei quali fornisce risultati ad altri. Il codice potrebbe essere scritto come una serie di caratteri nella notazione postfix;

  2. "Postfix" con una pila di variabili. Le variabili sono archiviate in una pila; ogni operazione prende due variabili dall'alto e mette il risultato al loro posto. Il programma termina quando raggiunge l'ultima operazione o variabile.

Personalmente odio entrambi. Non solo sono limitati, ma non sono eleganti. Non sono nemmeno soluzioni reali, più come soluzioni alternative, essenzialmente "offshoring" del lavoro su un processo esterno.

Qualcuno ha qualche idea su come risolvere questo problema?


48
Dato un compilatore , creare un nuovo compilatore che funziona come segue: data sorgente , passarlo al . Se è contento e produce un eseguibile, allora è quello, ma se lamenta, quindi genera un eseguibile che stampa Il compilatore accetta ogni stringa come un programma valido. C s C C C C CCsCCCYou are a bimbo.C
Andrej Bauer,

1
BF ha bisogno di un [ ]comando corrispondente (secondo la pagina Wiki). Il mio pensiero era di esaminare i codici operativi della CPU. Ma anche in questo caso, alcuni schemi possono causare un problema (ad esempio, se un codice operativo è di 3 bit, ma il programma è solo di 2 bit.) Tranne questo problema di eventuale riempimento con alcuni 0 bit in più, si può pensare a qualsiasi CPU con un set di codici operativi completo che soddisferà l'affermazione "ogni stringa è un programma valido". Forse insignificante, ma comunque valido.
Ran G.

1
Lascia che il tuo hardware sia una CPU Z-80 con 64k di RAM. Scrivi un compilatore che copia semplicemente il codice sorgente in codice ASCII nella memoria 64k (troncamento o zero-padding se necessario). Questo compilatore non fornisce mai un errore di sintassi.
Ben Crowell,

1
@Suonò. Un "compilatore" che elabora qualsiasi bitstream e lo risolve in modo che sia un bit di codice oggetto valido per il processore in questione soddisferebbe, a mio avviso, i requisiti dei PO. Probabilmente non sarebbe tremendamente difficile anche per i sistemi con set di istruzioni complessi come x86. Ho letto un articolo anni fa sulla validità dei byte casuali come programmi x86 e ho scoperto che x86 era in realtà molto più robusto di quanto gli autori inizialmente si aspettassero.
otakucode,

2
Senza ulteriori condizioni, questa domanda è noiosa: il commento di Andrej e la risposta di David danno risposte "banali". Devi inchiodare più precisamente ciò che vuoi.
Raffaello

Risposte:


31

Redcode, il linguaggio di assemblaggio dietro codewars, è stato scritto esplicitamente per avere pochissime istruzioni di arresto, perché il codice viene spesso alterato prima che alla fine venga distribuito, e più opportunità ha da fermare, meno interessante è il gioco.

Vedi pochissime lingue di questo tipo in pratica perché non vogliamo solo che un programma venga eseguito, ma vogliamo che funzioni nel modo che ci aspettiamo. Se riesci a fare un refuso e a cambiare il modo in cui il programma è stato eseguito, deve essere accettabilmente vicino al comportamento originale previsto, oppure i programmatori si sentono frustrati.

Esiste una precedenza per tali cose usando i linguaggi naturali piuttosto che i linguaggi formali, ma non è quello che definirei un campo ampio quando lo confronti con l'uso dei linguaggi formali. Se sei interessato a tali linguaggi di programmazione, la comunità di elaborazione del linguaggio naturale è dove vorrei cercare.

Un altro campo che potresti guardare è la genetica. Esistono poche sequenze genetiche che sono semplicemente non valide. Molti di questi non sono molto efficaci nelle riproduzioni, ma pochissimi quelli non validi.


1
La genetica non sembra un buon esempio. In termini di valido o non valido, stai solo parlando di replica? Perché ovviamente ogni stringa sarà un programma valido per una lingua in cui l'unica istruzione possibile è replicate this string. Non è in realtà un linguaggio di programmazione significativo, in quanto non è vicino a Turing Complete.
tel

2
@tel: Cort sta probabilmente parlando della sintesi proteica tramite mRNA, piuttosto che della replicazione. Praticamente qualsiasi sequenza genetica può essere trascritta e quindi inserita nel meccanismo di sintesi proteica: se la proteina che viene fuori è sufficientemente stabile da non essersi già degradata al momento della sua costruzione, e in tal caso se fa qualcosa di utile per l'organismo, è un'altra cosa ...
Steve Jessop,

3
Il codice genetico non è un codice per riprodursi. È (generalmente) un codice per una proteina. Se la proteina è utile è spesso una domanda diversa. Certo, diventa più interessante. Alcuni frammenti di "codice" in una sequenza genetica finiscono per essere più come un'istruzione lungo le linee di "quel codice alcune righe più in basso - a volte dovresti semplicemente ignorarlo". Ci sono molti tipi di "programmi" fantastici che cellule e virus hanno scritto combattendo l'un l'altro.
Gioele,

TECO è un altro esempio del mondo reale.
cjm

1
@cjm wow. "Un'API non termina quando hai finito di aggiungere tutto, ma quando hai finito di estrarre tutto." A meno che tu non sia TECO, allora hai finito quando esaurisci i personaggi a cui assegnare il significato.
Cort Ammon - Ripristina Monica il

16

L'idea di una macchina di Turing universale utilizza proprio un tale "linguaggio di programmazione": una codifica di macchine di Turing come numeri naturali, rappresentata ad esempio in binario, tale che ogni numero naturale indica una macchina di Turing, cioè un programma. In questa lingua, ogni stringa di zero e uno è un programma.

Se sei preoccupato che alcuni numeri possano codificare programmi non validi, questo può essere modificato come segue. Immagina di scrivere tutte le stringhe nel set di caratteri del tuo linguaggio di programmazione (diciamo, Java), in ordine lessicografico, iniziando con stringhe di lunghezza una, poi due, poi tre, ... Quindi, crea un nuovo linguaggio di programmazione lasciando il numero stand per la ° stringa nell'elenco che è un programma Java valido. Nel nuovo linguaggio di programmazione, i programmi sono solo numeri naturali e ogni numero naturale è un programma valido.nnn

Sono sicuro che ci sono anche linguaggi di programmazione esoterici in cui ogni stringa è un programma; tuttavia, se stai solo chiedendo un elenco di quelli, penso che la tua domanda sia fuori tema qui.


13

L'estensione di un linguaggio di programmazione in modo che ogni espressione abbia un senso è sempre possibile, ma non interessante. Ad esempio, puoi semplicemente assegnare il significato di "non fare nulla" a qualsiasi espressione rifiutata dalla lingua originale.

Progettare un linguaggio di programmazione in cui ogni espressione abbia un senso in un modo "puoi eseguirlo" non è particolarmente utile. Un buon linguaggio di programmazione non è solo quello in cui una scimmia può digitare su una tastiera e scrivere un programma valido, ma quella in cui un programmatore può facilmente scrivere il programma che intendeva scrivere. Scrivere programmi validi non è la parte difficile della programmazione: la parte difficile è scrivere un programma che esegue ciò che ci si aspettava da esso. Rifiutare ovviamente programmi errati è molto utile in questo senso.

Un altro modo per affrontarlo è definire completamente la semantica di tutti i possibili input, incluso specificare quale errore di compilazione, tempo di caricamento o tempo di esecuzione dovrebbe essere generato per ogni input, se presente. Vale a dire, "interrompere il programma dopo la stampa Syntax error at line 42sul flusso di errori standard" fa parte della semantica definita della lingua. Ogni espressione "ha un senso" in quanto ha un significato definito. È un significato utile? Forse - dopo tutto, se il programma è ovviamente sbagliato, rifiutarlo è utile.


12

Dai un'occhiata a Jot , un linguaggio completo di Turing basato sulla logica combinatoria, in cui ogni sequenza di 0 e 1 (inclusa una sequenza vuota) è un programma valido.


2
Questo non è un computer science risposta.
Raffaello

2
@Abdulrhman È semplice definire una biiezione tra stringhe binarie e numeri naturali. Quindi puoi codificare qualsiasi programma come numero naturale se vuoi.
CodesInChaos,

7
@Raphael Per favore, elabora o suggerisci un miglioramento della risposta, sarò felice di migliorarlo se fornirai motivi per la tua critica.
Petr Pudlák,

+1, stavo per dare una risposta simile per un linguaggio di programmazione fittizio basato su numeri naturali, ma questo è simile. AFAIK non esiste una programmazione (in uso pratico) che abbia questa caratteristica, ma si può costruirne una usando solo numeri, diciamo, dove ogni combinazione ha un significato (agire come operatori e operandi) Questa è la chiave
Nikos M.

8

Un bell'esempio è lo spazio bianco . Nella lingua corretta, qualsiasi combinazione di operatori è valida. Gli operatori sono spazio, tab e newline (in particolare "\ n"). Tutti gli altri personaggi sono considerati commenti .

Questa risposta, e in effetti la tua domanda (così come l'intera pagina Web) sono esempi di programmi validi per gli spazi bianchi (anche se potrebbero non fare nulla di particolarmente interessante).


Stavo solo pensando a questo dopo aver pubblicato la mia risposta brainfuck (la tua è migliore poiché è corretta), ma mi chiedo: un programma vuoto è ancora un programma? (cioè se quei tre caratteri mancano dall'intero flusso di file). - Ad esempio, se alla mia auto mancassero tutte le cose che l'hanno resa un'auto, sarebbe comunque un'auto?
BrainSlugs83,

Questo non è un computer science risposta. (Inoltre, "ogni stringa di spazi bianchi"! = "Ogni stringa".)
Raffaello

2
@Raphael: Ma ogni possibile stringa (incluse quelle che non contengono spazi bianchi) sono programmi validi per gli spazi bianchi - nota che qualsiasi personaggio che non è uno spazio bianco è semplicemente un commento nel linguaggio di programmazione degli spazi bianchi
slebetman

2
@slebetman Hai interpretato il mio commento tra parentesi in modo troppo letterale. Stavo parlando di token loop non accoppiati. Alcuni problemi simili negli spazi bianchi potrebbero essere: Il ritorno senza una precedente chiamata funziona? (codificato come [LF][Tab][LF]) Cosa succede se si apre una pila vuota? Cosa succede se si passa a un'etichetta non definita? Cosa succede se si definiscono etichette duplicate?
CodesInChaos,

7

Vorrei affrontare l'idea che molti manifesti hanno dato, che una tale lingua sarebbe "inutile". Forse sarebbe inutile per gli umani scrivere, manualmente, con l'intenzione di risolvere qualche compito particolare. Tuttavia, nonostante sia un caso d'uso di maggioranza per i linguaggi di programmazione, questo non è certamente l'unico caso d'uso. Vengono in mente diversi casi d'uso in cui tale lingua è utile e possiamo guardare a questi campi per esempi di tali lingue.

Innanzitutto, l'allusione di Cort Ammon alla genetica è esatta: la trasformazione del programma nella domanda (in sostituzione )di 5) può essere vista come una mutazione . Questo tipo di manipolazione è comune nel campo del calcolo evolutivo ; in particolare gli algoritmi genetici eseguono tali trasformazioni su stringhe , mentre la programmazione genetica trasforma i programmi . In entrambi i casi, di solito vogliamo assegnare un significato a ogni possibilità, poiché ciò produrrà lo spazio di ricerca più compatto.

Gli algoritmi genetici si basano su una sorta di funzione di valutazione per le stringhe; se utilizziamo un interprete del linguaggio di programmazione come nostra funzione di valutazione, allora abbiamo uno scenario in cui è utile un linguaggio di programmazione che assegni significato a tutte le possibili stringhe. Nella programmazione genetica, si presume che la nostra funzione di valutazione sia un interprete del linguaggio di programmazione, ma possiamo scegliere varie rappresentazioni per i nostri programmi; ad esempio, molti sistemi operano su alberi di sintassi astratti. Se scegliamo le stringhe come nostra rappresentazione, recuperiamo lo stesso scenario degli algoritmi genetici.

Un'altra situazione in cui si potrebbe desiderare che ogni stringa sia un programma valido è quando si elencano i programmi. Ciò è legato alla biiezione menzionata da CodesInChaos, ma potremmo preferire operare su stringhe piuttosto che su numeri naturali per diversi motivi:

  • Se esiste una struttura nella lingua, ad es. possiamo assegnare un significato alle sottostringhe, ciò può andare perso durante la traduzione in numeri naturali. In questo caso potremmo preferire usare le stringhe, per ragionare e trasformare le sottostringhe localmente, piuttosto che rappresentare l'intero programma come un numero. Questo è analogo a come potremmo preferire usare operazioni bit per bit su espressioni int piuttosto che aritmetiche, quando ogni bit ha un significato individuale. Questa è fondamentalmente una generalizzazione dello scenario evolutivo.
  • Potremmo voler generare i programmi su richiesta; per esempio, potremmo iniziare l'esecuzione di un programma che è completamente indeterminato e generare (es. casualmente) le singole istruzioni (es. caratteri) quando / se il puntatore dell'istruzione li raggiunge. Questo è comune nella teoria dell'informazione algoritmica, in cui il programma è un nastro di Turing e lo scopo è quello di caratterizzare il comportamento dei programmi generati casualmente. Ad esempio, possiamo formulare il Solomonoff prima di stringhe arbitrarie come la probabilità che una macchina di Turing universale con un nastro casuale emetta quella stringa.

In termini di linguaggi di esempio, molti sistemi di calcolo evolutivo si basano su linguaggi stack come la famiglia Push . Questi tendono a consentire flussi arbitrari di token (che potremmo rappresentare come singoli personaggi). A volte (come nell'esempio Brainfuck di BrainSlugs83) ci sono restrizioni sul bilanciamento delle parentesi; tuttavia, possiamo collegarlo a programmi di delimitazione automatica , in quanto una stringa come [potrebbe non essere un programma valido , ma è un prefisso di programma valido . Se immaginiamo che un compilatore / interprete legga il codice sorgente da stdin, allora non rifiuterà una stringa come [, semplicemente aspetterà più input prima di continuare.

Linguaggi come Logica combinatoria binaria e Calcolo lambda binario sono nati direttamente senza lavoro sulla teoria dell'informazione algoritmica, ad es. da http://tromp.github.io/cl/cl.html

La progettazione di un computer universale minimalista è stata motivata dal mio desiderio di elaborare una definizione concreta della complessità di Kolmogorov, che studia la casualità dei singoli oggetti.


2

I veri linguaggi di programmazione devono trasmettere significato alle persone , non ai computer. Come un sacco di testi divertenti con lettere quasi casualmente mescolate che fluttuano attorno allo spettacolo, le persone possono leggere incomprensibili e dare un senso a ciò, anche senza notare apertamente il maltrattamento. Basti pensare a quanto sia difficile trovare errori di battitura e altri errori simili nei testi.

Un linguaggio di programmazione come quello che chiedi farebbe capire alle persone cosa vogliono leggere, non cosa è scritto. Il debug in lingue in cui è presente un numero limitato di dichiarazioni legali, in cui non vi è molta ambiguità possibile, è già abbastanza difficile. Buone lingue riducono possibili interpretazioni dovute ad esempio a simboli o errori di battitura trasposti. Le lingue naturali sono anche famose per la loro ridondanza, per lo stesso tipo di ragione.


0

Nel linguaggio di programmazione Brainfuck , quasi ogni possibile espressione binaria può essere interpretata come un programma. - Cioè, potresti prendere un programma completamente buono, digitare un mucchio di immondizia, e sarebbe comunque compilabile / interpretabile senza problemi.

( Modifica: si scopre che è necessario abbinare l'apertura e la chiusura delle parentesi quadre, ma per il resto è vero quanto sopra.)

Raggiunge questo tramite questi due semplici metodi:

  1. Tutti i comandi che comprende sono un singolo byte (in BF sono ad esempio tutti i caratteri ASCII singoli *).

  2. Tutti i personaggi che non comprende, scartano come commenti.

Il linguaggio di programmazione è Turing completo (nel senso che può fare qualsiasi cosa qualsiasi altra lingua possa fare).

*: Si scopre che non tutti i comandi BF sono un singolo byte ASCII - cioè le parentesi DEVONO essere abbinate - così com'è, quella lingua non soddisfa i primi criteri lì. - Ma qualsiasi lingua che soddisfi entrambi i criteri soddisferebbe ciò che l'OP chiede.


2
Non solo questo non risponde alla domanda, non è un computer science risposta.
Raffaello

1
Potresti ridefinire il linguaggio per gestirli in modo sensato. ad esempio inserendo abbastanza parentesi aperte all'inizio del programma e chiudendo parentesi alla fine del programma per renderlo bilanciato. È semplice scrivere un interprete che gestisce i programmi come se quelle parentesi esistessero senza effettivamente riscrivere il programma. Ovviamente l'avvio di un programma Brainfuck con una parentesi aperta è piuttosto inutile, poiché ignorerà tutto fino alla parentesi chiusa corrispondente.
CodesInChaos,

1
@Raphael la domanda del PO era "Esiste un linguaggio di programmazione in cui ogni possibile combinazione di simboli - cioè ogni espressione - ha senso?" - la mia risposta è "sì, ecco un esempio di uno che si avvicina, ed ecco la teoria alla base". - oltre a stabilire regole esatte per una classe di lingue che soddisferebbero i requisiti del PO, non sono sicuro di quanto più spazio per la scienza ci sia qui. Puoi fare un esempio o un link a una risorsa di ciò che speri esattamente di vedere qui? -- Grazie.
BrainSlugs83,

2
David e Gilles danno risposte informatiche. Esplorano i principi e non dicono semplicemente "il linguaggio X lo fa (quasi)". Se leggi le loro risposte, imparerai che anche le risposte di quest'ultimo modulo sono piuttosto noiose. Non è colpa tua, ma dei PO - la domanda (come una domanda di informatica) è noiosa; c'è un falso senso di complessità.
Raffaello

Si potrebbe facilmente "aggiustare" BF in modo che qualsiasi stringa venga accettata: basta fingere che ci siano abbastanza ]caratteri alla fine della fonte per abbinare tutti i [s senza pari e abbastanza [all'inizio per corrispondere a tutti senza pari ]. La semantica di [e ]può essere facilmente modificata per renderli equivalenti a questo. (ad es. se non c'è corrispondenza, ]allora [ferma l'esecuzione se il byte sul puntatore dati è zero. ]Salta all'inizio del programma in una situazione simile.) La lingua risultante sarebbe Turing completa e accetterebbe qualsiasi stringa.
Nathaniel,
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.