Quatro níveis de programação em pesquisa

por Iporã Possantti, Engenheiro Ambiental, Mestre e doutorando em Recursos Hídricos e Saneamento Ambiental (IPH/UFRGS).
Em diversas áreas de pesquisa, novas ferramentas são desenvolvidas na forma de códigos de computador. Não é diferente na área de recursos hídricos, principalmente em hidrologia e planejamento, onde modelos de otimização, simulação e processamento de dados são largamente empregados. Nesse post é feita uma proposta de classificação dessas ferramentas. O objetivo é criar uma terminologia prática. Exemplos são dados com Python.
Leia e execute os códigos desse post no Google Colab
Esse post foi originalmente escrito para o Google Colab. Nessa plataforma, se você acessar pela sua conta google, é possível executar os códigos diretamente no navegador, sem nenhuma configuração.
Link para visualizar o post no Google Colab:
https://colab.research.google.com/drive/1sL9SWgZWpSlJxN6ZSZXXD3zqf9v0BzTh?usp=sharing
Pesquisa e programação de códigos
Em diversas áreas de pesquisa, novas ferramentas são desenvolvidas na forma de códigos de computador. Não é diferente na área de recursos hídricos, principalmente em hidrologia e planejamento, onde modelos de otimização, simulação e processamento de dados são largamente empregados.
Nesse post é feita uma proposta de classificação dessas ferramentas. O objetivo é criar uma terminologia prática. Exemplos são dados com Python.
Nível 1: hard coding
É a forma mais simples de programar um código de processamento. Os dados estão diretamente escritos junto com o código de processamento, atribuídos a variáveis, e não existem níveis hierárquicos de códigos, como as funções. Qualquer modificação deve ser feita de forma exaustiva no código inteiro. Por isso, hard coding (“programação dura”). Os resultados são impressos na tela do computador. Assim, existem duas seções: a seção de processamento e a seção de impressão em tela.
Um exemplo simples, em Python:
[in:] # processamento s = 1 + 2 # impressão em tela print(s) [out:] >>> 3
Nível 2: soft coding
Aqui surgem as funções. As funções executam operações unitárias, que podem ser convocadas em muitas partes do código. Assim, uma modificação na definição função é automaticamente atualizada em todas as partes do código que ela é convocada. Os dados ainda são escritos no código, mas são passados como parâmetros das funções. Dessa maneira, uma terceira seção é adicionada: a seção que define as funções

Esquema de um código nível 2, quando funções passam a fazer operações unitárias, aumentando a eficiência de programação. Elaboração do autor.
No exemplo anterior, o processamento poderia fazer uso de uma função que soma dois números quaisquer, passados como parâmetros. Essa função é convocada no código de processamento:
[in:] # definição de função: def somar(n1, n2): """ uma função que soma dois números, passados como parâmetros :param n1: primeiro número :param n2: segundo número :return : soma dos dois números """ def_s = n1 + n2 return def_s # processamento a = 1 b = 2 s = somar(n1=a, n2=b) # impressão em tela print(s) [out:] >>> 3
Nível 3: Input, Process, Output (IPO)
O terceiro nível consiste na separação completa daquilo que é dado e aquilo que é código. Isso requer a importação de dados que estão armazenados em arquivos e a exportação de dados para arquivos. Essas operações de input e output (I/O) podem ser feitas por funções novas ou mesmo funções de bibliotecas específicas para isso (como a <pandas>, em Python). Isso adiciona pelo menos mais duas seções: uma seção de importação e outra de exportação, resultando na estrutura IPO – input, process, output.

Esquema de um código nível 3, quando os dados são completamente separados do código. Elaboração do autor.
Voltando ao nosso exemplo, considere que existe um arquivo de texto chamado input.txt que possui os número que queremos somar. Os dados estão organizados separados por “;” da sequinte forma (A e B são os rótulos dos números):
;A ;B 0;1;2
O código abaixo cria o arquivo de entrada para nosso exemplo:
[in:]
import pandas as pd
dct = {'A': 1, 'B': 2}
df = pd.DataFrame(dct, index={'': 0})
df.to_csv('./input.txt', sep=';')
Para o código funcionar, a formatação dos dados no arquivo de entrada precisa ser previamente conhecida. Isso requer um certo grau de documentação, tanto na forma de comentários no código como em documentos armazenados em repositório na internet, como o GitHub.
O valor da soma, por sua vez, deverá ser exportado para um arquivo de texto também, chamado output.txt. Deverá ter a mesma formatação do arquivo de entrada, mas com um campo adicional chamado “S”.
Usando <pandas>, o problema fica assim:
[in:]
import pandas as pd # importar biblioteca pandas
# definição de função:
def somar(n1, n2):
"""
uma função que soma dois números, passados como parâmetros
:param n1: primeiro número
:param n2: segundo número
:return : soma dos dois números
"""
def_s = n1 + n2
return def_s
# importar dados de input.txt
input_file = './input.txt'
print('Importar dados de: {}'.format(input_file))
indf = pd.read_csv(input_file, sep=';') # obter DataFrame
a = indf['A'][0] # obter o primeiro valor da coluna 'A '
b = indf['B'][0] # obter o primeiro valor da coluna 'B '
print('Primeiro número: {}'.format(a))
print('Segundo número: {}'.format(b))
print('>> OK')
# processamento
s = somar(n1=a, n2=b)
print('\nSoma: {} + {} = {}\n'.format(a, b, s))
# exportar dados para output.txt
output_file = './output.txt'
print('Exportar dados para: {}'.format(output_file))
outdf = pd.DataFrame({"A": a, "B": b, "S": s}, index={"": 0}) # criar DataFrame
outdf.to_csv(output_file, sep=';', index=False) # exportar para
print('>> OK')
[out:]
>>> Importar dados de: ./input.txt
>>> Primeiro número: 1
>>> Segundo número: 2
>>> >> OK
>>> Soma: 1 + 2 = 3
>>> Exportar dados para: ./output.txt
>>> >> OK
Nível 4: Aplicação
Na maioria dos casos de pesquisa, o nível 3 de programação já é suficiente: importa, processa e exporta (o modelo input, process, output – IPO). Mas é possível direcionar o código para um público maior, pensando no modelo de aplicação.
Uma aplicação tem os seguinte objetivo:
Ampliar a quantidade de usuários da ferramenta.
Portanto, para ter sucesso, uma aplicação precisa deixar tudo extremamente fácil e intuitivo. Todas as funcionalidades devem estar bem documentadas e manuais com exemplos práticos devem ser disponibilizados, de preferência em uma página da internet que hospeda o projeto da aplicação.
O modelo de aplicação demanda alguma forma de interação com o usuário da aplicação, seja per uma interface de terminal (TUI) ou mesmo por uma interface gráfica (GUI). A interface consiste no frontend da aplicação. Com essa interface, o usuário determina comandos ou manualmente indica arquivos para serem processados. O usuário precisa ser considerado de forma genérica, leigo, que não precisa entender como o programa funciona.
Uma vez autorizado pelo usuário, o processamento é feito pelo backend da aplicação. Assim, uma aplicação consiste em uma forma interativa de acionar os códigos IPO por um usuário “leigo”.

Esquema de um código nível 4. Esse nível requer um frontend para a interação com o usuário, que é considerado leigo em programação. Elaboração do autor.
TUI: interface baseada em terminal de comandos
Uma TUI (Terminal-based User Interface) pode ser programada de forma bem simples com Python. Primeiramente é preciso definir um “laço infinito” que é apenas quebrado com um comando de saída fornecida pelo usuário.
Um exemplo em Python:
[in:]
# a TUI como um função
def tui():
# entra no laço infinito:
while True:
# mensagem de inicialização:
print('\n>> Olá! Essa é uma interface de terminal')
# função input() permite o usuário inserir texto no código
command = input('\n>> Voce deseja fechar o programa? (s / n): ')
# condições de parada
if command.upper() == 'S':
# mensagem para saída
print('\n>> Ok, fechando o programa...')
break # quebra o laço
else:
print('\n>> Ok, continuamos aqui!')
# função da interface é convocada:
tui()
[out:]
>> Olá! Essa é uma interface de terminal
>> Voce deseja fechar o programa? (s / n): d
>> Ok, continuamos aqui!
>> Olá! Essa é uma interface de terminal
>> Voce deseja fechar o programa? (s / n): s
>> Ok, fechando o programa...
Validação das entradas do usuário
O problema das interfaces é que precisam lidar com as falhas humanas. As entradas dos usuários precisam ser validadas.
Por exemplo, suponha que uma interface peça para o usuário digitar o caminho de um arquivo. Se o arquivo digitado não existir, por uma falha de digitação, o programa inteiro irá travar. É preciso bolar protocolos de validação diversos para garantir que os usuários consigam usar o programa. Nesse caso do caminho do arquivo, deve-se obviamente checar se o arquivo existe antes de continuar o programa. Se não existir, a interface deve retornar uma mensagem de erro para o usuário.
TUI com menus
Uma forma que eu acho particularmente simples e útil de desenvolver uma TUI é com o uso de menus.
Considere uma aplicação que é uma calculadora simples: soma, subtrai, multiplica e divide. A TUI apresenta para o usuário um menu das operações disponíveis. O usuário precisa digitar uma “chave” para acionar cada operação. Com base na “chave” entrada, a operação é acionada, por meio de uma ou mais funções que requerem do usuário que entre com os números. Precisam ser números, não textos. A TUI também precisa oferecer uma opção de “sair” do programa, para quando o usuário estiver já entediado.
O código para essa aplicação tem 211 linhas, por isso será omitido aqui. Mas você pode conferir e executar o código no Google Colab.
Quando executada, essa aplicação tem a seguinte cara:
[out:]
************ CALCULADORA ************
************ Menu ************
Options Keys
Adição...............0
Subtração............1
Multiplicação........2
Divisão..............3
Exit program..............e
>>> Enter a menu key: 0
>>> OK
******* Adição *******
Digite o primeiro número!
Entre um número inteiro positivo: 2
>>> OK
Digite o segundo número!
Entre um número inteiro positivo: 3
>>> OK
2 + 3 = 5
************ CALCULADORA ************
************ Menu ************
Options Keys
Adição...............0
Subtração............1
Multiplicação........2
Divisão..............3
Exit program..............e
>>> Enter a menu key: e
>>> OK
>> Ok, fechando o programa...
GUI: interface gráfica
As interfaces gráficas de usuário (GUI) inserem no modelo de aplicação os objetos chamados widgets. Os widgets são classes e representam os itens programáveis da interface gráfica, como as janelas, os botões, os rótulos, os campos, as caixas de seleção, etc, etc.
Exemplo de alguns widgets em uma GUI:
Bibliotecas para GUI em Python
Em Python, existem diversas bibliotecas (pacotes) que oferecem widgets para GUI. O pacote “padrão de fábrica” que vem junto quando se instala python para desktop é o TkInter.
Um tutorial simples para o TkInter:
https://www.dummies.com/programming/python/using-tkinter-widgets-in-python/
Lista completa de bibliotecas de GUI para Python:
https://wiki.python.org/moin/GuiProgramming
Uma solução prática: GUIs que se parecem formulários
As GUI não precisam ser complexas. O InVEST, é um software de mapeamento de serviços ambientais e capital natural da Stanford University (https://naturalcapitalproject.stanford.edu/software/invest ) que oferece GUIs bem simplificadas para cada modelo.
As GUIs se parecem com formulários onde o usuário aponta os arquivos de entrada e nomeia os arquivos de saída. Um botão de execução é ativado assim que todas as entradas obrigatórias são preenchidas.
No QGIS (https://www.qgis.org ) a abordagem é idêntica, os geoalgoritmos possuem um frontend na forma de GUIs simplificadas, que se parecem formulários.
Quem sabe esse seja o melhor um grupo de pesquisa possa oferecer sem se transformar uma empresa de software!




