Cordas codificadas que nunca mudam

39

Assim, em meus esforços para escrever um programa para conjugar verbos (algoritmicamente, não através de um conjunto de dados) para o francês, deparei-me com um pequeno problema.

O algoritmo para conjugar os verbos é realmente bastante simples para os mais ou menos 17 casos de verbos e é executado em um padrão específico para cada caso; portanto, os sufixos de conjugação para essas 17 classes são estáticos e (muito provavelmente) não serão alterados tão cedo. Por exemplo:

// Verbs #1 : (model: "chanter")
    terminations = {
        ind_imp: ["ais", "ais", "ait", "ions", "iez", "aient"],
        ind_pre: ["e", "es", "e", "ons", "ez", "ent"],
        ind_fut: ["erai", "eras", "era", "erons", "erez", "eront"],
        participle: ["é", "ant"]
    };

Estes são sufixos flexionados para a classe de verbo mais comum em francês.

Existem outras classes de verbos (irregulares), cujas conjugações provavelmente também permanecerão estáticas nos próximos dois séculos. Como são irregulares, suas conjugações completas devem ser incluídas estaticamente, porque não podem ser conjugadas de maneira confiável a partir de um padrão (também existem apenas [pela minha conta] 32 irregulares). Por exemplo:

// "être":
    forms = {
        ind_imp: ["étais", "étais", "était", "étions", "étiez", "étaient"],
        ind_pre: ["suis", "es", "est", "sommes", "êtes", "sont"],
        ind_fut: ["serai", "seras", "sera", "serons", "serez", "seront"],
        participle: ["été", "étant"]
    };

Eu poderia colocar tudo isso em XML ou JSON e desserializá-lo quando precisar ser usado, mas há algum ponto? Essas cadeias fazem parte da linguagem natural, que muda, mas em um ritmo lento.

Minha preocupação é que, ao fazer as coisas da maneira "correta" e desserializar alguma fonte de dados, eu não apenas tenha complicado o problema que não precisa ser complicado, mas também tenha retrocedido completamente todo o objetivo do abordagem algorítmica: para não usar uma fonte de dados! Em C #, eu poderia apenas criar uma classe em namespace Verb.Conjugation(por exemplo class Irregular) para abrigar essas seqüências de caracteres em um tipo enumerado ou algo assim, em vez de colocá-las em XML e criar a class IrregularVerbDeserializer.

Portanto, a pergunta: é apropriado para cadeias de código rígido que são muito pouco provável que a mudança durante o tempo de vida de uma aplicação? É claro que não posso garantir 100% de que eles não vão mudar, mas o risco versus o custo é quase trivial para pesar nos meus olhos - codificar é a melhor ideia aqui.

Edit : O duplicado proposto pergunta como armazenar um grande número de strings estáticas , enquanto minha pergunta é quando devo codificar essas strings estáticas .

Chris Cirefice
fonte
26
Deseja usar este software em outro idioma que não o francês no futuro?
10
Abordagem algorítmica ou não, é claro que você simplesmente precisa codificar essas cadeias 32 * 20 (e mais quando adiciona mais idiomas), e a única questão real é onde colocá-las. Eu escolheria o que lhe parecer mais conveniente, o que parece estar no código por enquanto. Você sempre pode embaralhá-los mais tarde.
Ixrec
1
@ChrisCirefice Isso parece ótimo para mim. Vá em frente.
Ixrec
2
@Gusdor Eu não acho que você leu claramente - eu disse que os padrões de conjugação provavelmente nunca mudarão ou mudarão tão raramente que uma recompilação a cada 100 anos ou mais seria boa. É claro que o código mudará, mas assim que as strings estiverem lá, como eu as quero, a menos que eu esteja refatorando, elas ficarão estáticas pelos próximos 100 anos.
precisa saber é o seguinte
1
+1. Sem mencionar que em 60 a 100 anos o custo não existirá ou será substituído por uma versão melhor.
precisa saber é o seguinte

Respostas:

56

é apropriado codificar cadeias de caracteres que dificilmente mudarão durante a vida útil de um aplicativo? É claro que não posso garantir 100% de que eles não mudarão, mas o risco versus o custo é quase trivial para pesar nos meus olhos - codificar é a melhor ideia aqui

Parece-me que você respondeu sua própria pergunta.

Um dos maiores desafios que enfrentamos é separar as coisas que provavelmente mudarão daquelas que não mudarão. Algumas pessoas ficam loucas e despejam absolutamente tudo o que podem em um arquivo de configuração. Outros vão para o outro extremo e exigem uma recompilação até para as mudanças mais óbvias.

Eu adotaria a abordagem mais fácil de implementar até encontrar um motivo convincente para torná-lo mais complicado.

Dan Pichelman
fonte
Obrigado Dan, foi o que eu imaginei. Escrever um esquema XML para isso, ter outro arquivo para acompanhar e ter que escrever uma interface para desserializar os dados pareceu um exagero, considerando que não existem muitas strings e, por ser uma linguagem natural, é improvável que mude drasticamente nos próximos 100 anos. Felizmente, atualmente em linguagens de programação, temos maneiras sofisticadas de abstrair esses dados brutos por trás de uma interface bonita, por exemplo, French.Verb.Irregular.Etreque conteria os dados da minha pergunta. Eu acho que funciona bem;)
Chris Cirefice
3
+1 Aqui no campo do Ruby, eu começava a codificar e a mover para a configuração conforme necessário. Não planeje demais o projeto prematuramente, tornando as coisas configuráveis. Isso apenas atrasa você.
Overbryd
2
Nota: alguns grupos têm uma definição diferente de "codificação", portanto, saiba que esse termo significa várias coisas. Existe um anti-padrão bem reconhecido no qual você codifica valores nas instruções de uma função, em vez de criar estruturas de dados como você ( if (num == 0xFFD8)). Esse exemplo deve se tornar algo como if (num == JPEG_MAGIC_NUMBER)em quase todos os casos por razões de legibilidade. Apenas aponto porque a palavra "hardcoding" geralmente arrepia o pescoço das pessoas (como o meu) por causa desse significado alternativo da palavra.
Cort Ammon
O @CortAmmon JPEG tem muitos números mágicos. Certamente JPEG_START_OF_IMAGE_MARKER?
precisa saber é o seguinte
@immibis Sua escolha de nomeação constante provavelmente é melhor que a minha.
Cort Ammon
25

Você está raciocinando no escopo errado.

Você não codificou apenas verbos individuais. Você codificou o idioma e suas regras . Por sua vez, isso significa que seu aplicativo não pode ser usado para qualquer outro idioma e não pode ser estendido com outras regras.

Se essa é sua intenção (por exemplo, usá-lo somente em francês), essa é a abordagem correta, devido ao YAGNI. Mas você admite que também deseja usá-lo posteriormente para outros idiomas, o que significa que muito em breve você precisará mover toda a parte codificada para os arquivos de configuração de qualquer maneira. A questão restante é:

  • Com uma certeza próxima de 100%, em um futuro próximo, estenderá o aplicativo para outros idiomas? Nesse caso, você deveria ter exportado coisas para arquivos JSON ou XML (para palavras, partes das palavras etc.) e linguagens dinâmicas (para regras) agora em vez de se forçar a reescrever a maior parte do seu aplicativo.

  • Ou há apenas uma pequena probabilidade de que o aplicativo seja estendido em algum lugar no futuro; nesse caso, o YAGNI dita que a abordagem mais simples (a que você está usando agora) é a melhor?

Como ilustração, use o verificador ortográfico do Microsoft Word. Quantas coisas você acha que estão codificadas?

Se você está desenvolvendo um processador de texto, você pode começar por um motor de ortografia simples, com regras hardcoded e palavras mesmo hardcoded: if word == "musik": suggestSpelling("music");. Rapidamente, você começará a mover as palavras e se autogovernará fora do seu código. De outra forma:

  • Toda vez que você precisar adicionar uma palavra, precisará recompilar.
  • Se você aprendeu uma nova regra, precisará alterar o código-fonte novamente.
  • E, mais importante, não há como você adaptar o mecanismo ao alemão ou ao japonês sem escrever uma quantidade enorme de código.

Como você se destacou:

Poucas regras do francês poderiam ser aplicadas ao japonês.

Assim que você codifica as regras de um idioma, todos os outros precisarão de mais e mais código, especialmente devido à complexidade das linguagens naturais.

Outro assunto é como você expressa essas regras diferentes, se não através do código. Por fim, você pode achar que uma linguagem de programação é a melhor ferramenta para isso. Nesse caso, se você precisar estender o mecanismo sem recompilá-lo, linguagens dinâmicas podem ser uma boa alternativa.

Arseni Mourzenko
fonte
1
Bem, é claro que nem tudo é codificado lá: P, então eu acho que realmente se resume a determinar como eu quero que a interface seja para que eu possa aplicá-la a vários idiomas. O problema é que eu ainda não conheço todas as línguas o suficiente, o que é efetivamente impossível. Eu acho que a única coisa que talvez você esteja perdendo é que os padrões de conjugação (é disso que estou falando) são muito estáticos em uma linguagem, e realmente é algo que é caso a caso. Existem cerca de 17 padrões de conjugação em francês para verbos. Não vai estender em breve ...
Chris Cirefice
4
Eu discordo - não acho que faça sentido mover algo fora do código antes que ele ocorra naturalmente através da refatoração. Comece com um idioma e adicione outros - em algum momento, a implementação do ILanguageRule compartilhará código suficiente para que seja apenas mais eficiente ter uma única implementação parametrizada com um XML (ou outro arquivo). Mas mesmo assim você pode acabar com o japonês, que tem uma estrutura completamente diferente. Começar empurrando sua interface para XML (ou similar) é apenas implorar por alterações na interface, e não na implementação.
Ptyx
2
Nota: se você deseja adicionar mais idiomas, isso não implica mover os idiomas para um arquivo de configuração! Você poderia igualmente ter uma LanguageProcessorclasse com várias subclasses. (Efetivamente, o "arquivo de configuração" é na verdade uma classe)
user253751
2
@MainMa: Por que você considera um problema recompilar ao adicionar uma palavra? Você precisa recompilar ao fazer qualquer outra alteração no código, e a lista de palavras provavelmente é a parte do código com menor probabilidade de alteração ao longo do tempo.
precisa saber é o seguinte
3
Eu suspeito que a flexibilidade de codificar as regras gramaticais muito específicas de cada idioma em uma subclasse seria mais conveniente no final do que a capacidade de carregar essas mesmas regras de alguma forma a partir de um arquivo de configuração (porque você basicamente escreveria seu própria linguagem de programação para interpretar as configurações).
David K
15

As strings devem ser extraídas para um arquivo de configuração ou banco de dados quando os valores puderem mudar independentemente da lógica do programa.

Por exemplo:

  • Extraindo textos da interface do usuário para arquivos de recursos. Isso permite que um não programador edite e revise os textos e permite adicionar novos idiomas adicionando novos arquivos de recursos localizados.

  • Extraindo cadeias de conexão, URLs para serviços externos etc. para arquivos de configuração. Isso permite que você use configurações diferentes em ambientes diferentes e altere as configurações rapidamente, pois elas podem precisar ser alteradas por motivos externos ao seu aplicativo.

  • Um corretor ortográfico que possui um dicionário de palavras para verificar. Você pode adicionar novas palavras e idiomas sem modificar a lógica do programa.

Mas também há uma sobrecarga de complexidade na extração para configuração, e nem sempre faz sentido.

As cadeias podem ser codificadas quando a cadeia real não pode ser alterada sem alterar a lógica do programa.

Exemplos:

  • Um compilador para uma linguagem de programação. As palavras-chave não são extraídas para uma configuração, pois cada palavra-chave possui semântica específica que deve ser suportada pelo código no compilador. A adição de uma nova palavra-chave sempre exigirá alterações de código, portanto, não há valor em extrair as seqüências para um arquivo de configuração.
  • Implementando um Protocolo: Ex. um cliente HTTP terá cadeias codificadas como "GET", "tipo de conteúdo" etc. Aqui as cadeias fazem parte da especificação do protocolo, portanto são as partes do código com menor probabilidade de alteração.

No seu caso, acho claro que as palavras são parte integrante da lógica do programa (já que você está construindo um conjugador com regras específicas para palavras específicas) e extrair essas palavras para um arquivo externo não tem valor.

Se você adicionar um novo idioma, precisará adicionar um novo código de qualquer maneira, já que cada idioma possui uma lógica de conjugação específica.


Alguns sugeriram que você poderia adicionar algum tipo de mecanismo de regras que permita especificar regras de conjugação para idiomas arbitrários, para que novos idiomas possam ser adicionados apenas pela configuração. Pense com muito cuidado antes de seguir esse caminho, porque as línguas humanas são maravilhosamente estranhas, portanto você precisa de um mecanismo de regras muito expressivo. Basicamente, você estaria inventando uma nova linguagem de programação (uma DSL de conjugação) para benefício duvidoso. Mas você já tem uma linguagem de programação à sua disposição que pode fazer tudo o que você precisa. Em qualquer caso, YAGNI.

JacquesB
fonte
1
Na verdade, em um comentário à MainMa, mencionei que escrever uma DSL para isso seria inútil, porque poucas línguas naturais são semelhantes o suficiente para fazer valer o esforço. Talvez o francês / o espanhol / o italiano estejam próximos o suficiente , mas não valem o esforço extra, considerando que a quantidade de regras é altamente estática em qualquer idioma. Os outros pontos que você mencionou sobre complexidade foram minhas preocupações exatas, e acho que você entendeu maravilhosamente o que eu estava perguntando na minha pergunta e deu uma ótima resposta com exemplos, então marque com +1!
precisa saber é o seguinte
5

Concordo 100% com a resposta de Dan Pichelman, mas gostaria de acrescentar uma coisa. A pergunta que você deve fazer aqui é "quem vai manter / estender / corrigir a lista de palavras?". Se é sempre a pessoa que também mantém as regras de um idioma específico (o desenvolvedor em particular, acho), não faz sentido usar um arquivo de configuração externo se isso torna as coisas mais complexas - você não obterá nenhum benefício com isso. isto. Desse ponto de vista, fará sentido codificar essas listas de palavras, mesmo que você precise alterá-las periodicamente, desde que seja suficiente fornecer uma nova lista como parte de uma nova versão.

(Por outro lado, se houver uma pequena chance de alguém poder manter a lista no futuro, ou se você precisar alterar as listas de palavras sem implantar uma nova versão do seu aplicativo, use um arquivo separado.)

Doc Brown
fonte
Esse é um bom ponto - no entanto, é muito provável que eu seja a única pessoa que realmente mantenha o código, pelo menos nos próximos anos. A parte boa disso é que, mesmo que as strings sejam codificadas, é um conjunto muito pequeno de strings / regras que dificilmente serão alteradas em breve (como é a linguagem natural, que não evolui muito para o ano seguinte) -ano). Dito isto, as regras de conjugação, cordas de terminação do verbo, etc, com toda a probabilidade ser o mesmo para nossa vida :)
Chris Cirefice
1
@ ChrisCirefice ": exatamente o que quero dizer.
Doc Brown
2

Mesmo que a codificação física pareça boa aqui, e melhor do que carregar dinamicamente os arquivos de configuração, eu ainda recomendo que você separe estritamente seus dados (o dicionário de verbos) do algoritmo . Você pode compilá-los diretamente no seu aplicativo no processo de compilação.

Isso economizará muito trabalho com a manutenção da lista. No seu VCS, você pode identificar facilmente se um commit mudou o algoritmo ou apenas corrigiu um erro de conjugação. Além disso, a lista pode precisar ser anexada no futuro para os casos que você não considerou. Especialmente, o número dos 32 verbos irregulares que você contou não parece ser exato. Enquanto esses parecem abranger os mais usados, encontrei referências a 133 ou mesmo a 350 deles.

Bergi
fonte
Bergi, planejei separar os dados do algoritmo. O que você observa sobre os irregulares franceses - a definição de irregular é mal compreendida. O que quero dizer quando digo irregular são os verbos que não podem ser 'calculados' ou conjugados apenas a partir de sua forma infinitiva. Os verbos irregulares não têm um padrão específico e, portanto, precisam ter suas conjugações explicitamente listadas (em French.Verb.Conjugation.Irregular`, por exemplo). Tecnicamente, verbos -ir são 'irregular', mas eles realmente têm um padrão de conjugação fixo :)
Chris Cirefice
0

A parte importante é a separação de preocupações. Como você consegue isso é menos relevante. ou seja, Java está bem.

Independentemente de como as regras são expressas, você deve adicionar um idioma para alterar uma regra: quantos códigos e arquivos você precisa editar?

Idealmente, a adição de um novo idioma deve ser possível adicionando um arquivo 'english.xml' ou um novo 'EnglishRules implementa o objeto ILanguageRules'. Um arquivo de texto (JSON / XML) oferece uma vantagem se você deseja alterá-lo fora do ciclo de vida da construção, mas requer uma gramática complexa, análise e será mais difícil depurar. Um arquivo de código (Java) permite expressar regras complexas de uma maneira mais simples, mas requer uma reconstrução.

Eu começaria com uma API Java simples por trás de uma interface independente de linguagem limpa - conforme necessário nos dois casos. Você sempre pode adicionar uma implementação dessa interface apoiada por um arquivo XML posteriormente, se desejar, mas não vejo a necessidade de resolver esse problema imediatamente (ou nunca).

ptyx
fonte