C++ está se tornando parecido com Python [RESOLVIDO]

1. C++ está se tornando parecido com Python [RESOLVIDO]

Fernando
phoemur

(usa Debian)

Enviado em 25/09/2017 - 23:25h

http://preshing.com/20141202/cpp-has-become-more-pythonic/

Vejam os exemplos do artigo e julguem por si mesmos...


It's true. Modern C++ lends itself to a whole new style of programming – and I couldn't help noticing it has more of a Python flavor. Ranged-based for loops, type deduction, vector and map initializers, lambda expressions. The more you explore modern C++, the more you find Python's fingerprints all over it.

Was Python a direct influence on modern C++? Or did Python simply adopt a few useful constructs before C++ got around to it? You be the judge.





  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 30/09/2017 - 01:56h

Como hoje em dia nenhuma linguagem de programação surge ex nihilo nem evolui sem influências externas, é possível que Python tenha tido alguma influência sobre alguns novos recursos incorporados ao C++ na última década.

Contudo, o tom do artigo indicado me pareceu bem inadequado, como se Python tivesse surgido do nada e fosse a mãe de todas as boas ideias no mundo da programação. Lembrou até certa pessoa, com seu emblemático e solipsista bordão: “nunca na história desse (sic) país”.

Eu fiz uma longa pesquisa, antes de responder a esta mensagem, procurando por inovações introduzidas por Python, comparando timelines etc. Se Python tem mérito por adotar muitas boas ideias, não pode se gabar da autoria original de nenhuma delas.

Se a linguagem A e a linguagem B adotam uma mesma ideia, surgida originalmente na linguagem Z, A e B terminarão mais parecidas, mas não necessariamente porque A imitou B (ou vice-versa), mas porque ambas tiveram a mesma finte de inspiração.

Eu imagino um fã de LISP lendo o artigo em questão. Ele, sim, talvez pudesse dizer, com alguma razão, que Python e C++ vem evoluindo no sentido de se tornarem “lispic”.

Outro problema com o artigo é que parte dos recursos não são constitutivos de uma linguagem, mas implementados em nível de biblioteca (quer em C++, como no caso de tuplas, quer em ambas, com seus algoritmos padronizados). Com bibliotecas é possível fazer praticamente qualquer coisa, então a comparação perde um pouco o sentido.

De todo modo, minha opinião é de que possivelmente há, de 2011 para cá, mais coisas em C++ comparáveis às de Python do que havia no padrão de 1998. Isso não implica que Python foi a única fonte de inspiração, e muito menos que o objetivo tenha sido deixar a linguagem mais “pythonic”.

Eu diria até que é o contrário. Grande parte das adições feitas ao C++ tem sido no sentido de tornar a linguagem mais adequada à programação genérica (GP) e metaprogramação, paradigmas que Python não suporta diretamente.

Aí alguém dirá que há bibliotecas (experimentais) para trazer GP para Python. E outra pessoa lembrará que Python vem evoluindo no sentido de incorporar elementos de tipagem estática à linguagem, e com a bênção e o toque do próprio Guido van Rossum.

E um cara empolgado com C++ vai gritar que Python está mais parecida com C++:

— Nunca na história desse (sic) país!

3. Re: C++ está se tornando parecido com Python [RESOLVIDO]

Fernando
phoemur

(usa Debian)

Enviado em 25/09/2017 - 23:30h

Agora com C++17 ainda vamos ter "structured bindings" e "constexpr if" entre outras coisas, e a sintaxe vai ficar ainda mais parecida


4. Re: C++ está se tornando parecido com Python [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 30/09/2017 - 11:52h

C++ enchendo-se de "features" e tornando-se cada vez mais "legivel"


5. Re: C++ está se tornando parecido com Python

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/10/2017 - 22:04h

Andei pensando em alguns comentários a mais sobre o artigo, em pontos específicos de alguns itens, que vão além da simples lembrança de que muito do que foi considerado “pythonic” não é original de Python (mas que é verdade, e se aplica a todos os itens).

— Laços de repetição iterando sobre coleções: A rigor, C++ já tinha uma função para iteração sobre coleções (std::for_each) desde o padrão de 1998, mas não restam dúvidas de que a nova sintaxe é mais conveniente. Porém, é complicado dizer que a inspiração veio de Python, uma vez que tantas outras linguagens têm mecanismos semelhantes.

Interessante no artigo que o autor termina falando que gostaria de ter em C++ algo como xrange() do Python 2, renomeado para range() no Python 3 (o antigo range() do Python 2 deixou de existir), no C++.

Antes de entrar no mérito, cabe lembrar que range()/xrange() é um recurso da biblioteca, não da linguagem propriamente dita. A parte da linguagem, tanto em Python quanto em C++, é um comando, chamado for em ambas, permitir o uso de um protocolo para iteração sobre objetos de qualquer classe que se interesse por servir a essa finalidade.

A construção como “for i in range(incio, fim, passo)” no Python funciona do seguinte modo: a função range() cria um objeto da classe range, que, por sua vez, tem métodos para gerar um iterador, que permite acesso sequencial aos elementos da faixa. Assim sendo, um laço de repetição que tem como controle um objeto desse tipo depende da chamada a métodos padronizados para fazer a iteração.

É óbvio que tal coisa pode ser representada em C++ também (tenho um programa ilustrativo que pretendo postar posteriormente). Mas se objetivo de uma classe à la range é principalmente para controle de laços de repetição, então dificilmente um programador C++ teria interesse em usá-la. A forma tradicional de fazer repetições ou sequências com for — algo como “for(int i=inicio; i<fim; i+=passo)” — é muito mais eficiente, por não ter de lidar com objetos nem invocar métodos ou funções. A representação como a de Python não economizaria nem mesmo muita coisa de digitação: “for(auto i: xrange(n))” tem apenas um caráter a menos que “for(auto i=0; i<n; i++)”, e empata se, no segundo caso, se usar “int” em vez de “auto”, como é mais comum; num caso geral, “for(auto i: xrange(inicio, fim, passo))” tem quatro caracteres a mais do que “for(auto i=inicio; i<fim; i+=passo)”.

— Dedução automática de tipos: Sendo C++ uma linguagem de tipagem estática mas que, ao mesmo tempo, permite a criação de tipos novos e composição desses tipos com graus de complexidade virtualmente ilimitados, tanto por herança tradicional de OOP quanto, principalmente, por mecanismos de GP, é bem razoável criar um instrumento que transfira para o compilador a tarefa de descobrir o tipo de um objeto complexo, desde que tal tipo possa ser obtido de modo não ambíguo.

Não fosse assim (e não era no padrão de 1998; e ainda o será se a composição de tipos resultar num em ambiguidade), um programador que quisesse usar as composições complexas teria de conhecer os pormenores de cada tipo criado diretamente ou por derivação, e usar declarações gigantescas que expusessem toda complexidade dessas composições a cada vez que quisesse usar um objeto desse tipo.

A forma de Python de evitar esse tipo de problema é usar tipagem dinâmica. Contudo, essa escolha tem lá seus ônus, incluindo o menor desempenho. Em C++, o programa compilado não carrega ônus de descobrir tipos, permitindo execução muito eficiente, mas esse custo não some: ele é transferido para o momento da compilação (o que explica, em parte, a percepção de que a compilação de programas em C++ é mais lenta do que a de códigos compráveis em C, Pascal e outras linguagens semelhantes).

— Inicialização uniforme: Não me parece que a motivação tenha sido externa. C++ (e C) sempre permitiram a inicialização de alguns tipos agregados de dados por meio de listas de inicialização (particularmente arrays nativos e estruturas). Sendo classes uma forma de agregação de dados, e mais ainda quando existem muitas classes cuja função é justamente substituir os tipos agregados nativos com interfaces mais convenientes (e.g. std::vector, std::list, std::string etc.), é razoável desejar que listas de inicialização pudessem ser usadas com esse novos tipos, como eram com os antigos.

— Lambdas: Os lambdas de C++ são mais versáteis que os de Python, pois permitem escrever funções tão complexas quanto se queria, ao passo que Python as limita a apenas uma expressão, pois ficaria difícil ter lambdas mais complexos sem interferir no comportamento que a linguagem exige com relação à indentação de código.

O irônico é que, apesar da limitação que têm em Python, sua comunidade se refere ao recurso como “funções lambda”, enfatizando os momentos em que os lambdas são usados. Em C++, embora a função definida possa ter a complexidade que se quiser, muita gente põe o foco no loco em que a função é definida, que é num contexto de expressão, e acaba chamado de “expressão lambda”.

Como eu acho que as duas coisas estão corretas, em ambas as linguagens, prefiro chamar só de lambda.

— Algoritmos padronizados: Outra obra que me parece interna. A biblioteca padrão já trazia algoritmos padronizados desde 1998. O que se fez agora foi apenas incluir alguns algoritmos novos e atualizar alguns dos antigos, adaptando-os ao uso de novos recursos, como referências para rvalues e templates com argumentos variáveis.

— Empacotamento de parâmetros: O autor do artigo fez colocações corretas, mas acho não soube mostrar isso nos exemplos que trouxe. Na verdade, os exemplos acabam borrando a distinção que ele soube colocar bem e em poucas palavras.

Penso que seriam mais produtivos exemplos que se aproximassem mais de como os programadores de cada uma das linguagens efetivamente usariam parâmetros empacotados de num programa real do dia-a-dia. Tentei imaginar um caso assim, que transcrevo abaixo, em três versões: Python 3, C++14 e C++17.

#!/usr/bin/python3

import sys

def pack_to_str(*args):
// Os vários argumentos viram uma única tupla dentro da função.
if len(args)==0:
return "\n"
elif len(args)==1:
return str(*args)+"\n" # expande a tupla
return str(args[0])+", "+pack_to_str(*args[1:])
# ^^^^^^^ ^^^^^^^^^
# usa 1º argumento expande N-1 argumentos em nova chamada

# Modo de uso
sys.stdout.write(pack_to_str())
sys.stdout.write(pack_to_str(1))
sys.stdout.write(pack_to_str(1, "two"))
sys.stdout.write(pack_to_str(1, "two", 3))


// C++14
#include <iostream>
#include <string>

using namespace std;

// Função auxilar: a biblioteca padrão só define to_string()
// para tipos numéricos. Então eu providencio uma para strings.
string to_string(const string &s){ return s; }
string to_string(char c){ return string(1, c); }

std::string pack_to_str(){ return "\n"s; }

template <class Type> std::string pack_to_str(const Type &arg){
return to_string(arg)+"\n"s;
}

// N argumentos: um não-empacotado, N-1 empacotados.
template <class Type, class... OtherTypes>
std::string pack_to_str(const Type &arg, const OtherTypes... &other_args){
return to_string(arg)+", "s+pack_to_str(other_args);
// Na próxima chamada, o segundo argumento vira primeiro (não-empacotado).
}

int main(){
// Exemplo de uso:
cout << pack_to_str();
cout << pack_to_str(1);
cout << pack_to_str(1, "two"s);
cout << pack_to_str(1, "two"s, '3');
}


// C++17, com folding e constexpr if.
#include <iostream>
#include <string>
#include <utility>

using namespace std;

string to_string(const string &s){ return s; }
string to_string(char c){ return string(1, c); }

// Função embute vírgula ao final da string.
template <class C> string concat_helper(const C &arg){
return to_string(arg)+", "s;
}

template <class... C> string my_func(const C &... args){
// Dois constexpr ifs (e seus elses), que são testados e resolvidos
// durante a compilação, não durante a execução.
if constexpr(sizeof...(args)==0)
return "\n"s;
else if constexpr(sizeof...(args)==1)
return to_string(args...)+'\n';
else{
string s;
(s+=...+=concat_helper(args)); // Folding.
s.replace(s.length()-2, 2, "\n"s); // Troca a vírgula a mais por quebra de linha.
return s;
}
}

int main(){
cout << my_func();
cout << my_func(1);
cout << my_func(1, "two"s);
cout << my_func(1, "two"s, '3');
}







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts