quinta-feira, 9 de janeiro de 2020

Emprego do Linq Parte1

Recebi um teste para resolver algumas questões e quero mostrar as soluções.

LINQ

Seja uma classe

List employee = new List()
{
 new Employee() { Name = "John", Salary = 2500, Bonus = 100 },
 new Employee() { Name = "Ed", Salary = 3000, Bonus = 100 },
 new Employee() { Name = "Taylor", Salary = 2650, Bonus = 150 },
 new Employee() { Name = "Mike", Salary = 1580, Bonus = 120 },
 new Employee() { Name = "Ted", Salary = 1950, Bonus = 150 },
 new Employee() { Name = "Adam", Salary = 2300, Bonus = 0 },
};

1. Implemente um método que retorne a média salarial desta lista.
2. Retorne os nomes das pessoas que ganham acima da média.
3. Faça uma nova lista que retorne a soma do salário e do bonus.
4. Aproveitando o item anterior, retorne uma nova lista das pessoas que ganham abaixo da média (salário + bonus) e acrescente 15%.

Solução:

Para manipular uma lista com 3 colunas foi preciso fazer a classe Employee e o conteúdo foi:

    class Employee
    {
        public string Name { get; set; }
        public double Salary { get; set; }
        public double Bonus { get; set; }
    }

Para manipular uma lista com 2 colunas foi preciso fazer a classe NomeSalario e o conteúdo foi:

    class NomeSalario
    {
        public string Name { get; set; }
        public double SalarioTotal { get; set; }
    }

O código-fonte completo no método Main() está abaixo:

static void Main(string[] args)
{
 List employee = new List()
 {
  new Employee() { Name = "John", Salary = 2500, Bonus = 100 },
  new Employee() { Name = "Ed", Salary = 3000, Bonus = 100 },
  new Employee() { Name = "Taylor", Salary = 2650, Bonus = 150 },
  new Employee() { Name = "Mike", Salary = 1580, Bonus = 120 },
  new Employee() { Name = "Ted", Salary = 1950, Bonus = 150 },
  new Employee() { Name = "Adam", Salary = 2300, Bonus = 0 },
 };

 Console.WriteLine("A média salarial é {0}", GetMeanSalary(employee));
 Console.WriteLine("Os funcionários que ganham acima da média são: ");
 var nomeSalarioAcimaMedia = NamesAboveMeanSalary(employee);
 foreach (var item in nomeSalarioAcimaMedia)
 {
  Console.WriteLine(item.Name);
 }


 Console.WriteLine("\nA lista com nomes e salário total é: ");
 var nomeSalario = SumSalaryBonus(employee);
 foreach (var item in nomeSalario)
 {
  Console.WriteLine("{0,-7} {1,10}", item.Name, item.SalarioTotal);
 }
 
 Console.WriteLine("A média (salário + bonus) é: {0:C2}\n", GetMeanSalary(nomeSalario));
 
 Console.WriteLine("As pessoas que ganham abaixo da média e tiveram reajuste de 15% são: ");
 var listaReajustada = GetTotalSalaryBelowMeanApply15percent(nomeSalario);
 foreach (var item in listaReajustada)
 {
  Console.WriteLine("{0,-7} {1,10}", item.Name, item.SalarioTotal);
 }
}

static double GetMeanSalary(List lista)
{
 return lista.Sum(x => x.Salary) / lista.Count();
}

static List NamesAboveMeanSalary(List lista)
{
 return lista.Where(x => x.Salary > GetMeanSalary(lista)).ToList();
}

static List SumSalaryBonus(List lista)
{
 List nomeSal = new List();
 foreach (var item in lista)
 {
  nomeSal.Add(new NomeSalario() { Name = item.Name, SalarioTotal = item.Salary + item.Bonus});
 }
 return nomeSal;
}

static double GetMeanSalary(List lista)
{
 return lista.Sum(x => x.SalarioTotal) / lista.Count();
}

static List GetTotalSalaryBelowMeanApply15percent(List lista)
{
 var abaixoMediaSalarioTotal = lista.Where(x => x.SalarioTotal < GetMeanSalary(lista));
 foreach (var item in abaixoMediaSalarioTotal)
 {
  item.SalarioTotal = item.SalarioTotal * 1.15;
 }
 return lista;
}

A saída do programa da implementação é:

A média salarial é 2330
Os funcionários que ganham acima da média são:
John
Ed
Taylor

A lista com nomes e salário total é:
John          2600
Ed            3100
Taylor        2800
Mike          1700
Ted           2100
Adam          2300
A média (salário + bonus) é: R$2.433,33

As pessoas que ganham abaixo da média e tiveram reajuste de 15% são:
John          2600
Ed            3100
Taylor        2800
Mike          1955
Ted           2415
Adam          2645


Conceitos

1. Sem citar código/tecnologia defina o que é abstração. Cite um exemplo.
2. O que é herança? Quais as vantagem de usá-la?
3. O que é polimorfismo?
4. O que é classe e o que é objeto?

O que é débito técnico?

Git

Se você tem uma branch chamada task1234 e quer fazer um merge com a branch master, qual o procedimento?

a) Estar na branch task1234 e digitar git merge master
b)
c)
d) Estar na branch master e digitar git merge task1234

O stash tem uma estrutura de dados:

a) Fila
b)
c) Pilha
d) Árvore

quinta-feira, 26 de dezembro de 2019

Git e Github

Git e Github para iniciantes

https://www.udemy.com/course/git-e-github-para-iniciantes

Aula 6 - Configuração inicial do Git

Configurando o sistema

$ git config --global user.name "Murilo Fujita"
$ git config --global user.email "murilofujita@gmail.com"

Configurar o editor de texto

$ git config --global core.editor sublime/vim/emacs

Verificando qual o valor atribuído
$ git config user.name
$ git config user.email

Listando todos os valores atribuídos na configuração
$ git config --list

Aula 7 - Inicializando um repositório

Crie um diretório
$ mkdir myGithub

Entre neste diretório
$ cd myGithub

Inicie o monitoramento dos arquivos a partir deste diretório-raiz
$ git init

Aula 9 - O ciclo de vida dos status de seus arquivos

Estados dos arquivos no ciclo de vida

Untracked → Unmodified → Modified → Staged

  • Untracked: não é rastreado e o git desconhece sua existência
  • Unmodified: Não teve modificação
  • Modified: Arquivo que teve modificação
  • Staged: arquivo afetado pelo comando commit.

Um commit é a criação de uma imagem dos arquivos que estão sendo monitorados pelo git.

Adicione o(s) arquivo(s) que serão monitorados pelo git:
$ git add readme.md

Faça o commit:
$ git commit -m "Primeiro commit."

Observação 1: sempre analise o cenário com o comando git status.

Observação 2: caso apareça a mensagem:

On branch master
Changes not staged for commit:
        modified:   readme.md

Significa que nada foi adicionado para acontecer o commit. Corrija através do comando
$ git add [arquivo]

para só então realizar o commit:
$ git commit -m "nova descrição"

Aula 10 - Visualizando logs

Visualize o histórico dos commits.
$ git log

commit 1fed672a9473d092d401d3238fb4f77139cf63fa (HEAD -> master)
Author: Murilo Fujita 
Date:   Wed Dec 18 19:52:45 2019 -0300

    atualizado 1.

commit 380e8296c40f9f9c17dcb05c2984f0f64f6a44d7
Author: Murilo Fujita 
Date:   Wed Dec 18 19:44:06 2019 -0300

    Primeiro commit.

Algumas variações da visualização dos logs.
$ git log --decorate
$ git log --author="Murilo Fujita"

Comando para visualizar de forma resumida o autor e as mensagens de commits.
$ git shortlog

Comando para visualizar de forma resumida apenas o nome e a quantidade.
$ git shortlog -sn

Git exibindo as branches
$ git log --graph

Se quiser detalhes específicos do commit, digite o comando git log e em seguida copie a hash do commit desejado
$ git show [hash]

Aula 11 - Visualizando o diff

Estes comandos são feitos antes de realizar o commit. Desta forma é possível saber quais foram as mudanças antes de realizar o commit.

As linhas na cor verde (sinal de + no início da linha) indicam quais foram adicionadas.
$ git diff

Para saber somente os arquivos que foram modificados
$ git diff --name-only

Observação: Caso já tenha colocado na área de staged, o git diff não mostra diferenças.

Aula 12 - Desfazendo as coisas

Este comando é válido somente se houve alterações no arquivo e logo em seguida desistiu do que fez (não fez "git add [arquivo]").
$ git checkout [arquivo]

Caso já tenha colocado na área de stage através do comando "git add [arquivo]", o comando é:
$ git reset HEAD [arquivo]
$ git reset HEAD readme.md
Unstaged changes after reset:
M readme.md Geralmente se quis remover o arquivo da área staged, também quer desfazer as modificações do arquivo. Então:
$ git checkout [arquivo]

Nesta situação, o commit foi feito. 3 níveis de desfazer.

  • soft: elimina o commit, mas o arquivo permanece na área de staged (depois do git add e antes do git commit).
    • staged → modified
  • mixed: elimina o commit e deixa o arquivo antes do git add (modified da cor verde).
    • staged → unmodified
  • hard: extermina tudo que foi feito (modified da cor vermelha). O arquivo local ainda tem a alteração feita.
    • staged → untracked

Observação: a hash escolhida deve ser a do commit específico (voltar 1, 2 ou n posições do histórico).
$ git reset --hard 1e73c557f51f75750f57be7a21e357b12fdae83c
HEAD is now at 1e73c55 Esta é a aula 12.

Observação: usar o reset --hard causará a seguinte situação: o git mostrará diferenças entre o está no remoto e o que você tem (local). A única forma de atualizar deixando-os iguais é através do parâmetro force.

Aula 13 - Criando um repositório no Github

Todas as instruções do quick setup apareceram quando não foi colocado não selecionado para criar o arquivo readme.

Aula 14 - Criando e Adicionando uma chave SSH

Aula 15 - Ligando repositório local a um remoto

$ git remote add origin https://github.com/mfujita/myGithub.git

Nome do repositório padrão ao qual local e remoto estão ligados
$ git remote

origin

Informações mais detalhadas
$ git remote -v

origin  https://github.com/mfujita/myGithub.git (fetch)
origin  https://github.com/mfujita/myGithub.git (push)

Enviando logs e demais informações para o repositório remoto.
$ git push -u origin master

Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (9/9), 806 bytes | 403.00 KiB/s, done.
Total 9 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/mfujita/myGithub.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

Aula 16 - Enviando mudanças para um repositório remoto

$ git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 298 bytes | 298.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/mfujita/myGithub.git
   1e73c55..4498c53  master -> master

Aula 17 - Clonando repositórios remotos

Clonando através de HTTPS
$ git clone https://github.com/mfujita/myGithub.git [nome_do_seu_projeto]

Clonando através de SSH
$ git clone git@github.com:mfujita/myGithub.git [nome_do_seu_projeto]

Aula 25 - Criando o .gitignore

Crie um arquivo denominado .gitignore e configure para determinar quais arquivos não serão monitorados pelo git.

Pode ser pela extensão ou o nome específico do arquivo.

Para um modelo completo acesse https://github.com/github/gitignore

domingo, 12 de novembro de 2017

Selecionando arquivos de 6 em 6 ou outros intervalos

Quando os nomes dos arquivos estão sequenciados (000, 001, 002, 003, ...) e há necessidade de alguma ação como copiar determinados arquivos para outro diretório, um script resolve o problema.

Inicialmente todos os arquivos são listados pelo comando ls *.jpg e lidos pelo while. Nomes dos arquivos: z0001.jpg, z0002.jpg, z0003.jpg, ...

A variável file armazena o nome do arquivo a partir da segunda posição e elimina ".jpg".
Por exemplo, o nome z0001.jpg torna-se 001, z0002.jpg torna-se 002 e assim por diante.

Para eu obter somente os arquivos múltiplos de 6, a variável calc armazena o resto da divisão por 6.

O comando de decisão if verifica os arquivos que são múltiplos de 6 e copia para outro diretório.

Caso queria de 5 em 5 ou qualquer outro intervalo, modifique a linha que contém a variável calc.

#!/bin/bash

ls *.jpg | { 
  while read arq 
    do
      file="`echo $arq | cut -c 2- | sed 's/.jpg//'`"
      calc=`echo "$file % 6" | bc`
      if [ $calc == 0 ]
        then
          cp -v $arq de6em6
      fi
done
}

quarta-feira, 19 de outubro de 2016

Procurando texto em arquivos de diversos diretórios

Faço uso de mala direta e uso as ferramentas que criei para propósitos diferentes como comunicar o número de faltas, divulgar notas de trabalhos e provas bem como emitir comunicados com meus alunos.

Às vezes os alunos mudam o endereço de e-mail e preciso atualizar em todos os meus programas. Como procurar todas as ocorrências nos arquivos que estão espalhado em diversos diretórios?

Acompanhe o shell script a seguir no qual a aluna Rosalia pediu para receber meus comunicados em outro endereço de e-mail.

#!/bin/bash
find . -name \*.cs -exec ls {} \; > arquivosCS.txt
linhas=`wc -l arquivosCS.txt | awk '{printf "%s\n",$1}'`

for i in `seq $linhas`
  do
    grep -i rosalia `head -n $i arquivosCS.txt | tail -n 1` > /dev/null
    if [ $? == 0 ]
      then 
        echo `head -n $i arquivosCS.txt | tail -n 1`
    fi
  done
  

Os passos são:

  1. Fazer o levantamento de todos os arquivos que têm códigos-fontes (extensão .cs)
  2. Determinar quantos arquivos têm a extensão cs
  3. Executar o for para analisar cada arquivo (combinando head e tail)
  4. Se o nome da pessoa está naquele arquivo (comando grep), então $? retorna 0 (zero) que é o mesmo que verdadeiro.
  5. O que for verdadeiro, faço que exiba o arquivo com o comando echo.

A instrução > /dev/null direciona a saída para o dispositivo que anula seu efeito, ou seja, não imprime a saída.

Este script deve ser executado no diretório que contém todos os arquivos relevantes da sua busca, ou seja, será feita a busca em todos os níveis a partir do ponto onde é executado.

A saída abaixo gerou uma listagem de todos os arquivos que tenho que fazer as modificações:

./faltas2016s2/faltas2016s2/faltas2016s2/FaltaAlunos.cs
./RHMatFinEstatistica2016/RHMatFinEstatistica2016/RHMatFinEstatistica2016/MalaDireta.cs   
  

Conclusão: Funciona muito bem e recomendo que adote a ferramenta ao invés de confiar na memória acreditando que modificou em todos os arquivos necessários.

quinta-feira, 5 de fevereiro de 2015

Usando curl para acessar dados do roteador

Uma das minhas preocupações é a de saber quantas e quais máquinas estão conectadas à minha rede. Para este propósito tenho que acessar o roteador pelo browser, realizar a autenticação preenchendo os campos usuário e senha e por fim entrar na página que fornece o recurso de listar quem está nos meus domínios.

Um bom shell script tem a vantagem de substituir muito bem esta questão da segurança, mas a desvantagem de não entregar a resposta formatada como as páginas HTML neste caso. É claro que os dados podem ser tratados e criar uma página HTML elaborada.

Antes de abordar o comando, exportei os valores da variável pelo arquivo .bashrc por questões de segurança. Você pode usar o nome de login e senha explicitamente na linha de comando que funcionará da mesma forma.

Finalmente, o comando usado é o curl.

curl -b -c -u $user:$pass http://192.168.xx.xx/Status/st_session.htm | grep 192.

A opção -b transfere a informação para o servidor HTTP como um cookie.
A opção -c armazena este cookie.
A opçao -u informa o nome de usuário e senha separados por dois pontos (:).

Em seguida o endereço URL e saída dele é jogada para o grep que busca os IPs que començam com 192 que é o meu intervalo de IPs.

Como a resposta é muito confusa, acrescentei mais alguns comandos:

  • substituição do símbolo "[" por "pular linha"
  • substituição do símbolo "," por espaço em branco.
  • exibir o segundo campo
  • ordenar de forma crescente
  • exibir os resultados distintos

O comando completo é:

curl -b -c -u $user:$pass http://192.168.xx.xx/Status/st_session.htm | grep 192. | tr "[" "\n" | sed 's/,/ /g' | cut -f2 -d" " | sort | uniq

Algumas estatísticas aparecem, mas o essencial está feito. Levo um tempo insignificante em comparação com o uso do browser.

Obviamente será preciso que você substitua os valores da autenticação, as URLs e adeque os comandos para que a resposta fique legível.

quarta-feira, 17 de dezembro de 2014

Análise dos números da Mega Sena versão 2

Com o ganho de experiência, aprende-se a resolver os mesmos problemas de forma otimizada. Há um tempo foi apresentado o Análise dos números da Mega Sena e ao executar o mesmo script, resulta em erros porque a formatação dos dados foi alterada. Com algumas novas "sacadas", há uma nova versão disponível acompanhada dos comentários.

Com relação às melhorias, lista-se:

  • Saída sem exibir as respostas do unzip;
  • Ao invés de localizar um bloco que contém os números sorteados, localiza-se a data do sorteio e armazena-se as 6 linhas abaixo (as dezenas sorteadas);
  • Como consequência do item anterior, menos tags HTML para manipular.

Abaixo o shell script comentado mostrando a frequência de cada dezena sorteada e os 6 números mais frequentes.


#!/bin/bash
# Created in dez/26/2012 by murilofujita@gmail.com
# Modified in dez/14/2014
# Optimized in dez/16/2014

if [ -f D_mgsasc.zip ]
  then
    rm D_mgsasc.zip
fi

if [ -f T2.GIF ]
  then
    rm T2.GIF
fi

if [ -f /tmp/sena1.txt ]
  then
    rm /tmp/sena*.txt
fi

wget http://www1.caixa.gov.br/loterias/_arquivos/loterias/D_mgsasc.zip
unzip D_mgsasc.zip >& /dev/null

tr -d '\r' < d_megasc.htm > $$
mv $$ d_megasc.htm

ano_atual=`date +%Y`

# Quantidade de jogos realizados
for ano in `seq $ano_atual 1 $ano_atual`
    do
      jogo=`sed -n "/$ano/{x;p;};h" d_megasc.htm | sed 's/<[^>]*>//g'` 
    done 

# sorteios=`echo "$jogo"`
printf "Dados dos `echo "$jogo" | tail -n 1` sorteios"

# Obtendo os números sorteados
# "/ano" é a palavra para ser localizada. Se for apenas "ano" o numero 2000 pode aparecer 
# em outro campo. Por exemplo: 2000 é o número de ganhadores da quadra em 28/04/2004
  for ano in `seq 1996 1 $ano_atual` 
    do  
      awk "/\/$ano/ {for(i=1; i<=6; i++) {getline; print}}" d_megasc.htm 
    done > /tmp/sena1.txt

# Necessário remover as tags para que a massa de dados contenha apenas os números sorteados
sed 's/<[^>]*>//g' /tmp/sena1.txt > $$ 
mv $$ /tmp/sena1.txt

echo 
echo Exibindo a frequência dos números sorteados da Mega Sena
  for i in `seq 9`
    do
      printf "0$i `grep 0$i /tmp/sena1.txt | wc -l`  "
    done
  printf "10 `grep 10 /tmp/sena1.txt | wc -l` "
  printf "\n"

  for i in `seq 11 1 60`
    do
      printf "$i `grep $i /tmp/sena1.txt | wc -l`  "
      if [ $((i % 10)) -eq 0 ]
        then
          printf "\n"
      fi
    done
  printf "\n"

for i in `seq 9`; do printf "0$i `grep 0$i /tmp/sena1.txt | wc -l`\n" ; done >> /tmp/sena2.txt
for i in `seq 10 1 60`; do printf "$i `grep $i /tmp/sena1.txt | wc -l`\n" ; done >> /tmp/sena2.txt
awk '{printf "%3s %3s\n",$2, $1}' /tmp/sena2.txt | sort -n | tail -n 6 > /tmp/sena3.txt

echo Os números mais sorteados são:
echo Dezena Frequência

for i in `seq 6`
  do
     tail -n $i /tmp/sena3.txt | head -n 1 | awk '{printf "%4s %8s\n",$2, $1}'
  done

printf "\n"

awk '{printf "%s\n",$2}' /tmp/sena2.txt | paste -s -d"+"

if [ -f d_megasc.htm ]
  then
    rm d_megasc.htm
fi

Até a data de publicação foram realizados 1661 sorteios e o resultado é exibido abaixo:

Dados dos 1661 sorteios
Exibindo a frequência dos números sorteados da Mega Sena
01 159  02 170  03 157  04 192  05 196  06 163  07 168  08 170  09 143  10 176 
11 154  12 168  13 177  14 151  15 149  16 174  17 181  18 161  19 153  20 159  
21 145  22 137  23 178  24 182  25 148  26 137  27 169  28 176  29 175  30 170  
31 167  32 172  33 184  34 166  35 157  36 173  37 164  38 166  39 143  40 158  
41 175  42 184  43 178  44 173  45 151  46 160  47 170  48 150  49 179  50 173  
51 187  52 177  53 186  54 187  55 144  56 164  57 155  58 162  59 163  60 160  

Os números mais sorteados são:
Dezena Frequência
  05      196
  04      192
  54      187
  51      187
  53      186
  42      184

159+170+157+192+196+163+168+170+143+176+154+168+177+151+149+174+181+161+153+159+145+137+178+182+148+137+169+176+175+170+
167+172+184+166+157+173+164+166+143+158+175+184+178+173+151+160+170+150+179+173+187+177+186+187+144+164+155+162+163+160

A razão desta última linha é fazer uma checagem. Analise:

1661 sorteios * 6 números sorteados = 9966 números.

Basta copiar a linha do somatório e colar em uma calculadora ou célula de planilia eletrônica para verificar se coincide com a mesma quantidade dos números sorteados.

Conclusão: Por causa da diferença no formato da tabela, invalidou o primeiro script da "mega sena" e forçou uma otimização na forma de lidar com novo arquivo. O tempo de processamento diminuiu e é possível fazer uma verificação se todos os números foram contabilizados.

sábado, 24 de agosto de 2013

Preenchendo com zeros a esquerda para ordenar números


Um determinado arquivo contém números de 1 a 1045, mas foram gravados fora de ordem. Ao tentar ordená-lo com o comando

$ cat num.txt | sort

tive como resposta a listagem de todos os números que começam com 1, em seguida os que começam com 2 e assim por diante. Logo, não é a forma esperada e para resolver esta situação é preciso preencher com 0 (zero) até que cada número tenha 4 algarismos. Em outras palavras, ficará assim:

0001, 0002, ..., 0009, 0010, ..., 0099, 0100, ..., 0999, 1000, ..., 1045

O código-fonte que apresento desta vez é da linguagem C#. No IDE Visual Studio, abra um novo projeto e escolha o Template: Console Application. Esta opção chama o prompt de comando que é exclusivamente texto.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace AjeitaParaNumerar
{
    class Program
    {
        static void Main(string[] args)
        {
            int i, quantidade=0;
            Console.WriteLine("Este programa le um arquivo de nome num.txt e coloca 3 zeros se o numero for menor que 10, 2 zeros se maior do que 9 e menor do que 100 e por fim, 1 zero se maior do que 99 e moneor do que 1000.");
            StreamReader sr = new StreamReader("num.txt");

            string leitura;
            
     while ((leitura = sr.ReadLine()) != null)
            {
                quantidade++;
            }

            sr.Close();

            StreamWriter sw = new StreamWriter("ok.txt");
            sr = new StreamReader("num.txt");
            
            for (i = 0; i < quantidade; i++)
            {
                string[] valor = new string[quantidade];
                valor[i] = sr.ReadLine();
                if (Convert.ToInt32(valor[i]) < 10)
                {
                    valor[i] = valor[i].PadLeft(4, '0');
                    sw.WriteLine(valor[i]);
                }

                else if (Convert.ToInt32(valor[i]) > 9 && Convert.ToInt32(valor[i]) < 100)
                {
                    valor[i] = valor[i].PadLeft(4, '0');
                    sw.WriteLine(valor[i]);
                }

                else if (Convert.ToInt32(valor[i]) > 99 && Convert.ToInt32(valor[i]) < 1000)
                {
                    valor[i] = valor[i].PadLeft(4, '0');
                    sw.WriteLine(valor[i]);
                }


                else
                {
                    sw.WriteLine(valor[i]);
                }
            }

            sr.Close();
            sw.Close();
        }
    }
}

Mesmo que o código tenha sido desenvolvido em outro sistema operacional, é possível executá-lo no Linux. Para tal, instale o pacote mono-mcs e em seguida execute a linha de comando com o comando mcs com a seguinte sintaxe:

$ mcs fonte.cs -o nome_do_arquivo

Exemplificando supondo que eu escolhi que nome_do_arquivo seja ajeita.out:

$ mcs Program.cs -o ajeita.out

Como resultado, o arquivo ajeita.out não é executável e para torná-lo, basta usar o procedimento usual de dar permissão de execução:

$ chmod a+x ajeita.out

Finalmente, para executá-lo, faça simplesmente:

$ ./ajeita.out

Ao fim da execução, o arquivo ok.txt foi criado e seu conteúdo é a listagem dos 1045 números com quatro algarismos.

Conclusão: O projeto mono permite uma incrível portabilidade para o Linux. Há muitas facilidades por causa dos métodos prontos do C# e por esta razão, torna o desenvolvimento rápido e capaz de funcionar nos dois sistemas operacionais.

Adicionalmente, ao executar o comando para ordenar os números (cat ok.txt | sort), funcionou perfeitamente.

segunda-feira, 22 de julho de 2013

Backup entre pen-drive e HD

Sempre fiz o mesmo procedimento com relação aos trabalhos feitos na faculdade: o que era produzido nos laboratórios era armazenando no meu pen-drive e este conteúdo copiado para o meu computador pessoal. O problema é que às vezes eu esquecia de fazer a segunda etapa e ao longo de um semestre é certo acumular uma quantidade expressiva. Simplesmente copiar todos os trabalhos pode sobreescrever algum diretório e assim, perder algum trabalho. A solução é executar o script abaixo.

As variáveis origem e destino representam, respectivamente, o ponto de montagem do pen-drive e o meu userspace o qual armazeno meus dados. É importante que os subdiretórios abaixo deles tenham o mesma estruturação:

 $ find -maxdepth 1
.
./laboratorio_de_hardware
./Dev-Cpp 5.2.0.3 IDE Only Portable.7z
./engenharia_de_software
./ra_ads.txt
./minicurso_C
./sistemas_operacionais
./ihc
./php
./estatistica
./horario_de_aulas.odt
./contabilidade

O primeiro passo é fazer todo levantamento dos diretórios e arquivos da origem e destino.

Em seguida, o script separa o que é comum a ambos dentro do arquivo /tmp/comum.txt e o que precisa ser copiado em /tmp/inexistente.txt.

Se há algo a ser copiado são exibidas mensagens contabilizando quantos arquivos são comuns, quantos serão copiados e a quantidade em kB a ser copiado. Todos os diretórios que não existem na variável destino (computador) são criados pois causa um erro se tentar copiar para um diretório que não existe. Depois, é necessário preparar um arquivo que contenha o caminho dos arquivos que serão copiados para o HD. Para tal fim, o sed substitui o valor de $origem por $destino no arquivo /tmp/inexistente.txt. Desta forma, tudo que não é comum ao HD e ao pen-drive está pronto pronto para ser copiado.

Se não houver diferença entre origem e destino, exibe a mensagem: "$origem" e "$destino" têm os mesmos arquivos.

#!/bin/bash
rm /tmp/*.txt

origem="/media/murilo/FUJITA/fatec"
destino="/home/murilo/fatec/periodo2-1s_2013"

cd "$destino" 
find -type f > /tmp/fatec_hd.txt
cd "$origem" 
find -type f > /tmp/fatec_pendrive.txt

# Tem arquivo no pen-drive que nao existe no HD?
pen_drive_linhas=`wc -l /tmp/fatec_pendrive.txt | cut -f1 -d" "`
for i in `seq $pen_drive_linhas`
  do
    pendrive_arquivo=`head -n $i /tmp/fatec_pendrive.txt | tail -n 1`
    grep "$pendrive_arquivo" /tmp/fatec_hd.txt
    if [ $? -eq 0 ]
      then 
        echo "$pendrive_arquivo" >> /tmp/comum.txt
      else
        echo "$pendrive_arquivo" >> /tmp/inexistente.txt
    fi
  done

if [ -f /tmp/inexistente.txt ]
  then
    # Full path for source
    sed "s,^.,$origem," /tmp/inexistente.txt >> /tmp/inexistente1.txt
    sed 's/^/"/g' /tmp/inexistente1.txt >> /tmp/$$
    sed 's/$/"/g' /tmp/$$ >> /tmp/inexistente3.txt
    rm /tmp/$$
    
    inexistente=`wc -l /tmp/inexistente1.txt | cut -f1 -d" "`
    
    echo `wc -l /tmp/comum.txt | cut -f1 -d" "` arquivos comuns.
    echo $inexistente arquivos que estao no pen-drive e nao estão no HD.
    echo `wc -l /tmp/fatec_pendrive.txt | cut -f1 -d" "` no total.
    
    # Quantidade em kB a ser copiado do pen-drive para o HD
    for i in `seq $inexistente`
      do 
        arquivo_inexistente=`head -n $i /tmp/inexistente1.txt | tail -n 1`
        du -m "$arquivo_inexistente" | awk '{printf "%s\n",$1}' 
      done > /tmp/kB_arquivos.txt
    
    echo `paste -s -d"+" /tmp/kB_arquivos.txt | bc` kB a ser copiado do pen-drive para o HD
    sleep 2
    
    sed "s,^,$origem," /tmp/inexistente1.txt >> /tmp/inexistente2.txt
    sed 's/$/"/g' /tmp/inexistente2.txt >> /tmp/inexistente3.txt
    
    # Checking for absent directories
    for i in `seq $inexistente`
      do
        inexistente_diretorio=`head -n $i /tmp/inexistente3.txt | tail -n 1 | sed 's/"//g'`
        /usr/bin/dirname "$inexistente_diretorio" 
      done >> /tmp/inexistente_diretorio.txt
    
    sort /tmp/inexistente_diretorio.txt >> /tmp/inexistente_diretorio2.txt
    uniq /tmp/inexistente_diretorio2.txt >> /tmp/inexistente_diretorio3.txt
    sed "s,$origem,$destino," /tmp/inexistente_diretorio3.txt >> /tmp/inexistente_diretorio4.txt
    
    echo Criando `wc -l /tmp/inexistente_diretorio4.txt | cut -f1 -d" "` diretorios.
    sleep 2
    
    #Creating the absent directories in destination (hd)
    int_diretory2create=`wc -l /tmp/inexistente_diretorio4.txt | cut -f1 -d" "`
    for i in `seq $int_diretory2create`
      do
        diretory2create=`head -n $i /tmp/inexistente_diretorio4.txt | tail -n 1`
        mkdir -p "$diretory2create"
      done
    
    # replacing source path for destination path
    sed "s,$origem,$destino," /tmp/inexistente1.txt >> /tmp/inexistente_destino1.txt 
    
    # performs the copy process from pen-drive to hd
    for i in `seq $inexistente`
      do
        from=`head -n $i /tmp/inexistente1.txt | tail -n 1`
        destination=`head -n $i /tmp/inexistente_destino1.txt | tail -n 1`
        cp -v "$from" "$destination"
      done 
  else
     echo
     echo
     echo "$origem" e "$destino" têm os mesmos arquivos
fi

Suprimindo a saída com a longa listagem de arquivos, estes são os dados obtidos pelo script e o tempo para realizar as tarefas de levantar os dados da origem e destino, comparar o que precisa ser copiado, criar os todos os diretórios e copiar nos seus devidos lugares:

381 arquivos comuns.
284 arquivos que estao no pen-drive e nao estão no HD.
665 no total.
301 kB a ser copiado do pen-drive para o HD
Criando 51 diretorios.

real 0m9.874s
user 0m0.300s
sys 0m0.844s

Ao executar novamente, a seguinte resposta é obtida:

/media/murilo/FUJITA/fatec e /home/murilo/fatec/periodo2-1s_2013 têm os mesmos arquivos

Conclusão: Não importa quantos arquivos tenham se acumulados ao longo do tempo, este script permite sincronizar os conteúdos em um intervalo de tempo extremamente curto.

quarta-feira, 26 de junho de 2013

Comparando 2 arquivos de textos e procurando nomes repetidos

Para desenvolver um software é necessário desenvolver ferramentas de testes e assim, mais softwares são desenvolvidos.

Ao executar o comando find procurando arquivos *.JPG e *.jpg, obtém-se como resposta 633 arquivos.

O problema era o fato do software informar 579 arquivos de imagens. A diferença é 54 unidades.

O primeiro passo é comparar as duas listagens de arquivos e entender o que torna o conteúdo distinto. Como o pen-drive é do tipo VFAT, não tinha permissão para executar scripts, então a solução foi usar a linha de comando e direcionar a saída para um arquivo:

$ ls -R | grep '\./' | cut -c 3- | sed 's/://g' > ~/programas/arq2.txt

Em seguida foi feita uma ordenação no arquivo arq2.txt que a saída foi para arq633.txt:

$ cat arq2.txt | sort > arq633.txt

O arquivo arq633.txt contém 633 linhas e cada uma delas o nome do arquivo *.jpg ou *.JPG

O arquivo com as 579 linhas foi gerado pelo software que estou desenvolvendo. É preciso usar o editor vim para eliminar colunas como data, tamanho do arquivo etc.

O script que levanta quais são os arquivos que diferem está abaixo.

#!/bin/bash

ls repeated_files.txt > /dev/null
if [ $? = 0 ]
  then
    rm repeated_files.txt
fi

for i in `seq 633`
  do
    arq633=`head -n $i arq633.txt | tail -n 1`
    freq=`grep "$arq633" arq633.txt | wc -l | awk '{printf "%s\n",$1}'`
    if [ $freq != 1 ]
      then
        grep ^"$arq633" arq633.txt >> repeated_files.txt
    fi
  done

cat repeated_files.txt | sort | uniq

Comento sobre a linha

grep ^"$arq633" arq633.txt >> repeated_files.txt

Sem o circunflexo (^) não há distinção para a ocorrência

1.jpg
baixa11.jpg
baixa1.jpg
CIMG3701.jpg
CIMG3711.jpg
CIMG3751.jpg
CIMG4531.jpg

porque o grep encontra "1.jpg" em todos os arquivos.

Executando o script obtém-se a seguinte saída:

1.jpg
DSC07857.JPG
DSC07858.JPG
DSC07859.JPG
DSC07861.JPG
DSC07862.JPG
DSC07863.JPG
DSC07864.JPG
DSC07865.JPG
DSC07866.JPG
DSC07867.JPG
DSC07868.JPG
DSC07869.JPG
DSC07870.JPG
DSC07871.JPG
DSC07872.JPG
DSC07873.JPG
DSC07874.JPG
DSC07875.JPG
DSC07876.JPG
DSC07877.JPG
DSC07878.JPG
DSC07879.JPG
DSC07880.JPG
DSC07881.JPG
DSC07882.JPG
DSC07883.JPG
DSC07884.JPG
DSC07885.JPG
DSC07886.JPG
DSC07887.JPG
DSC07888.JPG
DSC07889.JPG
DSC07890.JPG
DSC07891.JPG
DSC07892.JPG
DSC07893.JPG
DSC07894.JPG
DSC07895.JPG
DSC07896.JPG
DSC07897.JPG
DSC07898.JPG
DSC07899.JPG
DSC07900.JPG
DSC07901.JPG
DSC07902.JPG
DSC07903.JPG
DSC07904.JPG
DSC07905.JPG
DSC07906.JPG
DSC07907.JPG
DSC07908.JPG
DSC07909.JPG
DSC07910.JPG

Que são 54 arquivos JPG.

Conclusão: O software processa 633 imagens, mas como há nomes repetidos, 54 são sobreescritos mostrando 579 arquivos JPG/jpg.

Caso encerrado!

segunda-feira, 1 de abril de 2013

Criando tabela em arquivo HTML usando C

Criando tabelas HTML através da linguagem C

Em uma aula de linguagem de programação, foi proposto o seguinte exercício de lógica:

O usuário deve entrar com a quantidade de linhas e colunas e com estes dados, desenhar um retângulo que a borda é o caracter 'O' e o seu interior é preenchido com 'X'.

Por exemplo, se a entrada fosse 3 linhas e 5 colunas, teríamos:

OOOOO
OXXXO
OOOOO

Assim, tive a ideia de fazer um programa em C que fizesse a estrutura de uma tabela HTML pedindo para o usuário a quantidade de linhas e colunas.

O ponteiro para arquivos ps cria o arquivo tabelas.html e ao invés de usar printf que imprime na tela, usa-se fprintf para imprimir dentro do arquivo.

São necessários dois laços for sendo que o for externo diz respeito à linha e o mais interno, à coluna.

Uma vez que o ponteiro é aberto para escrita, deve-se instruí-lo a fechá-lo e o comando para este fim é o fclose(nome_do_ponteiro). Neste exemplo, fclose(ps).

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,j,colunas=1, linhas=1, contador=0;
    FILE *ps;

    ps = fopen("tabela.html","w");
    if (ps == NULL)
    {
        printf("Erro na abertura do arquivo");
        exit(0);
    }

    printf("Programa de geracao de tabelas\n\n");
    printf("Digite o numero de linhas da tabela: ");
    scanf("%d",&linhas);
    printf("Digite o numero de colunas da tabela: ");
    scanf("%d",&colunas);

    fprintf(ps,"<html>");
    fprintf(ps,"\n");
    fprintf(ps," <head>");
    fprintf(ps,"\n");
    fprintf(ps,"   <title></title>\n");
    fprintf(ps," </head>");
    fprintf(ps,"\n\n");

    fprintf(ps,"  <body>\n\n");
    fprintf(ps,"  <table>\n");

    for (i=1; i<=linhas;i++)
    {
        for (j=1; j<=colunas+2; j++)
        {
              if (j==1)
                fprintf(ps,"   <tr> ");
              else if (j==colunas+2)
                fprintf(ps," </tr>\n");

              else
                fprintf(ps," <td></td> ");
              contador++;              
        }
    }

    fprintf(ps,"  </table>\n");
    fprintf(ps,"\n");

    fprintf(ps,"  </body>\n");
    fprintf(ps,"</html>");

    fclose(ps);

    printf("Arquivo tabelas.html criado com sucesso\n");

    return 0;
}

Se estiver usando uma distribuição GNU/Linux, compile com:

$ gcc tabelaHTML.c -o tabelaHTML.out

Em seguida, execute da seguinte forma:

$ ./tabelaHTML.out

Como diz a mensagem, o arquivo tabela.html foi criado com sucesso. Abra este arquivo que está no mesmo diretório que o seu binário e bom proveito!

sábado, 2 de março de 2013

Ponteiro tipo char para imprimir um número indeterminado de caracteres

Uma habilidade muito comum e exigido na linguagem C é a manipulação de caracteres. Como a declaração da variável é do tipo char, é preciso tratar caracter a caracter ao invés da string toda.

O exemplo abaixo declara um ponteiro do tipo char e é atribuído palavras, portanto, contém inclusive espaços.

O laço while analisa o indíce do ponteiro que incrementado antes do fim do bloco verificando se chegou no fim da cadeia de caracteres indicada por '\0'.

A cada vez que o caracter é impresso, o comando printf adiciona um espaço pela ação de "%c ".

# include <stdio.h>
# include <unistd.h>

int main()
{
  int i;
  char *vetor;

  vetor="Este ponteiro de nome vetor passa a armazenar uma frase que poderia ser muito maior do que esta.";
  
  i=0;
  while (vetor[i] != '\0')
  {
    printf ("%c ", vetor[i]);
    i++;    
  }

  printf("\n");

  return 0;
}

O resultado é exibido abaixo:

E s t e p o n t e i r o d e n o m e v e t o r p a s s a a a r m a z e n a r u m a f r a s e q u e p o d e r i a s e r m u i t o m a i o r d o q u e e s t a .

sábado, 2 de fevereiro de 2013

Exibição dos caracteres especiais para HTML

O fato de desenvolver textos HTML tanto para Linux como para Windows tem causado o incoveniente de exibir os caracteres acentuados de forma errada porque os sistemas operacionais usam codificações distintas.

A melhor forma de adequar a ambas as situações é substituir os caracteres problemáticos por códigos que ao serem interpretados pelo navegador são traduzidos pela combinação de vogais acentuadas ou cedilha.

O código abaixo faz a substituição global destes caracteres. Desta forma, basta redigir o texto normalmente e após a execução do script o texto está pronto para ser visualizado por qualquer navegador.

Observação: É preciso juntar o "&" com o que vem em seguida, pois se fosse feito no editor seria interpretado e desta forma não seria exibido o código HTML.

#/bin/bash
# Fonte dos caracteres especiais: http://www.lsi.usp.br/~help/html/iso.html

if [ $# != 1 ]
  then
    echo Digite $0 arq
    exit
  else
    sed 's/Á/\& Aacute;/g' $1 > $$
    sed 's/á/\& aacute;/g' $$ > $1
    sed 's/Â/\& Acirc;/g' $1 > $$
    sed 's/â/\& acirc;/g' $$ > $1
    sed 's/À/\& Agrave;/g' $1 > $$
    sed 's/à/\& agrave;/g' $$ > $1
    sed 's/Â/\& Atilde;/g' $1 > $$
    sed 's/ã/\& atilde;/g' $$ > $1

    sed 's/É/\& Eacute;/g' $1 > $$
    sed 's/é/\& eacute;/g' $$ > $1
    sed 's/Ê/\& Ecirc;/g' $1 > $$
    sed 's/ê/\& ecirc;/g' $$ > $1

    sed 's/Í/\& Iacute;/g' $1 > $$
    sed 's/í/\& iacute;/g' $$ > $1

    sed 's/Ó/\& Oacute;/g' $1 > $$
    sed 's/ó/\& oacute;/g' $$ > $1
    sed 's/Ô/\& Ocirc;/g' $1 > $$
    sed 's/ô/\& ocirc;/g' $$ > $1
    sed 's/Õ/\& Otilde;/g' $1 > $$
    sed 's/õ/\& otilde;/g' $$ > $1

    sed 's/Ú/\& Uacute;/g' $1 > $$
    sed 's/ú/\& uacute;/g' $$ > $1

    sed 's/ç/\& ccedil;/g' $1 > $$

    sed 's/>/\&# 62;/g' $$ > $1
    sed 's/</\&# 60;/g' $1 > $$
    
fi
mv -v $$ $1


segunda-feira, 31 de dezembro de 2012

Análise dos números da Mega Sena

Para encerrar o ano, um script que trabalha com os números dos concursos anteriores da Mega Sena.

O script baixa um arquivo com todos os resultados da Mega Sena, descompacta e apaga arquivos sem importância.

Considero ser uma melhoria em relação ao script da Análise dos números da Quina por simplificar a etapa de extrair os números sorteados e evitar mensagens de erros quando se tenta apagar arquivos que não existem.

#!/bin/bash
# Created in dez/26/2012 by murilofujita@gmail.com

# Baixa e descompacta
wget http://www1.caixa.gov.br/loterias/_arquivos/loterias/D_mgsasc.zip
unzip D_mgsasc.zip

# Apaga arquivos desnecessários
if [ -f D_mgsasc.zip ]
  then
    rm D_mgsasc.zip
fi

if [ -f T2.GIF ]
  then
    rm T2.GIF
fi

if [ -f /tmp/sena1.txt ]
  then
    rm /tmp/sena*.txt
fi

inicio=4 # Primeira linha da sequência do primeiro sorteio 
fim=9 # Última linha da sequência do primeiro sorteio
contador=1 # Primeiro sorteio

# Necessário eliminar o Feed Line e Carriage Return editado pelo windows
tr -d '\r' < d_megasc.htm > $$
mv $$ d_megasc.htm 

# Busca a quantidade de sorteios realizados
jogos=`tail -n 23 d_megasc.htm | head -n 1 | sed 's/<td>//' | sed 's/<\/td>//'`
echo $jogos sorteios realizados

# Loop que elimina as marcações HTML
while [ $contador -le $jogos ]
  do
    for i in `seq $inicio 1 $fim`
      do
        head -n $i d_megasc.htm | tail -n 1 | sed 's/<td>//g' | sed 's/<\/td>//g' 
      done 
    let inicio=$inicio+21 # A cada 21 linhas uma nova sequência do sorteio
    let fim=$fim+21
    let contador=$contador+1    
  done > /tmp/sena1.txt

echo Exibindo a frequencia dos numeros sorteado da Mega Sena
  for i in `seq 9`
    do
      printf "0$i `grep 0$i /tmp/sena1.txt | wc -l`  "
    done
  printf "10 `grep 10 /tmp/sena1.txt | wc -l` "
  printf "\n"

  inicio=11
  fim=20
  while [ $inicio -le 60 ] # Conta a frequencia dos 60 números da Mega Sena
  do
   for i in `seq $inicio 1 $fim`;
     do
       printf "$i `grep $i /tmp/sena1.txt | wc -l`  "
     done
     printf "\n"
     let inicio=$inicio+10
     let fim=$fim+10
  done

# Para efeito de tabulação, separa-se os números de 1 e 2 algarismos
for i in `seq 9`; do printf "0$i `grep 0$i /tmp/sena1.txt | wc -l`\n" ; done >> /tmp/sena2.txt
for i in `seq 10 1 60`; do printf "$i `grep $i /tmp/sena1.txt | wc -l`\n" ; done >> /tmp/sena2.txt

# Ordena de acordo com a frequencia sorteada
awk '{printf "%3s %3s\n",$2, $1}' /tmp/sena2.txt | sort -n | tail -n 6 > /tmp/sena3.txt

printf "\n"
echo Os numeros mais sorteados sao:
echo Dezena Frequencia

# Exibe as 6 dezenas mais frequentes (do maior para o menor)
for i in `seq 6`
  do
     tail -n $i /tmp/sena3.txt | head -n 1 | awk '{printf "%4s %8s\n",$2, $1}'
  done
  
if [ -f d_megasc.htm ]
  then
    rm d_megasc.htm
fi

O resultado do script, executado até o concurso 1454 é exibido a seguir:

1454 sorteios
Exibindo a frequencia dos numeros sorteado da Mega Sena
01 135  02 143  03 144  04 159  05 177  06 137  07 146  08 150  09 126  10 151 
11 136  12 153  13 155  14 134  15 133  16 154  17 157  18 138  19 141  20 140  
21 127  22 123  23 150  24 158  25 137  26 117  27 145  28 152  29 158  30 149  
31 144  32 152  33 163  34 146  35 136  36 148  37 147  38 141  39 134  40 141  
41 155  42 156  43 160  44 146  45 130  46 130  47 147  48 132  49 156  50 155  
51 161  52 156  53 160  54 162  55 132  56 143  57 140  58 140  59 146  60 140  

Os numeros mais sorteados sao:
Dezena Frequencia
  05      177
  33      163
  54      162
  51      161
  53      160
  43      160

real 1m1.930s

quarta-feira, 28 de novembro de 2012

Geração de combinações entre n elementos

comb3.h

#ifndef COMB3_H
#define COMB3_H

class comb3 {
public:
    comb3();
    comb3(const comb3& orig);
    virtual ~comb3();
    void elementos3();
private:

};

#endif /* COMB3_H */

comb3.cpp

#include 
using std::cout;
using std::endl;
#include 
#include "comb3.h"

comb3::comb3() {
}

comb3::comb3(const comb3& orig) {
}

comb3::~comb3() {
}

void comb3::elementos3()
{
  int p1=1, p2=1, p3=1;
  int total, totalc=0;

  total=pow(3,3);

  for (int i = 1; i <= total; i++)
  {
        if (p1 != p2 && p1 != p3 && p2 != p3)
        {
           totalc++;
           cout << p1 << " " << p2 << " " << p3 << endl;
        }

        if (p3 < 3)
        {
            p3++;
        }
        else
        {
           p2++;
           p3 = 1;
        }

        if (p2 == 4)
        {
           p2 = 1;
           p1++;
        }
  }
  cout << totalc << " combinações" << endl;    
}

comb4.h

#ifndef COMB4_H
#define COMB4_H

class comb4 {
public:
    comb4();
    comb4(const comb4& orig);
    virtual ~comb4();
    void elementos4();
private:

};

#endif /* COMB4_H */

comb4.cpp

#include 
using std::cout;
using std::endl;
#include 
#include "comb4.h"

comb4::comb4() {
}

comb4::comb4(const comb4& orig) {
}

comb4::~comb4() {
}

void comb4::elementos4()
{
    int p1 =1, p2 = 1, p3 = 1, p4 = 1;
    int total = pow(4,4);
    int totalc = 0;

    for (int i = 1; i <= total; i++) {
            if (p1 != p2 && p1 != p3 && p1 != p4 && p2 != p3  && p2 != p4 && p3 != p4) 
            {
               totalc++;
               cout << p1 << " " << p2 << " " << p3 << " " << p4 << endl;
            }

            if (p4 < 4) 
            {
               p4++;
            }
            else
            {
               p3++;
               p4 = 1;
            }

            if (p3 == 5) 
            {
               p3 = 1;
               p2++;
            }

            if (p2 == 5) 
            {
               p2 = 1;
               p1++;
            }
    }
    cout << totalc << " combinações" << endl;    
}

comb5.h

#ifndef COMB5_H
#define COMB5_H

class comb5 {
public:
    comb5();
    comb5(const comb5& orig);
    virtual ~comb5();
    void elementos5();
private:

};
#endif /* COMB5_H */

comb5.cpp

#include 
using std::cout;
using std::endl;
#include 
#include "comb5.h"

comb5::comb5() {
}

comb5::comb5(const comb5& orig) {
}

comb5::~comb5() {
}

void comb5::elementos5()
{
   int p1 = 1, p2 = 1, p3 = 1, p4 = 1, p5 = 1;
   int total = pow(5,5);
   int totalc = 0;

   for (int i = 1; i <= total; i++) 
   {
      if (p1 != p2 && p1 != p3 && p1 != p4 && p1 != p5 && p2 != p3 && p2 != p4 && 
p2 != p5 && p3 != p4 && p3 != p5 && p4 != p5) 
      {
         totalc++;
         cout << p1 << " " << p2 << " " << p3 << " " << p4 << " " << p5 << endl;
      }

      if (p5 < 5) 
      {
         p5++;
      }
      else
      {
        p4++;
        p5 = 1;
      }

      if (p4 == 6) 
      {
        p4 = 1;
        p3++;
      }

      if (p3 == 6) 
      {
        p3 = 1;
        p2++;
      }

      if (p2 == 6) 
      {
        p2 = 1;
        p1++;
      }
      
  }
  cout << totalc << " combinações" << endl;     
}

comb6.h

#ifndef COMB6_H
#define COMB6_H

class comb6 {
public:
    comb6();
    comb6(const comb6& orig);
    virtual ~comb6();
    void elementos6();
private:

};

#endif /* COMB6_H */

comb6.cpp

#include 
using std::cout;
using std::endl;
#include 
#include "comb6.h"

comb6::comb6() {
}

comb6::comb6(const comb6& orig) {
}

comb6::~comb6() {
}

void comb6::elementos6()
{
    int p1 = 1, p2 = 1, p3 = 1, p4 = 1, p5 = 1, p6 = 1;
    int total = pow(6, 6);
    int totalc = 0;

    for (int i = 1; i <= total; i++) 
    {
        if (p1 != p2 && p1 != p3 && p1 != p4 && p1 != p5 && p1 != p6 && p2 != p3 &&
 p2 != p4 && p2 != p5 && p2 != p6 && p3 != p4 && p3 != p5 && p3 != p6 && 
p4 != p5 && p4 != p6 && p5 != p6) 
        {
           totalc++;
           cout <<  p1 << " " << p2 << " " << p3 << " " << p4 << " " << p5 << " " 
<< p6 << endl;
        }

        if (p6 < 6) 
          {
            p6++;
          }
        else
        {
            p5++;
            p6 = 1;
        }

        if (p5 == 7) 
        {
           p5 = 1;
           p4++;
        }

        if (p4 == 7) 
        {
           p4 = 1;
           p3++;
        }

        if (p3 == 7) 
        {
           p3 = 1;
           p2++;
        }

        if (p2 == 7) 
        {
           p2 = 1;
           p1++;
        }                                                                               
    }
    cout << totalc << " combinações" << endl;    
}
  • Shell Script
  • PHP
  • C++
Elementos Combinações Shell Script PHP C++
3 6 0m0.016s 0m0.00069s 0m0.005s
4 24 0m0.051s 0m0.0012s 0m0.005s
5 120 0m0.471s 0m0.015s 0m0.008s
6 720 0m9.639s 0m0.25s 0m0.023s
7 5040 5m44.219s 0m4.98s 0m0.159s
8 40320 142m51.553s 1m52s 0m1.712s
9 362880 - - 0m24.090s

sábado, 22 de setembro de 2012

Da base decimal para as bases binária, hexadecimal e octal

Há muito tempo já tinha reparado que não existia uma função pronta que converte da base decimal para binária.

Eu estava enganado e um professor falou da função itoa. No entanto, para minha decepção, a função não funciona apresentando o erro abaixo:

teste.c:(.text+0x4f): undefined reference to `itoa'

Como disse um velho amigo de programação: "você não precisa saber todos os comandos, funções e etecera. Se for criativo, você contorna a situação". Então criei minha própria forma de converter de decimal para binário. Basta executar o programa na linha de comando passando como parâmetro o número na base decimal. O código em C++ contém comentários descrevendo suas etapas.

Bibliotecas necessárias:

  • iostream
  • iomanip
  • cstdlib

Para converter de decimal para binário, a primeira tarefa é determinar a quantidade de divisões. Para tal, foi preciso que primeiro cálculo fosse feito fora do while para determinar o valor do quociente. Só então o loop while começa incrementando a variável divisoes.

A cada divisão, o valor do resto é armazenado em um vetor. Assim, cada cálculo, um índice é preenchido.

Para exibir o resultado, é impresso o quociente encontrado na última divisão e o vetor de restos do maior para o menor índice.

Para o valor hexadecimal, a namespace hex converte para hexadecimal. Finalmente, para o valor octal, a namespace oct converte para octal.

Apresento o código-fonte abaixo.


// Created by Murilo Fujita <murilofujita@gmail.com> in 09/21/2012

#include <iostream>
using std::cin;
using std::cout;
using std::endl;
#include <iomanip>
using std::setprecision;
using std::setw;
#include <cstdlib>
using std::hex;
using std::oct;

int main(int argc, char** argv) {

    int pos=50; //quantidade de POSICOES do array
    int num,quo,res[pos], divisoes, numero;

    if (argc <2)
    {
      cout << "Digite um número na base decimal" << endl;
      exit(1);
    }

    num=atoi(argv[1]);

    numero=num; // num é usado para calculos; numero é usado para recuperar o input
    divisoes=0; // inicializa a quantidade de divisoes

    quo=num/2;
    res[0]=num%2; // precisa calcular o primeiro resto para o WHILE saber como comeca

    pos=1; // comeca em 1 porque o indice 0 ja foi calculado fora do loop
    while (quo>1)
    {
        quo=num/2; // quociente
        res[pos++]=num%2; // armezena os restos
        num=quo; // sacada para realizar as divisoes sucessivas
        divisoes++; //conta a quantidade de divisoes
    }
    cout << numero << " precisa de " << divisoes+1 << " bits." << endl;

    pos=divisoes;
    cout << "binário: ";
    cout << quo; // imprime o bit mais significativo: o quociente da ultima divisao
    for (int j=divisoes; j>0; j--)
    {
       cout << res[pos--]; // imprime os restos do ultimo indice para o primeiro
    }

    cout << endl;
    cout << "hexadecimal: " << hex << numero << endl;
    cout << "octal: " <<  oct << numero << endl;
    return 0;
}

E para concluir, dois exemplos da resposta do programa:

./bases_numericas.out 51966
51966 precisa de 16 bits.
binário: 1100101011111110
hexadecimal: cafe
octal: 145376

./bases_numericas.out 987654321
987654321 precisa de 30 bits.
binário: 111010110111100110100010110001
hexadecimal: 3ade68b1
octal: 7267464261

quinta-feira, 6 de setembro de 2012

Script para organizar a conjugação de verbos

Procurando ajudar uma colega taiwanesa que está se adaptando à nossa língua-pátria, eu respondo os e-mails mostrando a conjugação dos verbos para que ela entenda a concondância do pronome com a flexão do verbo.

Antes é necessário instalar o pacote brazilian-conjugate. O aplicativo apt-get instala o pacote através da linha de comando:

$ apt-get install brazilian-conjugate

Uma vez instalado, para chamar o aplicativo, digite:

$ conjugue

A figura abaixo mostra a tela assim que o programa inicia e em destaque vemos o comando que executa o conjugue bem como o verbo escolhido para ser conjugado (extorquir).


Figura 1. Tela Inicial do conjugue

Esta outra figura mostra a conjugação. Perceba que o programa reconhece os verbos defectivos e omite em certos pronomes.


Figura 2. Selecionando os tempos verbais

Note que o texto selecionado será copiado para área de memória e será usado como entrada para o script a seguir.

#!/bin/bash
# Created in September/02/2012

if [ "$#" -eq 0 ]
  then
    echo Falta o parâmetro dos verbos
  else
    conj1=`echo $1 | tr ":" " " | cut -f1 -d" "`
    conj3=`echo $1 | tr ":" " " | cut -f3 -d" "`
    conj4=`echo $1 | tr ":" " " | cut -f4 -d" "`
    conj6=`echo $1 | tr ":" " " | cut -f6 -d" "`

    echo eu $conj1
    echo ele/você $conj3
    echo nós $conj4
    echo eles/vocês $conj6
fi

Primeiro o script verifica se recebeu uma entrada ao ser chamado na linha de comando. Caso sim, as variáveis conj1, conj2, conj4 e conj6 associarão às flexões dos pronomes eu, ele, nós, eles respectivamente. Em seguida o echo imprime o pronome juntamente com o verbo flexionado. Como exemplo, vamos ver a saída do verbo extorquir no presente do indicativo:

./conjugador.sh :extorques:extorque:extorquimos:extorquis:extorquem
eu
ele/você extorque
nós extorquimos
eles/vocês extorquem

Repare que o script separa as flexões identificando o sinal de dois pontos (:). Assim, foram recebidos 5 parâmetros e reconhecido que não existe a primeira flexão.

Conclusão: Além de agilizar na orientação de como escrever corretamente, pode ser uma ferramenta de grande utilidade para evitar deslizes na língua portuguesa.

domingo, 5 de agosto de 2012

Planeje o futuro através das finanças

Apresento uma aplicação interessante na área de finanças e quem sabe pode despertar o seu perfil poupador. Trata-se da quantia que você pode juntar fazendo depósitos regulares sob uma taxa de juros contante durante um determinado número de períodos.

Após a exibição dos conteúdos de cada arquivo, veja uma demonstração com base nos dados compatíveis com os índices atuais da economia brasileira (agosto/2012). Por exemplo:

  1. Valor do depósito a cada mês: R$ 100,00
  2. Taxa de juros (valor percentual): 0,5% de acordo com este link.
  3. Período: 240 meses = 20 anos

Como sempre nos códigos-fontes de C++, divide-se em 3 arquivos:

  1. Definição da classe
  2. Definições das funções-membros da classe
  3. Programa driver

1. Definição da classe

$ cat rendimento.h

#ifndef RENDIMENTO_H
#define RENDIMENTO_H

class rendimento {
public:
    rendimento();
    rendimento(const rendimento& orig);
    virtual ~rendimento();
    void calculo(int, float, float);
private:

};

#endif /* RENDIMENTO_H */

2. Definições das funções-membros da classe

$ cat rendimento.cpp

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <cmath>
#include <iomanip>
using std::setw;
using std::fixed;
using std::setprecision;
#include "rendimento.h"

rendimento::rendimento() {
}

rendimento::rendimento(const rendimento& orig) {
}

rendimento::~rendimento() {
}

void rendimento::calculo(int periodo, float juros, float depositoRegular) {
    
    float montante;  
    
    for (int i=1; i<=periodo; i++)
    {
        montante=(1+juros)* (pow(1+juros,i)-1)*depositoRegular/juros;        
        cout << setw(3) << i << " " << fixed << setprecision(2) << setw(10) <<  montante << "   ";
        if (i%4==0)
            cout << endl;
    }
}

3. Programa driver

$ cat main.cpp

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include "rendimento.h"

int main(int argc, char** argv) {
    
    int tempo;
    float taxa, valorInicial, depositoRegular;
    
    cout << "Digite o valor depositado regular: ";
    cin >> depositoRegular;
    cout << "Digite a taxa de juros (em porcento): ";
    cin >> taxa;
    cout << "Digite o período de rendimentos: ";
    cin >> tempo;
    
    taxa/=100.0;
    
    rendimento mycalculo;
    mycalculo.calculo(tempo, taxa, depositoRegular);

    return 0;
}

Demonstração do resultado:

./caderneta_poupanca 
Digite o valor depositado regular: 100 
Digite a taxa de juros (em porcento): 0.5
Digite o período de rendimentos: 240
  1     100.50     2     201.50     3     303.01     4     405.03   
  5     507.55     6     610.59     7     714.14     8     818.21   
  9     922.80    10    1027.92    11    1133.56    12    1239.72   
 13    1346.42    14    1453.65    15    1561.42    16    1669.73   
 17    1778.58    18    1887.97    19    1997.91    20    2108.40   
 21    2219.44    22    2331.04    23    2443.20    24    2555.91   
 25    2669.19    26    2783.04    27    2897.45    28    3012.44   
 29    3128.00    30    3244.14    31    3360.86    32    3478.17   
 33    3596.06    34    3714.54    35    3833.61    36    3953.28   
 37    4073.54    38    4194.41    39    4315.88    40    4437.96   
 41    4560.65    42    4683.96    43    4807.88    44    4932.42   
 45    5057.58    46    5183.37    47    5309.78    48    5436.83   
 49    5564.52    50    5692.84    51    5821.80    52    5951.41   
 53    6081.67    54    6212.58    55    6344.14    56    6476.36   
 57    6609.24    58    6742.79    59    6877.00    60    7011.89   
 61    7147.45    62    7283.68    63    7420.60    64    7558.21   
 65    7696.50    66    7835.48    67    7975.16    68    8115.53   
 69    8256.61    70    8398.39    71    8540.89    72    8684.09   
 73    8828.01    74    8972.65    75    9118.01    76    9264.10   
 77    9410.92    78    9558.48    79    9706.77    80    9855.80   
 81   10005.58    82   10156.11    83   10307.39    84   10459.43   
 85   10612.23    86   10765.79    87   10920.12    88   11075.22   
 89   11231.09    90   11387.75    91   11545.19    92   11703.41   
 93   11862.43    94   12022.24    95   12182.85    96   12344.27   
 97   12506.49    98   12669.52    99   12833.37   100   12998.04   
101   13163.53   102   13329.84   103   13496.99   104   13664.98   
105   13833.80   106   14003.47   107   14173.99   108   14345.36   
109   14517.59   110   14690.67   111   14864.63   112   15039.45   
113   15215.15   114   15391.72   115   15569.18   116   15747.53   
117   15926.77   118   16106.90   119   16287.93   120   16469.88   
121   16652.72   122   16836.49   123   17021.17   124   17206.78   
125   17393.31   126   17580.78   127   17769.18   128   17958.53   
129   18148.82   130   18340.06   131   18532.26   132   18725.42   
133   18919.55   134   19114.65   135   19310.72   136   19507.78   
137   19705.81   138   19904.84   139   20104.87   140   20305.89   
141   20507.92   142   20710.96   143   20915.02   144   21120.09   
145   21326.19   146   21533.32   147   21741.49   148   21950.70   
149   22160.95   150   22372.26   151   22584.62   152   22798.04   
153   23012.53   154   23228.09   155   23444.73   156   23662.46   
157   23881.27   158   24101.18   159   24322.18   160   24544.29   
161   24767.51   162   24991.85   163   25217.31   164   25443.90   
165   25671.62   166   25900.47   167   26130.48   168   26361.63   
169   26593.94   170   26827.41   171   27062.04   172   27297.85   
173   27534.84   174   27773.02   175   28012.38   176   28252.94   
177   28494.71   178   28737.68   179   28981.87   180   29227.28   
181   29473.92   182   29721.79   183   29970.89   184   30221.25   
185   30472.86   186   30725.72   187   30979.85   188   31235.25   
189   31491.92   190   31749.88   191   32009.13   192   32269.68   
193   32531.53   194   32794.68   195   33059.16   196   33324.95   
197   33592.08   198   33860.54   199   34130.34   200   34401.49   
201   34674.00   202   34947.87   203   35223.11   204   35499.73   
205   35777.73   206   36057.11   207   36337.90   208   36620.09   
209   36903.69   210   37188.71   211   37475.15   212   37763.03   
213   38052.34   214   38343.10   215   38635.32   216   38929.00   
217   39224.14   218   39520.76   219   39818.86   220   40118.46   
221   40419.55   222   40722.15   223   41026.26   224   41331.89   
225   41639.05   226   41947.75   227   42257.98   228   42569.77   
229   42883.12   230   43198.04   231   43514.53   232   43832.60   
233   44152.27   234   44473.53   235   44796.39   236   45120.88   
237   45446.98   238   45774.71   239   46104.09   240   46435.11   

Considerações finais:

Os códigos-fontes foram escritos através da IDE Netbeans.

Caso prefira colar os respectivos conteúdo nos três arquivos com os nomes sugeridos, a linha de comando é:

$ g++ rendimento.cpp main.cpp -o caderneta_poupanca.out -ansi

O cálculo pode ser conferido através do BCB - Calculadora do cidadão.

Planeje seu futuro! Este programa pode auxiliar como lidar com o seu investimento.

quarta-feira, 18 de julho de 2012

Algoritmo do CPF

Achei interessante o algoritmo dos dígitos verificadores do CPF (Cadastro de Pessoa Física) e apresento o código-fonte em C++.

Para gerar o binário é necessário 3 arquivos:

  1. Definição da classe
  2. Definições das funções-membros da classe
  3. Programa driver

1. Definição da classe

$ cat Cpfnum.h

#ifndef CPFNUM_H 
#define CPFNUM_H 

class Cpfnum { 
public: 
    Cpfnum(); 
    Cpfnum(const Cpfnum& orig); 
    virtual ~Cpfnum();
    int PrimeiroDigito(int, int, int, int, int ,int ,int, int, int);
    int SegundoDigito (int, int, int, int, int ,int ,int, int, int, int);
private:


#endif /* CPFNUM_H */

2. Definições das funções-membros da classe

$ cat Cpfnum.cpp

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <cstring>
using std::string;

#include "Cpfnum.h"

Cpfnum::Cpfnum() {
}

Cpfnum::Cpfnum(const Cpfnum& orig) {
}

Cpfnum::~Cpfnum() {
}

int Cpfnum::PrimeiroDigito(int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,int v9)
{
  int m1, m2, m3, m4, m5, m6, m7, m8, m9; // multiplicado    
  int soma, resto, v10; 
  
  m1=v1*10;
  m2=v2*9;
  m3=v3*8;
  m4=v4*7;
  m5=v5*6;
  m6=v6*5;
  m7=v7*4;
  m8=v8*3;
  m9=v9*2;
  
// somando m1 a m9
  soma=m1+m2+m3+m4+m5+m6+m7+m8+m9;

// obtendo o 1° dígito verificador: divisão da soma por 11 e o resto é o 1° dígito verificador
    resto=soma%11;
    if (resto < 2)
      v10=0;
    else
      v10=11-resto;
    
    return v10;
}

int Cpfnum::SegundoDigito (int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,int v9, int v10)
{
  int m1, m2, m3, m4, m5, m6, m7, m8, m9, m10; // multiplicado
  int soma, resto, v11;

// novos valores multiplicados após encontrar o 1° dígito verificador
   m1=v1*11;
   m2=v2*10;
   m3=v3*9;
   m4=v4*8;
   m5=v5*7;
   m6=v6*6;
   m7=v7*5;
   m8=v8*4;
   m9=v9*3;
   m10=v10*2;

// somando m1 a m10
    soma=m1+m2+m3+m4+m5+m6+m7+m8+m9+m10;

// divisão da soma por 11. Aplica-se regra do resto para encontrar o 2° dígito verificador
    resto=soma%11;
    if (resto < 2)
      v11=0;
    else
      v11=11-resto;

    return v11;
}

3. Programa driver

$ cat main.cpp

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <cstring>
using std::string;
#include <sstream>
using std::stringstream;
#include <cstdlib>

#include "Cpfnum.h"

//using namespace std;

int main(int argc, char** argv) {
    
  string mystring;
  string sv1, sv2, sv3, sv4, sv5, sv6, sv7, sv8, sv9, sv10, sv11; // sv10 = 1° dígito verificador e sv11 = 2° dígito verificador
  int v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11;  // valor
  int pdv, sdv; // primeiro e segundo digito verificador

  cout << "Entre com o número do CPF: ";
  cin >> mystring;

  sv1=mystring[0];
  sv2=mystring[1];
  sv3=mystring[2];
  sv4=mystring[3];
  sv5=mystring[4];
  sv6=mystring[5];
  sv7=mystring[6];
  sv8=mystring[7];
  sv9=mystring[8];

  stringstream(sv1) >> v1;
  stringstream(sv2) >> v2;
  stringstream(sv3) >> v3;
  stringstream(sv4) >> v4;
  stringstream(sv5) >> v5;
  stringstream(sv6) >> v6;
  stringstream(sv7) >> v7;
  stringstream(sv8) >> v8;
  stringstream(sv9) >> v9;

  if (mystring.length() < 9 || mystring.length() == 10 || mystring.length() > 11)
    cout << "Você digitou " << mystring.length() << " caracteres. Digite 9 ou 11." << endl;

  Cpfnum myCpfnum;
  myCpfnum.PrimeiroDigito(v1, v2, v3, v4, v5, v6, v7, v8, v9);
  myCpfnum.SegundoDigito(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10);
  
  if (mystring.length() == 9)
  {
    v10=myCpfnum.PrimeiroDigito (v1, v2, v3, v4, v5, v6, v7, v8, v9);
    v11=myCpfnum.SegundoDigito (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10);
    cout << v1 << v2 << v3 << v4 << v5 << v6 << v7 << v8 << v9 << "-" << v10 << v11 << endl;
  }
  
  if (mystring.length() == 11)
  {
    pdv=myCpfnum.PrimeiroDigito (v1, v2, v3, v4, v5, v6, v7, v8, v9);
    sdv=myCpfnum.SegundoDigito (v1, v2, v3, v4, v5, v6, v7, v8, v9, pdv);

    sv10=mystring[9];
    sv11=mystring[10];
    stringstream(sv10) >> v10;  
    stringstream(sv11) >> v11;

    if ( (v10 == pdv ) && (v11 == sdv ) )
      cout << "CPF válido!" << endl;
    else
      cout << "CPF inválido" << endl;
  }
  
  return 0;
}

Considerações finais:

Uma IDE (Integrated Development Environment) faz muita diferença no desenvolvimento. Meus primeiros programas foram elaborados com o Anjuta, mas passei a usar o NetBeans.

O Anjuta cria as classes sem muitas instruções (virtual) o que considero bom para começar o contato com C++. No entanto, nada impede de iniciar com NetBeans e ignorar algumas funções-membros.

As IDEs vem com as configurações que buscam o compilador para gerar o binário. Caso prefira colar os respectivos conteúdo nos três arquivos com os nomes sugeridos, a linha de comando é:

$ g++ Cpfnum.cpp main.cpp -o cpf.out -ansi

E para escrever este texto usei o BlueFish. Claro que não deixei de usar o VIM, principalmente porque um recurso essencial como colar em colunas só encontro nele.

sábado, 16 de junho de 2012

Simplificação do código para ser otimizado


Um problema de matemática chamou-me a atenção e encontrei a solução usando a soma de uma P.A. O enunciado é este:


Seja
f(n)=n se n<10
f(n)=multiplicação dos algarismos se n>=10
Então f(1)+f(2)+f(3)+...+f(100) é igual a?

Decidi fazer um programa e o principal deste texto não é o programa que resolve este problema. É como o conhecimento pode simplificar o processamento e resolver em menos tempo além de comparar o desempenho de duas linguagens de programação.

Primeiro foi feito em shell script. No bloco for i in `seq 10 1 99` existem duas variáveis que armazenam os algarismos do número. O comando cut -c 1 associa a dezena à variável valor1 e cut -c 2 associa a unidade à variável valor2. A variável multiplicado armazena o produto enquanto que soma acumula os resultados de multiplicado.


#!/bin/bash 

for i in `seq 9`
   do 
      let soma1=$soma1+$i  
   done  
 
for i in `seq 10 1 99`  
   do  
      valor1=`echo $i | cut -c 1`  
      valor2=`echo $i | cut -c 2` 
      multiplicado=`echo "$valor1 * $valor2" | bc`  
      let soma2=$soma2+$multiplicado  
   done 
  
let soma=$soma1+$soma2 echo Resultado $soma

Executando o script junto com o comando time tenho a resposta que levou para efetuar os cálculos:

 

 
Resultado 2070

real 0m0.704s
user 0m0.036s
sys 0m0.028s

Encontrada a solução em uma linguagem, quis testar em C++. No entanto como não consegui separar as dezenas e unidades em duas variáveis, encontrei uma solução mais simples. E para que a comparação seja válida, é preciso que os programas executem as mesmas rotinas.

Usando dois laços for separam-se as dezenas e unidades. A otimização em shell script é apresentada abaixo:

   
#!/bin/bash  

for i in `seq 9`
   do  
      let soma1=$soma1+$i  
   done  

for dezena in `seq 9`  
  do  
    for unidade in `seq 9`  
      do  
        resultado=$dezena*$unidade  
        let soma2=$soma2+$resultado 
      done  
  done  

let soma=$soma1+$soma2  
echo Resultado $soma  


Resultado 2070

real 0m0.041s
user 0m0.004s
sys 0m0.012s

Por fim, testa-se em C++.

Como foi dito, elaborei a lógica deste código antes do segundo shell script. As explicações são as mesmas do código anterior.

 
# include <iostream>  
using namespace std;  
int main()  
{  
   int dezena, unidade;  
   int soma1=0, soma2=0;
  
   for (int i=1; i<10; i++)  
      soma1+=i;  

   for (dezena=1; dezena<10; dezena++) 
   {  
      for (unidade=1; unidade<10; unidade++) 
      { 
        soma2+=dezena*unidade; 
      } 
   } 
 
   cout << "Resultado " << soma1+soma2; 
   cout << endl; 
 
   return 0; 
}  


Resultado 2070

real 0m0.004s
user 0m0.000s
sys 0m0.000s


Resumo dos tempos:

Shell Script Shell Script (com otimização) C++
0.704s 0.041s 0.004s

Conclusão: cada linguagem tem a sua particularidade. Para desenvolver algo rápido considero que o shell script é uma ferramenta que atende muito bem. Por outro lado, se for preciso alto desempenho, o C++ é insuperável*. Adicionalmente, fica claro que o resultado do que é compilado é mais rápido do que interpretado.
* De acordo com o professor doutor José Luis Zem da disciplina de Arquitetura e Organização de Computadores, a linguagem Fortran tem desempenho superior além de maior capacidade de ponto flutuante.

Agradeço ao professor Zem por explicar a diferença de desempenho entre compilado e interpretado. No caso do interpretado há um outro programa analisando cada linha do script e transformando em instruções de computador. No caso dos compilados, o compilador usa regras de otimização para gerar o binário.

domingo, 13 de maio de 2012

Desligar computador se o download não terminar

  No código-fonte desta publicação tive que adaptar o código que está em Desligando o micro ao terminar o download.
  Sempre funcionou bem, mas foi preciso pensar numa situação de contorno quando a conexão caia ou quando o roteador wireless dava seus tilts. Neste caso, o download não se completa e a condição test -s "$inteiro" não passa para verdadeiro.
  Foi pensado da seguinte forma: comparo a quantidade de bytes entre 2 segundos consecutivos.
  Se não forem iguais a variável contagem sofre um reset voltando a valer 1 (um). Se forem iguais a variável contagem é incrementada de 1 a cada 1 segundo. Se a contagem chegar a 300 (5 minutos), é executado o comando para desligar.
  Alguns downloads não admitem continuar de onde pararam e talvez seja melhor desligar o computador tão logo que se perceba que o download não será concluído.


#!/bin/bash
# v1.0: 10/21/2006
# v1.1: 04/11/2012
# v1.2: 05/12/2012

parte=""
inteiro=""

contagem=0
until test -s "$inteiro"
  do
   status_antes=`du "$parte" | awk '{printf "%2s\n",$1}'`
   sleep 1
   status_depois=`du "$parte" | awk '{printf "%2s\n",$1}'`

   if [ "$status_antes" -ne "$status_depois" ]
    then
     contagem=1
   else
    let contagem=$contagem+1
     if [ "$contagem" -eq 300 ]
      then
       /sbin/shutdown -h now
     fi
   fi

   echo Download em andamento: $status_depois kB $contagem
   sleep 2
   continue
  done

test -s "$inteiro"
  /sbin/shutdown -h now