O "paradoxo blub" e c ++


37

Eu estava lendo o artigo aqui: http://www.paulgraham.com/avg.html e a parte sobre o "paradoxo blub" foi particularmente interessante. Como alguém que codifica principalmente em c ++, mas tem exposição a outras linguagens (principalmente Haskell), estou ciente de algumas coisas úteis nessas linguagens que são difíceis de replicar em c ++. A questão é principalmente para pessoas que são proficientes em c ++ e em alguma outra linguagem. Existe algum recurso ou idioma poderoso que você utiliza em uma linguagem que seria difícil de conceituar ou implementar se você estivesse escrevendo apenas em c ++?

Em particular, esta citação chamou minha atenção:

Por indução, os únicos programadores em posição de ver todas as diferenças de poder entre as várias linguagens são aqueles que entendem a mais poderosa. (Provavelmente, é isso que Eric Raymond quis dizer sobre Lisp fazer de você um programador melhor.) Você não pode confiar nas opiniões dos outros, por causa do paradoxo Blub: eles estão satisfeitos com o idioma que usam, porque dita o a maneira como eles pensam sobre os programas.

Se eu sou equivalente ao programador "Blub" em virtude do uso de c ++, isso levanta a seguinte pergunta: Existem conceitos ou técnicas úteis que você encontrou em outros idiomas que você teria achado difícil conceituar se você esteve escrevendo ou "pensando" em c ++?

Por exemplo, o paradigma de programação lógica visto em linguagens como Prolog e Mercury pode ser implementado em c ++ usando a biblioteca de castor, mas finalmente acho que conceitualmente estou pensando em termos de código Prolog e traduzindo para o equivalente em c ++ ao usá-lo. Como forma de ampliar meu conhecimento de programação, estou tentando descobrir se há outros exemplos semelhantes de expressões úteis / poderosas que são expressas com mais eficiência em outras linguagens que talvez eu não conheça como desenvolvedor de c ++. Outro exemplo que vem à mente é o sistema macro no lisp, gerar o código do programa a partir do programa parece ter muitos benefícios para alguns problemas. Parece ser difícil de implementar e pensar a partir do c ++.

Esta questão não pretende ser um debate "c ++ vs lisp" ou qualquer tipo de debate do tipo guerra de idiomas. Fazer uma pergunta como essa é a única maneira que eu posso ver possível para descobrir coisas que eu não sei e que não sei.



2
Concordo. Enquanto isso não se transformar em um debate C ++ vs Lisp, acho que há algo a ser aprendido aqui.
#

@MasonWheeler: there are things that other languages can do that Lisp can't- Improvável, já que Lisp é Turing-complete. Talvez você quisesse dizer que há algumas coisas que não são práticas para fazer no Lisp? Eu poderia dizer o mesmo sobre qualquer linguagem de programação.
Robert Harvey

2
@RobertHarvey: "Todas as línguas são igualmente poderosas no sentido de serem equivalentes a Turing, mas esse não é o sentido da palavra que os programadores se preocupam. (Ninguém quer programar uma máquina de Turing.) O tipo de energia que os programadores se importam pode não ser formalmente definível, mas uma maneira de explicar isso seria dizer que se refere a recursos que você só pode obter na linguagem menos poderosa escrevendo um intérprete para a linguagem mais poderosa ". - Paul Graham, em nota de rodapé do trollpost em questão. (Veja o que quero dizer?)
Mason Wheeler

@Mason Wheeler: (. Não realmente)
Robert Harvey

Respostas:


16

Bem, desde que você mencionou Haskell:

  1. Correspondência de padrões. Acho a correspondência de padrões muito mais fácil de ler e escrever. Considere a definição de mapa e pense em como ele seria implementado em um idioma sem correspondência de padrões.

    map :: (a -> b) -> [a] -> [b]
    map f [] = []
    map f (x:xs) = f x : map f xs
  2. O sistema de tipos. Às vezes pode ser uma dor, mas é extremamente útil. Você precisa programar com ele para realmente entender e quantos erros ele pega. Além disso, a transparência referencial é maravilhosa. Após a programação em Haskell, apenas se torna aparente quantos bugs são causados ​​pelo gerenciamento de estado em uma linguagem imperativa.

  3. Programação funcional em geral. Usando mapas e dobras em vez de iteração. Recursão. É sobre pensar em um nível superior.

  4. Avaliação preguiçosa. Novamente, trata-se de pensar em um nível superior e deixar o sistema lidar com a avaliação.

  5. Cabal, pacotes e módulos. Ter os pacotes de download do Cabal para mim é muito mais conveniente do que encontrar o código-fonte, escrever um makefile etc. Ser capaz de importar apenas determinados nomes é muito melhor do que essencialmente ter todos os arquivos de origem reunidos e compilados.


2
Uma observação sobre a correspondência de padrões: eu não diria que é mais fácil escrever em geral, mas depois de ler um pouco sobre o problema da expressão, fica claro que coisas como instruções if e switch, enumerações e o padrão observador são todas implementações inferiores dos Tipos de Dados Algébricos + Correspondência de padrões. (E não vamos mesmo começar em como Talvez faz exceções de ponteiro nulo obsoleto)
hugomg

O que você diz é verdade, mas o problema da expressão é sobre limitações de tipos de dados algébricos (e sobre as limitações duplas do OOP padrão).
Bluesorblade 28/10/2013

@hugomg Você quis dizer o padrão Visitor em vez de Observer?
Sebastian Redl

sim. Eu sempre mudar esses dois nomes :)
hugomg

@ hugomg Não se trata de ter Maybe(para ver em C ++ std::optional), é preciso ter que marcar explicitamente as coisas como opcional / anulável / talvez.
Deduplicator

7

Memoize!

Tente escrevê-lo em C ++. Não com C ++ 0x.

Muito pesado? Ok, tente com C ++ 0x.

Veja se você consegue vencer esta versão em tempo de compilação de 4 linhas (ou 5 linhas, o que for: P) em 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));
}

Tudo o que você precisa fazer para chamá-lo é algo como:

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

Você também pode tentar algo semelhante no Scheme, embora seja um pouco mais lento porque acontece em tempo de execução e porque a pesquisa aqui é linear em vez de hash (e bem, porque é o 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))

11
Então você ama APL, onde pode escrever qualquer coisa em uma única linha? Tamanho não importa!
Bo Persson

@ Bo: eu não usei APL. Não sei ao certo o que você quer dizer com "tamanho não importa", mas há algo errado com meu código que faz você dizer isso? E há alguma vantagem em como você faria isso em uma linguagem diferente (como C ++) que eu não conheço? (Eu editei as variáveis nomes um pouco, caso em que foi que você estava se referindo.)
Mehrdad

11
@ Mehrdad - Meu comentário é sobre o programa mais compacto não é sinal da melhor linguagem de programação. Nesse caso, o APL venceria, porque você faz a maioria das coisas com um único operador de char. O único problema é que é ilegível.
Bo Persson

@ Bo: Como eu disse, não estava recomendando o APL; Eu nunca vi isso. O tamanho era um critério (embora significativo, como pode ser visto se você tentar isso com C ++) ... mas há algo de errado com esse código?
Mehrdad

11
@ Mat: Seu código memorizou uma função, mas esse código pode memorizar qualquer função. Estes não são realmente equivalentes. Se você realmente tentar escrever uma função de ordem superior como essa em C ++ 0x, é muito mais entediante que em D (embora ainda seja bem possível ... embora não seja possível em C ++ 03).
Mehrdad

5

C ++ é uma linguagem multiparadigmática, o que significa que tenta oferecer suporte a muitas maneiras de pensar. Às vezes, um recurso C ++ é mais estranho ou menos fluente do que a implementação de outra linguagem, como é o caso da programação funcional.

Dito isso, não consigo pensar em um recurso nativo da linguagem C ++ que faça o que yieldPython ou JavaScript faz.

Outro exemplo é a programação simultânea . O C ++ 0x terá uma opinião a respeito, mas o padrão atual não, e a simultaneidade é uma maneira totalmente nova de pensar.

Além disso, o desenvolvimento rápido - mesmo a programação shell - é algo que você nunca aprenderá se nunca sair do domínio da programação C ++.


Não consigo nem começar a pensar em como seria difícil criar geradores em C ++, considerando o C ++ 2003. O C ++ 2011 tornaria mais fácil, mas ainda não trivial. Trabalhando rotineiramente com C ++, C # e Python, os geradores são facilmente os recursos que mais sinto falta no C ++ (agora que o C ++ 2011 adicionou lambdas).

Eu sei que vou levar um tiro por isso, mas se eu absolutamente tivesse que implementar geradores em C ++, teria que usar ... setjmpe longjmp. Não tenho ideia de quanto isso quebra, mas acho que as exceções seriam as primeiras a desaparecer. Agora, se você me der licença, preciso reler o Design Moderno C ++ para tirar isso da minha cabeça.
Mike DeSimone

@ Mike DeSimone, você pode elaborar (detalhadamente) como tentaria uma solução com setjmp e longjmp?

11
As corotinas são isomórficas para os functores.
GManNickG

11
@Xeo, @Mike: xkcd.com/292
Mehrdad

5

As corotinas são um recurso de linguagem imensamente útil que sustenta muitos dos benefícios mais tangíveis de outras linguagens sobre o C ++. Eles basicamente fornecem pilhas extras para que as funções possam ser interrompidas e continuadas, fornecendo recursos semelhantes a pipeline para o idioma que alimenta facilmente os resultados das operações através de filtros para outras operações. É maravilhoso, e no Ruby achei muito intuitivo e elegante. A avaliação preguiçosa também está ligada a isso.

Introspecção e compilação / execução / avaliação de código de tempo de execução / o que for, são recursos extremamente poderosos que o C ++ não possui.


As corotinas estão disponíveis no FreeRTOS ( veja aqui ), implementado em C. Gostaria de saber o que seria necessário para fazê-las funcionar em C ++?
Mike DeSimone

As co-rotinas são um truque desagradável para emular objetos em C. No C ++, os objetos são usados ​​para agrupar código e dados. Mas em C, você não pode. Então você usa a pilha de co-rotina para armazenar os dados e a função de co-rotina para armazenar o código.
MSalters


11
@Ferruccio: Obrigado pelo link ... também existem alguns no artigo da Wikipedia. @MSalters: o que faz você descrever as co-rotinas como "um truque desagradável"? Parece uma perspectiva muito arbitrária para mim. O uso de uma pilha para armazenar o estado também é feito por algoritmos recursivos - eles também são burros? FWIW, coroutines e OOP entraram em cena na mesma época (início dos anos 60) ... dizer que o primeiro é um hack para objetos em C parece bizarro ... Eu imagino que alguns programadores em C da época estavam interessados ​​em emular objetos, > 15 anos antes de C ++.
Tony

4

Tendo implementado um sistema de álgebra computacional em Lisp e C ++, posso dizer que a tarefa foi muito mais fácil no Lisp, mesmo sendo um novato completo na linguagem. Essa natureza simplista de tudo o que está sendo listado simplifica muitos algoritmos. Concedido, a versão C ++ era zilhões de vezes mais rápido. Sim, eu poderia ter feito a versão lisp mais rápida, mas o código não seria tão lispy. O script é outra coisa que sempre será mais fácil é o lisp, por exemplo. É tudo sobre como usar a ferramenta certa para o trabalho.


Qual foi a diferença de velocidade?
Quant_dev 10/05

11
@quant_dev: um múltiplo de um zilhão, é claro!
Matt Ellen

Eu nunca realmente medi, mas tive a sensação de que os grandes O's eram diferentes. Originalmente, escrevi a versão do C ++ em um estilo funcional e ele também teve problemas de velocidade até que eu o ensinei a modificar as estruturas de dados em vez de criar novas e alteradas. Mas isso também fez o código mais difícil de ler ...
jeffythedragonslayer

2

O que queremos dizer quando dizemos que um idioma é "mais poderoso" que outro? Quando dizemos que um idioma é "expressivo?" Ou "rico?" Acho que queremos dizer que uma linguagem ganha poder quando seu campo de visão se estreita o suficiente para tornar mais fácil e natural descrever um problema - realmente uma transição de estado, não? - que vive dentro dessa visão. No entanto, essa linguagem é consideravelmente menos poderosa, menos expressiva e menos útil quando nosso campo de visão se alarga.

Quanto mais "poderosa" e "expressiva" a linguagem, mais limitado seu uso. Então, talvez "poderoso" e "expressivo" sejam as palavras erradas para usar em uma ferramenta de utilidade restrita. Talvez "apropriado" ou "abstrato" sejam palavras melhores para essas coisas.

Comecei a programar escrevendo um monte de coisas de baixo nível: drivers de dispositivo com suas rotinas de interrupção; programas incorporados; código do sistema operacional. O código era íntimo do hardware e escrevi tudo em linguagem assembly. Não diríamos que o assembler é o menos abstrato, mas foi e é a linguagem mais poderosa e expressiva de todos eles. Eu posso expressar qualquer problema na linguagem assembly; é tão poderoso que eu posso fazer o que quiser com qualquer máquina.

E todo o meu entendimento posterior da linguagem de nível superior deve tudo à minha experiência com assembler. Tudo o que aprendi depois foi fácil porque, veja você, tudo - não importa o quão abstrato - deve, no final, acomodar-se ao hardware.

Você pode querer esquecer níveis cada vez mais altos de abstração - isto é, campos de visão cada vez mais estreitos. Você sempre pode pegar isso mais tarde. É muito fácil aprender, em questão de dias. Na minha opinião, seria melhor você aprender a linguagem do hardware 1 , chegar o mais perto possível do osso.


1 Talvez não completamente pertinente, mas care cdrtirar seus nomes do hardware: o primeiro Lisp correu em uma máquina que tinha um Decrement Register real e um endereço real Register. Que tal isso?


Você acha que a escolha é uma espada de dois gumes. Todos nós procuramos, mas há um lado sombrio e isso nos torna infelizes. É melhor ter uma visão muito definida do mundo e limites definidos nos quais você possa operar. As pessoas são criaturas muito criativas e podem fazer grandes coisas com ferramentas limitadas. Então, para resumir, estou dizendo que não é a linguagem de programação, mas sim pessoas talentosas que podem fazer qualquer linguagem cantar!
Chad

"Sugerir é criar; definir é destruir". Pensar que você poderia facilitar a vida com outro idioma é bom, mas depois de dar o salto, é preciso lidar com as verrugas do novo idioma.
Mike DeSimone

2
Eu diria que o inglês é muito mais poderoso e expressivo do que qualquer linguagem de programação, e ainda assim os limites de sua utilidade se expandem todos os dias e sua utilidade é imensa. Parte do poder e da expressividade advém da capacidade de se comunicar no nível apropriado de abstração e da capacidade de inventar novos níveis de abstração quando incorporados.
Moldnilo 10/05

@ Mike, então você tem que lidar com a comunicação com a linguagem anterior no novo;)
Chad

2

Matrizes associativas

Uma maneira típica de processar dados é:

  • lendo a entrada e construindo uma estrutura hierárquica a partir dela,
  • criando índices para essa estrutura (por exemplo, ordem diferente),
  • criando extratos (partes filtradas) deles,
  • encontrar um valor ou um grupo de valores (nó),
  • reorganize a estrutura (excluir nós, adicionar, acrescentar, remover subelementos com base em uma regra etc.),
  • digitalize através da árvore e imprima ou salve algumas partes delas.

A ferramenta certa para isso é a matriz associativa .

  • O melhor suporte de linguagem para matrizes associativas que eu já vi é o MUMPS , onde matrizes associativas são: 1. sempre classificadas 2. elas podem ser criadas em disco (o chamado banco de dados), com a mesma sintaxe. (Efeito colateral: é extremamente poderoso como banco de dados, o programador tem acesso ao btree nativo. O melhor sistema NoSQL de todos os tempos.)
  • Meu segundo prémio vai para PHP , eu como foreach e fácil de sintaxe, como $ a [] = x ou $ a [x] [y] [z] ++ .

Eu realmente não gosto da sintaxe associativa do JavaScript, porque não posso criar, digamos a [x] [y] [z] = 8 , primeiro tenho que criar a [x] e a [x] [y] .

Ok, em C ++ (e em Java) há um bom portfólio de classes de contêineres, Map , Multimap , qualquer que seja, mas se eu quiser varrer, preciso fazer um iterador e, quando quero inserir um novo elemento de nível profundo, eu tem que criar todos os níveis superiores etc. Desconfortável.

Não digo que não haja matrizes associativas utilizáveis ​​em C ++ (e Java), mas as linguagens de script sem tipo (ou de tipo não estrito) superam as compiladas, porque são linguagens de script sem tipo.

Disclaimer: Eu não estou familiarizado com C # e outros lances de .NET, AFAIK eles têm um bom manuseio associativo de array.


11
Divulgação completa: MUMPS não é para todos. Citação: Para dar um exemplo um pouco mais do mundo real do horror que é o MUMPS, comece participando de uma parte do International Obfuscated C Code Contest, uma pitada de Perl, duas medidas de FORTRAN e SNOBOL e as contribuições independentes e descoordenadas de dezenas de pesquisadores médicos, e lá vai você.
Mike DeSimone

11
No Python, você pode usar o dicttipo interno (por exemplo x = {0: 5, 1: "foo", None: 500e3}, observe que não há requisitos para que chaves ou valores sejam do mesmo tipo). Tentar fazer algo assim a[x][y][z] = 8é difícil, porque a linguagem precisa olhar para o futuro para ver se você deseja definir um valor ou criar outro nível; a expressão a[x][y]por si só não diz a você.
Mike DeSimone

O MUMPS é originalmente uma linguagem básica com matrizes associativas (pode ser armazenada diretamente no disco!). Versões posteriores contêm extensões procedurais, o que o torna muito semelhante ao PHP principal. Alguém com medo de Basic e PHP deve achar Mumps assustador, mas outros não. Programadores não. E lembre-se, é um sistema muito antigo, todas as coisas estranhas, como instruções de uma letra (embora você possa usar nomes completos), ordem de avaliação da LR etc. - assim como soluções não estranhas - têm apenas um objetivo: otimização .
ern0

"Devemos esquecer pequenas eficiências, digamos, 97% das vezes: a otimização prematura é a raiz de todo mal. No entanto, não devemos desperdiçar nossas oportunidades nesses 3% críticos". - Donald Knuth. O que você descreve me parece uma linguagem herdada cuja ênfase é a compatibilidade com versões anteriores, e tudo bem. Pessoalmente, nessas aplicações, considero a manutenção mais importante que a otimização, e uma linguagem com expressões não algébricas e comandos de uma letra parece contraproducente. Eu sirvo o cliente, não o idioma.
Mike DeSimone

@ Mike: links muito divertidos que você postou, eu ri muito enquanto os lia.
shuttle87

2

Eu não aprendo Java, C \ C ++, Assembly e Java Script. Eu uso C ++ para ganhar a vida.

No entanto, eu diria que gosto mais de programação Assembly e programação C. Isso está alinhado principalmente com a programação Imperative.

Eu sei que os paradigmas de programação são importantes para categorizar os tipos de dados e fornecem conceitos abstratos de programação mais elevados para permitir poderosos padrões de design e formalização de código. Embora, de certa forma, cada Paradigms seja uma coleção de padrões e coleções para abstrair a camada de hardware subjacente, para que você não precise pensar no EAX ou IP internamente na máquina.

Meu único problema com isso é que permite que a noção e os conceitos das pessoas de como a máquina funciona se transforme em ideologias e afirmações ambíguas do que está acontecendo. Este pão é todo tipo de abstrações maravilhosas, além de abstratos, para algum objetivo ideológico do programador.

No final do dia, é melhor ter uma boa mentalidade e limites claros do que é a CPU e como os computadores funcionam sob o capô. Todo o CPU se preocupa em executar uma série de instruções que movem os dados para dentro e para fora da memória em um registro e executam uma instrução. Não possui um conceito de tipo de dados ou qualquer conceito de programação superior. Ele apenas move os dados.

Torna-se mais complexo quando você adiciona paradigmas de programação à mistura, porque nossa visão do mundo é diferente.


2

existe algum recurso ou idioma poderoso da linguagem que você utiliza em uma linguagem que seria difícil de conceituar ou implementar se você estivesse escrevendo apenas em c ++?

Existem conceitos ou técnicas úteis que você encontrou em outros idiomas que você teria dificuldade em conceituar se estivesse escrevendo ou "pensando" em c ++?

O C ++ torna muitas abordagens intratáveis. Eu diria que a maior parte da programação é difícil de conceituar se você se limitar ao C ++. Aqui estão alguns exemplos de problemas que são muito mais facilmente resolvidos de maneiras que o C ++ dificulta.

Registre convenções de alocação e chamada

Muitas pessoas pensam em C ++ como uma linguagem de baixo nível bare metal, mas na verdade não é. Ao abstrair detalhes importantes da máquina, o C ++ torna difícil conceituar aspectos práticos, como alocação de registros e convenções de chamada.

Para aprender sobre conceitos como esses, recomendo dar uma olhada em alguma programação em linguagem assembly e confira este artigo sobre a qualidade da geração de código ARM .

Geração de código em tempo de execução

Se você conhece apenas C ++, provavelmente pensa que os modelos são o princípio e o fim da metaprogramação. Eles não são. De fato, eles são uma ferramenta objetivamente ruim para a metaprogramação. Qualquer programa que manipule outro programa é um metaprograma, incluindo intérpretes, compiladores, sistemas de álgebra computacional e provadores de teoremas. A geração de código em tempo de execução é um recurso útil para isso.

Eu recomendo EVALiniciar uma implementação do Scheme e brincar para aprender sobre a avaliação metacircular.

Manipulando árvores

Árvores estão por toda parte na programação. Na análise, você tem árvores de sintaxe abstratas. Nos compiladores, você tem IRs que são árvores. Em gráficos e programação GUI, você tem árvores de cena.

Este "Analisador JSON Ridiculamente Simples para C ++" pesa apenas 484 LOC, o que é muito pequeno para C ++. Agora compare-o com meu próprio analisador JSON simples, que pesa apenas 60 LOC de F #. A diferença é principalmente porque os tipos de dados algébricos do ML e a correspondência de padrões (incluindo padrões ativos) facilitam muito a manipulação de árvores.

Confira também árvores vermelho-pretas no OCaml .

Estruturas de dados puramente funcionais

A falta de GC no C ++ torna praticamente impossível adotar algumas abordagens úteis. Estruturas de dados puramente funcionais são uma dessas ferramentas.

Por exemplo, confira este correspondente de expressão regular de 47 linhas no OCaml. A brevidade se deve em grande parte ao uso extensivo de estruturas de dados puramente funcionais. Em particular, o uso de dicionários com chaves que são conjuntos. Isso é realmente difícil de fazer em C ++ porque os dicionários e conjuntos stdlib são todos mutáveis, mas você não pode alterar as chaves de um dicionário ou interromper a coleção.

A programação lógica e os buffers de desfazer são outros exemplos práticos em que estruturas de dados puramente funcionais tornam algo difícil em C ++ muito fácil em outras linguagens.

Chamadas de cauda

O C ++ não apenas não garante chamadas de cauda, ​​mas o RAII está fundamentalmente em desacordo com isso, porque os destruidores impedem uma chamada na posição de cauda. As chamadas finais permitem que você faça um número ilimitado de chamadas de função usando apenas uma quantidade limitada de espaço na pilha. Isso é ótimo para a implementação de máquinas de estado, incluindo máquinas de estado extensíveis, e é um ótimo cartão "saia da cadeia" em muitas circunstâncias que de outro modo seriam embaraçosas.

Por exemplo, confira esta implementação do problema da mochila 0-1 usando o estilo de passagem de continuação com memorização em F # do setor financeiro. Quando você tem chamadas de chamada, o estilo de passagem de continuação pode ser uma solução óbvia, mas o C ++ o torna intratável.

Concorrência

Outro exemplo óbvio é a programação simultânea. Embora isso seja inteiramente possível em C ++, é extremamente propenso a erros em comparação com outras ferramentas, principalmente na comunicação de processos sequenciais, como visto em linguagens como Erlang, Scala e F #.


1

Esta é uma pergunta antiga, mas como ninguém a mencionou, adicionarei compreensões de lista (e agora ditarei). É fácil escrever um one-liner em Haskell ou Python que resolva o problema do Fizz-Buzz. Tente fazer isso em C ++.

Embora o C ++ tenha feito movimentos massivos para a modernidade com o C ++ 11, é um pouco exagerado chamá-lo de linguagem "moderna". O C ++ 17 (que ainda não foi lançado) está fazendo mais movimentos para chegar aos padrões modernos, desde que "moderno" signifique "não do milênio anterior".

Mesmo as compreensões mais simples que se pode escrever em uma linha em Python (e obedecendo ao limite de comprimento de linha de 79 caracteres de Guido) tornam-se muitas e muitas linhas de códigos quando traduzidas para C ++, e algumas dessas linhas de código C ++ são bastante complicadas.


Observe bem: a maior parte da minha programação está em C ++. Eu gosto do idioma.
David Hammen

Eu pensei que a proposta de intervalos deveria resolver este? (Nem mesmo em C ++ 17, eu acho) #
373 Martin Ba

2
"grandes mudanças para a modernidade": quais recursos "modernos" o C ++ 11 fornece que foram inventados no milênio atual?
Giorgio

@MartinBa - Meu entendimento da proposta "intervalos" é que ele substitui os iteradores que são mais fáceis de trabalhar e menos propensos a erros. Não vi nenhuma sugestão de que eles permitiriam algo tão interessante quanto a compreensão da lista.
Jules

2
@Giorgio - que características de qualquer idioma atualmente popular foram inventadas no milênio atual?
Jules

0

Uma biblioteca compilada chamando um retorno de chamada, que é uma função de membro definida pelo usuário de uma classe definida pelo usuário.


Isso é possível no Objective-C e facilita a programação da interface do usuário. Você pode dizer um botão: "Por favor, chame este método para este objeto quando você pressionar", e o botão fará isso. Você pode usar qualquer nome de método para o retorno de chamada que desejar, não está congelado no código da biblioteca, não deve herdar de um adaptador para que ele funcione, nem o compilador deseja resolver a chamada em tempo de compilação, e, igualmente importante, você pode dizer a dois botões para chamar dois métodos diferentes do mesmo objeto.

Ainda não vi uma maneira igualmente flexível de definir um retorno de chamada em qualquer outro idioma (embora eu estivesse muito interessado em saber sobre eles!). O equivalente mais próximo em C ++ provavelmente está passando uma função lambda que executa a chamada necessária, que novamente restringe o código da biblioteca a ser um modelo.

É esse recurso do Objective-C que me ensinou a valorizar a capacidade de uma linguagem de transmitir qualquer tipo de objeto / função / qualquer conceito importante que a linguagem contenha livremente, juntamente com o poder de salvá-los. variáveis. Qualquer ponto em uma linguagem que define qualquer tipo de conceito, mas não fornece um meio para armazená-lo (ou uma referência a ele) em todos os tipos de variáveis ​​disponíveis, é um obstáculo significativo e provavelmente uma fonte de muito feio, código duplicado. Infelizmente, as linguagens de programação barrocas tendem a exibir vários desses pontos:

  • No C ++, você não pode anotar o tipo de um VLA, nem armazenar um ponteiro para ele. Isso proíbe efetivamente matrizes multidimensionais verdadeiras de tamanho dinâmico (que estão disponíveis em C desde C99).

  • No C ++, você não pode anotar o tipo de uma lambda. Você nem pode digitar isso. Portanto, não há como passar por um lambda ou armazenar uma referência a ele em um objeto. As funções do Lambda podem ser passadas apenas para modelos.

  • No Fortran, você não pode anotar o tipo de uma lista de nomes. Simplesmente não há meios de passar uma lista de nomes para qualquer tipo de rotina. Portanto, se você tiver um algoritmo complexo que possa lidar com duas listas de nomes diferentes, estará sem sorte. Você não pode simplesmente escrever o algoritmo uma vez e passar as listas de nomes relevantes para ele.

Estes são apenas alguns exemplos, mas você vê o ponto comum: sempre que você vê essa restrição pela primeira vez, geralmente não se importa, porque parece uma idéia tão louca fazer a coisa proibida. No entanto, quando você faz alguma programação sincera nessa linguagem, acaba chegando ao ponto em que essa restrição precisa se torna um verdadeiro incômodo.


11
I have not seen a similarly flexible way to define a callback in any other language yet (though I'd be very interested to hear about them!) O que você acabou de descrever soa exatamente como o código de interface do usuário orientado a eventos funciona no Delphi. (E em WinForms, que foi fortemente influenciada pela Delphi.)
Mason Wheeler

2
"No C ++, você não pode escrever o tipo de um VLA" - [...] no C ++, os VLAs no estilo C99 são desnecessários, porque nós temos std::vector. Embora seja um pouco menos eficiente devido ao não uso da alocação de pilha, é funcionalmente isomórfico para um VLA, portanto não conta realmente como um problema do tipo "blub": os programadores de C ++ podem ver como ele funciona e apenas dizer "ah sim , C faz isso com mais eficiência do que C ++ ".
Jules

2
"No C ++, você não pode escrever o tipo de um lambda. Você não pode digitá-lo. Portanto, não há como contornar um lambda ou armazenar uma referência a ele em um objeto" - std::functioné para isso.
Jules

3
"Ainda não vi uma maneira igualmente flexível de definir um retorno de chamada em qualquer outro idioma (embora eu esteja muito interessado em ouvi-los!)." - em Java, você pode escrever object::methode ele será convertido em uma instância de qualquer interface que o código receptor espere. C # tem delegados. Toda linguagem funcional de objetos tem esse recurso porque é basicamente o ponto de seção dos dois paradigmas.
Jules

@Jules Seus argumentos são precisamente o que o Blub-Paradox trata: Como um programador proficiente em C ++, você não os vê como limitações. No entanto, são limitações, e outros idiomas como o C99 são mais poderosos nesses pontos específicos. Até o seu último ponto: existem soluções possíveis em muitos idiomas, mas eu não conheço um que realmente permita que você passe o nome de qualquer método para outra classe e faça com que ele seja chamado em algum objeto que você também fornece.
Cmaster # 20/16
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.