Elmord's Magic Valley

Software, lingüística e rock'n'roll. Às vezes em Português, sometimes in English.

One man's gambiarra is another man's technique

2013-04-17 14:52 -0300. Tags: comp, prog, ramble, em-portugues

Certa feita (e de repente o "fecha" do espanhol não parece mais tão estranho), não me lembro mais em que contexto nem para quem, eu mostrei um código deste tipo com listas encadeadas em C:

for (item = list;
     item && item->value != searched_value;
     item = item->next) ;

Que tanto quanto me constasse, era a maneira de se procurar um valor em uma lista em C. Para minha surpresa na época, a pessoa disse que isso era gambiarra. (Eu dificilmente escreveria isso com um while, porque eu tenho uma propensão terrível a esquecer o item = item->next no final do while. Ou será que eu tenho essa propensão por não estar acostumado a fazer esse tipo de coisa com while? But I digress.)

Uma situação similar foi quando eu mostrei para alguém na monitoria de Fundamentos de Algoritmos que dava para usar "tail-recursion com acumuladores" para iterar sobre uma lista. Algo do tipo:

(define (média lista)
  (média* lista 0 0))

(define (média* lista soma conta)
  (cond [(empty? lista) (/ soma conta)]
        [else (média* (rest lista) (+ soma (first lista)) (+ conta 1))]))

Que, novamente, é a maneira padrão de se fazer esse tipo de coisa em uma linguagem funcional (talvez com a definição de média* dentro do corpo de média, ao invés de uma função externa, mas whatever), e novamente a pessoa viu isso como uma "baita gambiarra". Provavelmente, o código imperativo equivalente (com atribuição de variáveis para manter a conta e a soma) é que seria visto como gambiarra por um programador Scheme.

Outro item de contenda é usar x++ em C para usar o valor atual de x em uma expressão e incrementá-lo na mesma operação (que é, bem, exatamente a definição do operador). Ok, usar um x++ perdido no meio de uma expressão grande pode ser fonte de confusão. Mas algo como:

var1 = var2++;

ou more likely:

item->code = max_code++;

não deveria ser nenhum mistério para quem conhece a linguagem.

A impressão que eu tenho é que as pessoas consideram "gambiarra" qualquer coisa feita de uma maneira diferente do que elas estão acostumadas. Eu mesmo já fui vítima desse efeito. No terceiro semestre eu escrevi um "banco de dados" em C (basicamente um programa que aceitava queries em uma linguagem facão, mantinha uns arquivos seqüenciais e montava uns índices em memória) para o trabalho final da disciplina de Classificação e Pesquisa de Dados. Como o conteúdo dos arquivos binários era determinado em tempo de execução pelas definições das tabelas, o programa continha um bocado de casts, manipulações de ponteiros e outras "curiosidades" do C no código. Até então, tinha sido o programa mais complicado que eu tinha escrito. Durante um bom tempo, quando o assunto surgia, eu costumava comentar que esse código era "cheio de gambiarras" e coisas de "baixo-nível". Um belo dia, uns dois ou três semestres depois, eu resolvi dar uma olhada no código, porque eu já não lembrava direito o que ele fazia de tão mágico. Para minha surpresa, minha reação ao olhar as partes "sujas" do código foi algo como "pff, que brincadeira de criança". (E para minha surpresa, as partes "sujas" me pareceram bastante legíveis.)

(Um ponto relacionado é que quando estamos ensinando/explicando alguma coisa, devemos nos lembrar que o que nos parece óbvio e simples pode não o ser para a pessoa que tem menos experiência no assunto, mas esse não era o tema original do post.)

Agora dando uma olhada nesse código de novo, lembrei de uma outra coisa que eu tinha pensando quando o tinha revisto pela primeira vez: que certos trechos do código poderiam ter sido simplificados se eu tivesse usado um goto fail; da vida em pontos estratégicos. O que nos leva ao próximo tópico...

Considered harmful?

Entre muitos nesse mundo da programação existe um culto irracional às Boas Práticas de Programação™. Não me entenda mal; muitas técnicas e práticas ajudam a tornar o código mais legível e fácil de manter. O problema é quando as "boas práticas" se tornam regras fixas sem uma boa justificativa por trás.

Um exemplo é a proibição ao goto. A idéia básica é: "Alguns usos do goto produzem código ilegível; logo, o goto não deve ser usado." Isso é mais ou menos equivalente a "facas cortam os dedos; logo, não devemos usar facas". Ok, usar uma faca para desparafusar parafusos quando se tem uma chave de fenda à mão não é uma boa idéia. Mas usar uma chave de fenda para passar margarina no pão também não é. Verdade que em linguagens de mais alto nível do que C (com exceções e try/finally e labeled loops e what-not) é extremamente raro encontrar um bom motivo para se usar um goto; mas em C, há uma porção de situações em que um goto bem utilizado é capaz de tornar o código mais simples e legível.

Outro exemplo é o uso de TABLEs em HTML. O fundamento por trás da "regra" que "proíbe" certos usos de TABLE é evitar que TABLEs sejam usadas apenas para layout, com coisas que não têm a semântica de uma tabela. So far, so good. Mas no post (que eu nunca mais encontrei) pelo qual eu fiquei sabendo sobre "tableless tables" (i.e., o uso de CSS para aplicar o layout gerado pelas TABLEs do HTML a outros elementos), lembro que um indivíduo tinha postado um comentário semi-indignado dizendo que "usar CSS para simular uma tabela é só uma tabela disfarçada, e não deve ser feito". Ou seja, o camarada internalizou a noção de "usar TABLEs é errado", mas não o porquê.

Outra manifestação do "culto" são coisas do tipo:

Q: O que acontece se eu converter um ponteiro em inteiro e de volta em ponteiro?

A: Isso é uma péssima prática de programação e você não deveria fazer isso.

que eu vejo com alguma freqüência no Stack Overflow, i.e., o camarada não responde a pergunta, e ao invés disso se limita a repetir o mantra "Não Deve Ser Feito".

A freqüência com que eu vejo esse tipo de coisa me preocupa um pouco, na verdade. Acho que o princípio por trás disso é o mesmo que está por trás de zoar/condenar/excluir as pessoas que possuem algum comportamento "non-standard". Mas isso é uma discussão para outro post.

Appendix A

17.4: Is goto a good thing or a bad thing?

Yes.

17.5: No, really, should I use goto statements in my code?

Any loop control construct can be written with gotos; similarly, any goto can be emulated by some loop control constructs and additional logic.

However, gotos are unclean. For instance, compare the following two code segments:

do {                            foo();
        foo();                  if (bar())
        if (bar())                      goto SKIP;
                break;          baz();
        baz();                  quux();
        quux();
} while (1 == 0);               SKIP:
buz();                          buz();

Note how the loop control makes it quite clear that the statements inside it will be looped on as long as a condition is met, where the goto statement gives the impression that, if bar() returned a nonzero value, the statements baz() and quux() will be skipped.

Infrequently Asked Questions in comp.lang.c

_____

(O título do post é uma paráfrase de uma paráfrase de uma expressão idiomática.)

Comentários / Comments (12)

AVERIUELMISTERIO, 2013-04-17 17:53:24 -0300 #

Por falar em espanhol, viu que o meu nome faz bem mais sentido em espanhol praquela piada do Chaves? (como de costume)


AVERIUELMISTERIO, 2013-04-17 17:55:24 -0300 #

(se bem que acho que não tem "averiguar" em espanhol)


Vítor De Araújo, 2013-04-17 18:01:06 -0300 #

Pois tem.

http://en.wiktionary.org/wiki/averiguar#Spanish


Marcus Aurelius, 2013-04-18 12:05:37 -0300 #

Post totalmente excelente! É exatamente o que eu penso, a pessoa tem que saber o porquê das coisas para decidir a melhor maneira de fazer algum código. Não basta seguir o livro de receitas de boas práticas (e isso serve para outras coisas na vida também...)

Como a gente não consegue saber tudo sobre todos os assuntos, usamos as "boas práticas" como atalhos, mas elas mesmas não são equivalentes ao entendimento do assunto, é apenas uma pílula de conhecimento resumida e simplificada que a gente usa quando não pode fazer uma grande análise aprofundada sobre cada minúsculo detalhe.

Além do mais estou achando cada vez mais que o Dijkstra, famoso pelo "goto considered harmful" era praticamente um troll que criticava tudo do jeito mais rabugento e destrutivo que podia. Há poucos dias encontrei a frase do Alan Kay que diz que "arrogância em computação é medida em nano-Dijkstras" e achei muito engraçada, hahaha.

As pessoas repetem tanto certos conhecimentos-padrão que acaba virando meio que uma religião.
— Não usem goto, é feio!
— Eu usei e, naquele caso específico, meu código ficou até melhor!
— Herege! Queimem-no na fogueira!

Para ver o lado ruim de goto e variáveis globais (para adicionar mais um elemento à discussão), é só pegar um código antigo que use APENAS goto e quase só variáveis globais (já vi um livro antigo assim). Realmente é horrível, o único jeito de ler é interpretando linha a linha e anotando o resultado de cada passo. Mas esses elementos usados em combinação com todas as outras construções da linguagem, não são algo perfeitamente normal.

Até esses dias me deparei com algo que poderia ser uma construção genérica bastante útil: um for que indique se saiu com break ou normalmente, sem precisar de goto nem de variável booleana temporária. Semelhante ao for-else do Python (o uso da palavra else não foi muito bom... deveria ter sido then) ou algo que foi proposto para C++ aqui:

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3587.pdf

Claro, juntando "finally" (substitui goto fail;) breaks e continues nomeados, for-then-else, tail-calls otimizadas, o goto vai ficando cada vez menos necessário, mas não são todas as linguagens que têm todos esses recursos. Dependendo da pessoa, um finally também pode ser uma gambiarra: o bom mesmo é ter um on_function_exit({clean_up}) logo após alocar recursos. Pois então, só reforça a idéia do teu post.

Outra coisa que li faz um tempo e gostei bastante veio dos criadores da linguagem Lua, quando adicionaram goto à linguagem (em resposta aos constantes pedidos de adicionarem continue): continuations são muito piores que goto, mas ter continuations na linguagem é "cool" e ter "goto" não é. Gotos foram demonizados por causa de anos de abuso.

http://www.inf.puc-rio.br/~roberto/talks/novelties-5.2.pdf


Marcus Aurelius, 2013-04-18 12:08:47 -0300 #

Ignore a frase abaixo:

«Mas esses elementos usados em combinação com todas as outras construções da linguagem, não são algo perfeitamente normal.»

Tem um "não" sobrando, e mesmo tirando acho que não ficou muito claro o que eu quis dizer.


Vítor De Araújo, 2013-04-18 13:46:25 -0300 #

Eu entendi. :P

Uma das features planejadas para a próxima versão do blog system (que deve sair antes do Perl 6) é um "desfazer comentário", que há de resolver esse tipo de coisa e revolucionar a indústria. :P (Na verdade a feature estava nos planos desde o começo, mas eu preferi evitar a fadiga...)


Marcus Aurelius, 2013-04-18 15:12:46 -0300 #

Outra coisa bizarra: coloquei "continue" e "continuation" tão perto um do outro no mesmo parágrafo que parece até que eu confundi um com o outro :-p


Marcus Aurelius, 2013-04-20 13:22:49 -0300 #

Uma vez vi num livro o autor dizendo que ia criar uma "lista de tipos" em C++. Como assim, uma lista de tipos? C++ não tem reflexão! (E variadic templates não tinham sido inventados ainda)

Como ele fez? Criando um template com typedefs dentro chamados Head e Tail. E um NullType para usar como terminador. Com recursão e especialização (o que é como um pattern-matching) e já dá para ver em que direção o negócio está indo... Uma linguagem funcional, só que com uma sintaxe horrível e tudo em tempo de compilação.

Em C++, a gente fica pensando que drogas o cara tomou para inventar isso. Mas em linguagens funcionais é a coisa mais natural do mundo: http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/

(
Só é necessário lixar um pouco as bordas por causa da sintaxe e o bloco quadrado encaixa direitinho no buraco redondo.

http://www.unitysecurity.com/images/additional-businesses.jpg
)

Por isso eu acho que sintaxe _não_ é supérfluo:

O cara tem que tomar chimarrão com cogumelos alucinógenos para agüentar isto:

template<int n>
struct fact {
static const int value = n * fact<n - 1>::value;
};

template<>
struct fact<0> { // specialization for n = 0
static const int value = 1;
};

(especialmente logo depois de estudar o uso de templates apenas para construir estruturas de dados genéricas, o código acima é especialmente bizarro)

Mas isto aqui é bem tranqüilo, qualquer um entende:

fact 0 = 1
fact n = n * fact (n - 1)

E os dois são a mesma coisa...


Vítor De Araújo, 2013-04-21 22:10:31 -0300 #

Hahaha, mas a diferença é que esse código em Haskell faz as coisas em tempo de execução, não no sistema de tipos. Tem uma galera que adora perverter o sistema de tipos do Haskell pra coisas desse tipo (eu *lembro* de ter visto em algum lugar uma definição de tipo para números primos (representados a base de "Zero" e "Succ"), mas não sei mais onde), e a quantidade de drogas exigida é equiparável... :P


Vítor De Araújo, 2013-04-21 22:18:29 -0300 #

(O link parece mui interessante, por sinal, mas ainda não parei pra ler...)


Marcus Aurelius, 2013-04-22 00:49:21 -0300 #

Vish, mas se para entender programação com o sistema de tipos do C++ é necessário saber Haskell; para perverter o sistema de tipos do Haskell é necessário saber o quê? Ou é só à base de alucinógenos mesmo?

Se ler o artigo até o fim, faz um resumo, porque eu parei no fatorial mesmo :-)


lfz, 2013-04-30 16:40:47 -0300 #

Bom post. Eu concordo com tudinho escrito e assino embaixo. Já me aconteceu várias vezes de ver pessoas chamando expressões idiomáticas das linguagens de gambiarras o que sugere que estas pessoas não estão usando a definição certa da palavra e não curtem as partes mais interessantes das linguagens :)

Eu ia escrever um monte de coisa sobre o assunto e lembrar de umas discussões onde esses 'fundamentalistas' das boas práticas apareceram na lista da graduação, mas tu já disse tudo e eu acho que ta de bom tamanho. Volta e meia vou direcionar pessoas a este post.


Deixe um comentário / Leave a comment

Main menu

Posts recentes

Comentários recentes

Tags

em-portugues (213) comp (137) prog (68) in-english (50) life (47) pldesign (35) unix (34) lang (32) random (28) about (27) mind (25) lisp (23) mundane (22) fenius (20) ramble (17) web (17) img (13) rant (12) hel (12) privacy (10) scheme (10) freedom (8) copyright (7) bash (7) music (7) academia (7) lash (7) esperanto (7) home (6) mestrado (6) shell (6) conlang (5) emacs (5) misc (5) latex (4) editor (4) book (4) php (4) worldly (4) android (4) politics (4) etymology (4) wrong (3) security (3) tour-de-scheme (3) kbd (3) c (3) film (3) network (3) cook (2) poem (2) physics (2) wm (2) treta (2) philosophy (2) comic (2) lows (2) llvm (2) perl (1) en-esperanto (1) audio (1) old-chinese (1) kindle (1) german (1) pointless (1) translation (1)

Elsewhere

Quod vide


Copyright © 2010-2020 Vítor De Araújo
O conteúdo deste blog, a menos que de outra forma especificado, pode ser utilizado segundo os termos da licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

Powered by Blognir.