Il "blub paradosso" e c ++

32

Stavo leggendo l'articolo qui: link e la parte relativa al "paradosso del blub" era particolarmente interessante. Come qualcuno che codifica principalmente in c ++ ma ha esposizione ad altri linguaggi (principalmente Haskell) sono a conoscenza di alcune cose utili in questi linguaggi che sono difficili da replicare in c ++. La domanda è principalmente rivolta a persone con competenze sia in c ++ che in altre lingue, c'è qualche potente linguaggio o idioma che usi in un linguaggio che sarebbe difficile da concettualizzare o implementare se stavi scrivendo solo in c ++?

In particolare questa citazione ha attirato la mia attenzione:

By induction, the only programmers in a position to see all the differences in power between the various languages are those who understand the most powerful one. (This is probably what Eric Raymond meant about Lisp making you a better programmer.) You can't trust the opinions of the others, because of the Blub paradox: they're satisfied with whatever language they happen to use, because it dictates the way they think about programs.

Se si scopre che io sono l'equivalente del programmatore "Blub" in virtù dell'uso di c ++, sollevo la seguente domanda: ci sono concetti o tecniche utili che hai incontrato in altre lingue che avresti trovato difficile concettualizzare hai scritto o "pensato" in c ++?

Ad esempio il paradigma di programmazione logica visto in linguaggi come Prolog e Mercury può essere implementato in c ++ usando la libreria di castor, ma alla fine trovo che concettualmente sto pensando in termini di codice Prolog e di conversione all'equivalente c ++ quando si usa questo. Come un modo per ampliare le mie conoscenze di programmazione, sto cercando di scoprire se ci sono altri esempi simili di idiomi utili / potenti che sono espressi in modo più efficiente in altri linguaggi che potrei non essere a conoscenza di uno sviluppatore c ++. Un altro esempio che viene in mente è il sistema macro in lisp, che genera il codice del programma all'interno del programma sembra avere molti vantaggi per alcuni problemi. Questo sembra essere difficile da implementare e pensare dal c ++.

Questa domanda non è intesa come un dibattito "c ++ vs lisp" o qualsiasi tipo di dibattito sul tipo di guerra delle lingue. Chiedere una domanda come questa è l'unico modo in cui posso vedere possibile scoprire cose che non so di cui non conosco.

    
posta shuttle87 10.05.2011 - 05:40
fonte

10 risposte

15

Bene, visto che hai citato Haskell:

  1. Pattern Matching. Trovo che il pattern matching sia molto più facile da leggere e scrivere. Considera la definizione di mappa e pensa a come sarebbe implementata in una lingua senza abbinamento di modelli.

    map :: (a -> b) -> [a] -> [b]
    map f [] = []
    map f (x:xs) = f x : map f xs
    
  2. Il sistema dei tipi. A volte può essere un dolore, ma è estremamente utile. Devi programmarlo per capirlo veramente e quanti bug cattura. Inoltre, la trasparenza referenziale è meravigliosa. Diventa chiaro solo dopo aver programmato in Haskell per un po 'quanti bug sono causati dalla gestione dello stato in un linguaggio imperativo.

  3. Programmazione funzionale in generale. Utilizzo di mappe e pieghe anziché iterazione. Ricorsione. Si tratta di pensare ad un livello più alto.

  4. Valutazione pigra. Di nuovo si tratta di pensare ad un livello più alto e lasciare che il sistema gestisca la valutazione.

  5. Cabala, pacchetti e moduli. Avere pacchetti di download per Cabal per me è molto più comodo che trovare il codice sorgente, scrivere un makefile, ecc. Essere in grado di importare solo determinati nomi è molto meglio che essenzialmente avere tutti i file sorgente scaricati insieme e poi compilati.

Modifica: formattazione

    
risposta data 10.05.2011 - 05:54
fonte
7

Memoize!

Prova a scrivere in C ++. Non con C ++ 0x.

Troppo ingombrante? Va bene, provalo con C ++ 0x.

Verifica se puoi battere questa versione a 4 righe (o 5 righe, qualunque sia: P) compile-time in D:

auto memoize(alias Fn, T...)(T args) {
    auto key = tuple(args);                               //Key is all the args
    static typeof(Fn(args))[typeof(key)] cache;           //Hashtable!
    return key in cache ? cache[key] : (cache[key] = Fn(args));
}

Tutto ciò che devi fare per chiamarlo è qualcosa come:

int fib(int n) { return n > 1 ? memoize!(fib)(n - 1) + memoize!(fib)(n - 2) : 1;}
fib(60);

Puoi anche provare qualcosa di simile in Scheme, anche se è un po 'più lento perché accade in fase di esecuzione e perché la ricerca qui è lineare invece di hash (e beh, perché è Scheme):

(define (memoize f)
    (let ((table (list)))
        (lambda args
            (cdr
                (or (assoc args table)
                    (let ((entry (cons args (apply f args))))
                        (set! table (cons entry table))
                        entry))))))
(define (fib n)
        (if (<= n 1)
            1
            (+ (fib (1- n))
                (fib (- n 2)))))))
(set! fib (memoize fib))
    
risposta data 10.05.2011 - 08:01
fonte
5

C ++ è un linguaggio multiparadigm, il che significa che cerca di supportare molti modi di pensare. A volte una funzionalità C ++ è più scomoda o meno fluida dell'implementazione di un'altra lingua, come nel caso della programmazione funzionale.

Detto questo, non riesco a pensare in cima alla mia testa di una caratteristica del linguaggio C ++ nativo che fa ciò che yield in Python o JavaScript fa.

Un altro esempio è programmazione concorrente . C ++ 0x avrà voce in capitolo, ma lo standard attuale no e la concorrenza è un modo completamente nuovo di pensare.

Inoltre, lo sviluppo rapido - anche la programmazione della shell - è qualcosa che non imparerai mai se non lasci il dominio della programmazione C ++.

    
risposta data 10.05.2011 - 05:57
fonte
5

Coroutine sono una caratteristica linguistica estremamente utile che sostiene molti dei benefici più tangibili di altri linguaggi rispetto al C ++. Fondamentalmente forniscono stack aggiuntivi in modo che le funzioni possano essere interrotte e protratte, fornendo funzionalità simili a quelle di una pipeline alla lingua che alimenta facilmente i risultati delle operazioni attraverso filtri ad altre operazioni. È meraviglioso, e in Ruby l'ho trovato molto intuitivo ed elegante. La valutazione pigra si collega anche a questo.

Introspezione e compilazione di codice run-time / esecuzione / valutazione / qualunque siano le potenti funzionalità di C ++.

    
risposta data 10.05.2011 - 06:17
fonte
3

Avendo implementato un sistema di algebra per computer sia in Lisp che in C ++, posso dire che il compito era molto più semplice in Lisp, anche se ero un principiante assoluto della lingua. Questa natura semplicistica di tutto ciò che è lista semplifica moltissimi algoritmi. Certo, la versione C ++ è stata zillions di volte più veloce. Sì, avrei potuto rendere la versione lisp più veloce, ma il codice non sarebbe così patetico. Lo scripting è un'altra cosa che sarà sempre più facile, ad esempio è lisp. Si tratta di utilizzare lo strumento giusto per il lavoro.

    
risposta data 10.05.2011 - 06:14
fonte
2

Ho imparato Java, C \ C ++, Assembly e Java Script. Io uso C ++ per guadagnarmi da vivere.

Però, dovrei dire che mi piace di più la programmazione Assembly e la programmazione C. Questo è in linea principalmente con la programmazione imperativa.

So che i paradigmi di programmazione sono importanti per categorizzare i tipi di dati e fornire concetti di programmazione astratta più estesi per consentire potenti design pattern e formalizzazione del codice. Sebbene in un certo senso, ogni paradigma è una raccolta di pattern e collezioni per astrarre il livello hardware sottostante in modo da non dover pensare all'EAX o all'IP internamente all'interno della macchina.

Il mio unico problema con questo, è la nozione e i concetti concettuali della gente su come la macchina lavora per essere trasformata in affermazioni ideologiche e ambigue di ciò che sta accadendo. Questo pane è un insieme di meravigliose astrazioni in cima agli abstract, a qualche obiettivo ideologico del programmatore.

Alla fine della giornata, è meglio avere una buona mentalità chiara e confini di ciò che è la CPU e di come i computer funzionano sotto la copertura. Tutta la CPU si preoccupa sta eseguendo una serie di istruzioni che spostano i dati dentro e fuori la memoria in un registro ed eseguono un'istruzione. Non ha alcun concetto di tipo di dati o concetti di programmazione superiore. Sposta solo i dati.

Diventa più complesso quando aggiungi paradigmi di programmazione nel mix perché la nostra visione del mondo è diversa.

    
risposta data 10.05.2011 - 10:17
fonte
1

Che cosa intendiamo quando diciamo che una lingua è "più potente" di un'altra? Quando diciamo che una lingua è "espressiva?" O "ricco?" Io penso intendiamo che un linguaggio acquisisce potere quando il suo campo visivo si restringe abbastanza da rendere facile e naturale descrivere un problema - in realtà una transizione di stato, no? - che vive in quella vista. Eppure quel linguaggio è considerevolmente meno potente, meno espressivo e meno utile quando il nostro campo visivo si allarga.

Quanto più "potente" ed "espressivo" è il linguaggio, tanto più limitato è il suo uso. Quindi forse "potente" ed "espressivo" sono le parole sbagliate da usare per uno strumento di utilità ristretta. Forse "appropriato" o "astratto" sono parole migliori per queste cose.

Ho iniziato a programmare scrivendo un sacco di cose di basso livello: i driver di dispositivo con le loro routine di interrupt; programmi integrati; codice del sistema operativo. Il codice era intimo con l'hardware e l'ho scritto tutto in linguaggio assembly. Non diremmo che l'assemblatore è in astratto, eppure era ed è il linguaggio più potente ed espressivo di tutti loro. Posso esprimere qualsiasi problema nel linguaggio assembly; è così potente che posso fare qualsiasi cosa mi piaccia con qualsiasi macchina.

E tutta la mia successiva comprensione del linguaggio di livello superiore deve tutto alla mia esperienza con l'assemblatore. Tutto ciò che ho appreso in seguito è stato facile perché, vedi, tutto, non importa quanto astratto, deve alla fine adattarsi all'hardware.

Potresti voler dimenticare i livelli sempre più alti di astrazione, cioè i campi di vista più ristretti e più ristretti. Puoi sempre ritirarlo più tardi. È un gioco da ragazzi imparare, è questione di giorni. Faresti meglio a, secondo me, imparare la lingua dell'hardware 1 , per avvicinarti il più possibile all'osso.

1 Forse non del tutto pertinente, ma car e cdr prendono il loro nome dall'hardware: il primo Lisp è stato eseguito su una macchina che aveva un vero registro decremento e un vero registro indirizzi. Che ne pensi?

    
risposta data 10.05.2011 - 07:53
fonte
1

Array associativi

Un modo tipico di elaborazione dei dati è:

  • leggendo l'input e costruendogli una struttura gerarchica,
  • creazione di indici per quella struttura (ad esempio ordine diverso),
  • creazione di estratti (parti filtrate) di essi,
  • ricerca di un valore o un gruppo di valori (nodo),
  • riordina la struttura (elimina nodi, aggiungi, aggiungi, rimuovi elementi secondari in base a una regola, ecc.),
  • scansiona attraverso l'albero e stampa o salva alcune parti di esse.

Lo strumento giusto è array associativo .

  • Il miglior supporto linguistico per gli array associativi che ho visto è MUMPS , dove gli array associativi sono: 1. sempre ordinati 2. possono essere creati su disco (il cosiddetto database), con la stessa sintassi. (Effetto collaterale: è estremamente potente come database, il programmatore ha accesso al btree nativo. Il miglior sistema NoSQL di sempre.)
  • Il mio secondo premio va a PHP , mi piace foreach e sintassi semplice, come $ a [] = x o $ una [x] [y] [z] ++ .

Non mi piace la sintassi dell'array associativo di JavaScript, perché non riesco a creare, ad esempio a [x] [y] [z] = 8 , prima devo creare a [x] e a [x] [y] .

Va bene, in C ++ (e in Java) ci sono dei buoni portfolio di classi di contenitori, Map , Multimap , qualunque sia, ma se voglio eseguire la scansione, devo fai un iteratore e quando voglio inserire un nuovo elemento di livello profondo, devo creare tutti i livelli superiori ecc. Scomodo.

Non dico che non ci siano matrici associative utilizzabili in C ++ (e Java), ma i linguaggi di script senza scrittura (o non tipizzati) battono quelli compilati, perché sono linguaggi di script senza nome.

Dichiarazione di non responsabilità: non ho familiarità con C # e altri languges .NET, AFAIK hanno una buona gestione associativa di array.

    
risposta data 10.05.2011 - 09:59
fonte
1

Una libreria compilata che chiama un callback, che è una funzione membro definita dall'utente di una classe definita dall'utente.

Questo è possibile in Objective-C e rende la programmazione dell'interfaccia utente un gioco da ragazzi. Puoi dire a un pulsante: "Si prega di chiamare questo metodo per questo oggetto quando si è premuto", e il pulsante lo farà. Sei libero di usare qualsiasi nome di metodo per il callback che ti piace, non è congelato nel codice della libreria, non devi ereditare da un adattatore per farlo funzionare, né il compilatore vuole risolvere la chiamata al momento della compilazione, e, ugualmente importante, puoi dire a due pulsanti di chiamare due metodi diversi dello stesso oggetto.

Non ho ancora visto un modo altrettanto flessibile di definire un callback in qualsiasi altra lingua (anche se sarei molto interessato a sentirli!). L'equivalente più vicino in C ++ sta probabilmente passando una funzione lambda che esegue la chiamata richiesta, che di nuovo limita il codice della libreria come modello.

È questa caratteristica di Objective-C che mi ha insegnato a valutare la capacità di una lingua di passare qualsiasi tipo di oggetti / funzioni / qualunque-importante-concetto-la-lingua-contiene in giro liberamente, insieme al potere di salvarli in variabili. Qualsiasi punto in un linguaggio che definisce qualsiasi tipo di concetto, ma non fornisce un mezzo per memorizzarlo (o un riferimento ad esso) in tutti i tipi disponibili di variabili, è un ostacolo significativo e probabilmente una fonte di molto brutto, codice duplicato. Sfortunatamente, i linguaggi di programmazione barocchi tendono ad esibire un certo numero di questi punti:

  • In C ++ non è possibile scrivere il tipo di VLA, né memorizzarne un puntatore. Ciò proibisce efficacemente i veri array multidimensionali di dimensioni dinamiche (che sono disponibili in C dal C99).

  • In C ++ non è possibile scrivere il tipo di lambda. Non puoi nemmeno digitarlo. Quindi, non c'è modo di aggirare un lambda, o memorizzare un riferimento ad esso in un oggetto. Le funzioni Lambda possono essere passate solo ai template.

  • In Fortran non è possibile scrivere il tipo di un elenco nomi. Semplicemente non c'è modo di passare un namelist a nessun tipo di routine. Quindi, se si dispone di un algoritmo complesso che dovrebbe essere in grado di gestire due diversi elenchi nomi, non si è fortunati. Non puoi semplicemente scrivere l'algoritmo una volta e passare ad esso gli appositi elenchi nomi.

Questi sono solo alcuni esempi, ma vedi il punto in comune: ogni volta che vedi una tale limitazione per la prima volta, di solito non ti importa perché sembra una pazza idea fare la cosa proibita. Tuttavia, quando fai una seria programmazione in quella lingua, arrivi finalmente al punto in cui questa restrizione precisa diventa una vera seccatura.

    
risposta data 18.05.2016 - 23:03
fonte
0

Questa è una vecchia domanda, ma dal momento che nessuno l'ha menzionata, aggiungerò la comprensione delle liste (e ora dict). È facile scrivere un one-liner in Haskell o Python che risolve il problema Fizz-Buzz. Prova a farlo in C ++.

Mentre C ++ ha fatto passi da gigante alla modernità con C ++ 11, è un po 'difficile definirlo un linguaggio "moderno". C ++ 17 (che non è ancora stato rilasciato) sta facendo ancora di più per arrivare agli standard moderni, purché "moderno" significhi "non dal millennio precedente".

Anche la più semplice delle comprensioni che si può scrivere in una riga in Python (e rispettare il limite di lunghezza di 79 caratteri di Guido) diventano molte e molte linee di codici quando vengono tradotte in C ++, e alcune di quelle linee di codice C ++ sono piuttosto contorto.

    
risposta data 18.05.2016 - 21:38
fonte

Leggi altre domande sui tag