Página 2 de 3

Enviado: 08 Mar 2008 08:34
por MARCELOG
Que tal,

IF AT(opc,aMenu1) > 0
ALERT('Tá dentro')
ELSE
...
ENDIF

ou (em xHarbour)

IF opc IN aMenu1
ALERT('Tá dentro')
ELSE
...
ENDIF

ou

IF ASCAN(aMenu1,opc) > 0
ALERT('Tá dentro')
ELSE
...
ENDIF

E aí vai...

MarceloG

Enviado: 08 Mar 2008 09:35
por Pablo César
Bom dia Netavin,

"Imagine que uma MATRIZ seja uma TABELA assim como um DBF".

Uma matriz UNIDIMENSIONAL, isto é tem um só SEGMENTO, assim como você fez para criar uma:

Código: Selecionar todos

aMenu1:={"PR","AC","BA","PI","SC"}
Veja como varrer:

Código: Selecionar todos

FOR I=1 TO LEN(MATRIZ)  // para varrer a matriz toda em MATRIZ UNIDIMENSIONAL. 
    ? MATRIZ[I]
NEXT
Matriz multidimensional quando possue várias MATRIZ aninhadas, por exemplo:

Código: Selecionar todos

MATRIZ:={ {"PR","AC","BA","PI","SC"}, {"Paranaguá","Curitiba","Londrina"}, {"Rio Branco","Xapurí"}, {"Salvador","Alagoinhas"}, {"Alegre do Piauí","Bom Jesus"}, {"Florianópolis", "Bombinhas","Camboriú"} }

FOR I=1 TO LEN(MATRIZ) // para varrer a matriz toda em MATRIZ MULTIDIMENSIONAL. 
    FOR U=1 TO LEN(MATRIZ[I])
        ? MATRIZ[I,U]
    NEXT
NEXT
Agora vejamos o seu código:
Netavin escreveu:

Código: Selecionar todos

aMenu1:={"PR","AC","BA","PI","SC"}
opc:=Space(2)
@ 10,01 Say "Informe a UF: " get opc Pict "!!"
Read
Eu neste caso, faria diferente, colocaria um ACHOICE, onde eu possa fazer um menu e o usuário iria escolher o item conforme você desgnou para aMenu1. Exemplo simples (sem tratamento algum):

Código: Selecionar todos

opc:="  "
aMenu1:={"PR","AC","BA","PI","SC"}
@ 10,01 Say "Informe a UF:" get opc Pict "!!" WHEN MENU1() // O when força a entrar antes de edição do GET
READ

FUNCTION MENU1()
VTELA:=SAVESCREEN(10,00,14,20)
OP:=ACHOICE(10,15,14,17,aMenu1) // achoice simples sem função de controle
IF LASTKEY()=27  // Se digitado ESC, cai fora
   RESTSCREEN(10,00,14,20,VTELA)
   RETURN .F.
ELSE
   EST:=aMenu1[OP] // Atribuir  o conteudo selecionado a variavel EST
   KEYBOARD EST // o uso do keyboard ir  simular como se tivesse digitado o conteudo da var EST
ENDIF
RESTSCREEN(10,00,14,20,VTELA)
RETURN .T.
Ou então, validar o que o usuário digitar (acho que era essa sua intenção). Veja como ficaria:

Código: Selecionar todos

opc:="  "
aMenu1:={"PR","AC","BA","PI","SC"}
@ 10,01 Say "Informe a UF:" get opc Pict "!!" VALID ESTADOS(opc)
READ

FUNCTION ESTADOS(opc)
ACHOU:=ASCAN(aMenu1,opc)
IF ACHOU=0
   @ 24,00 SAY PADC("Sigla UF, incorreta. Tente novamente.",80)
   VRET:=.F.
ELSE
   VRET:=.T.
ENDIF
RETURN VRET
Espero ter esclarecido suas dúvidas. Mas pode postar suas perguntas, o que importa é que você entenda bem. Não é bicho de sete cabeça, não. Eu também tive dificuldades no começo, não se encabule, peque o boi pelos chifres que você irá domar-lo !.

Enviado: 08 Mar 2008 09:48
por Maligno
Netavin escreveu:... caro frazato.
É de uso cotidiano numa Madeireira, serraria, depósito de madeira etc...
Usa-se para calcular/cubicar a madeira.
Como curiosidade, um adendo para os que ainda não conhecem o ramo: no ramo de transporte sempre se calcula o frete por peso. Mas há certos tipos de cargas que tomam muito espaço e não pesam tanto quanto a transportadora gostaria. É mais volume. Imagine uma carreta carregada de isopor. Se fosse apenas o peso do isopor o frete custaria uma merreca e não pagaria nem o combustível. Então inventaram um fator de conversão com base no volume (metros cúbicos) e nasceu o termo cubagem. Portanto, se a carga é mais volume que peso, aplicam a cubagem, convertendo o volume em peso.

Já fiz programa de emissão de conhecimento de transporte. :)

Enviado: 08 Mar 2008 09:56
por Maligno
Netavin escreveu:Private aMenu1
aMenu1:={"PR","AC","BA","PI","SC"}
opc:=Space(2)
@ 10,01 Say "Informe a UF: " get opc Pict "!!"
Read

For i:= 1 to len(aMenu1)
Ascan(aMenu1,opc)
Next
O código está certo a não ser por alguns pequenos detalhes: a função ASCan() sempre retorna um valor em resposta à pesquisa. Se retornar zero, significa que a pesquisa resultou falso. Se retornar um valor maior que zero, sucesso; e o valor é o número de ordem do elemento encontrado. Como AScan() já faz o serviço, você não precisa dessa malha. Basta usar n := AScan(aMenu1,opc), onde n é a variável que armazenará o resultado.

Só um registro: eu sei que isso é apenas um código de teste, para aprender a usar matrizes. Mas se fosse essa a tarefa a ser executada, eu sugeriria um simples opc $ "PR.AC.BA.PI.SC" para saber se a sigla digitada é válida. Resolvi adicionar essa informação pois se um novato vê esse exemplo, pode achar que esta é a melhor forma de procurar uma sigla de Estado. :)

Enviado: 08 Mar 2008 10:11
por Maligno
Pablo César escreveu:

Código: Selecionar todos

FUNCTION ESTADOS(opc)
ACHOU:=ASCAN(aMenu1,opc)
IF ACHOU=0
   @ 24,00 SAY PADC("Sigla UF, incorreta. Tente novamente.",80)
   VRET:=.F.
ELSE
   VRET:=.T.
ENDIF
RETURN VRET
Me lembro que certa vez um amigo, também programador Clipper, me consultou a respeito de um erro muito estranho. A função era mais ou menos como essa: simples e pequena. Mas dava um erro muito esquisito. Não lembro exatamente qual era. Mas era comentar o código dessa pequena função e o erro sumia. O problema: assim como nessa pequena função, ele usava uma variável private que, coincidentemente, tinha o mesmo nome de uma outra variável em uma função que era executada antes dessa. Ou seja, a variável era alterada fora de sua função de origem. Seria como essa VRET que aqui aparece como private. Uma função anterior à ela teria o mesmo nome e um valor importante. Alterada aqui, o valor anterior, claro, era perdido e a confusão começava. Solução: tornar essa VRET local:

Código: Selecionar todos

function PesqUF(cUF,aUF)
local lRet := AScan(aUF,cUF) > 0
if lRet
   // Ok
else
   @ 24,00 SAY PadC("Sigla UF incorreta.",80)
end
return lRet
Esse é, aliás, um dos piores defeitos do Clipper: fraca tipagem. Refiz a função para não preicar ter a matriz como private. Ela deveria ser passada como parâmetro da função.

Em tempo: em todos os seus exemplos de manipulação de matrizes multidimensionais eu usaria AEval(), que é uma solução mais rápida.

Enviado: 08 Mar 2008 10:12
por Pablo César
Maligno escreveu:eu sugeriria um simples opc $ "PR.AC.BA.PI.SC" para saber se a sigla digitada é válida. Resolvi adicionar essa informação pois se um novato vê esse exemplo, pode achar que esta é a melhor forma de procurar uma sigla de Estado. :)
Claro, é bom exemplificar outras possibilidades, se bem que precisaria elaborar melhor o VALID opc $ "PR.AC.BA.PI.SC", pois desta forma iria validar erroneamente aceitando até o ponto, por exemplo: "R." então elaborando mais ainda poderia ficar assim:

Código: Selecionar todos

opc:="  "
@ 10,01 Say "Informe a UF:" get opc Pict "!!" VALID opc="PR" .OR. opc="AC" .OR.;
                                                    opc="BA" .OR. opc="PI" .OR.;
                                                    opc="SC"
READ
Eu acabei exemplificando anteriormente com MATRIZES, porque achei que haveria alguma necessidade de utilizá-las. Mas é como você disse Maligno, tudo isto é para puro aprendizado e os exemplos mais mixtos sempre são bem vindos.

Enviado: 08 Mar 2008 10:16
por Maligno
Pablo César escreveu:Claro, é bom exemplificar outras possibilidades, se bem que precisaria elaborar melhor o VALID opc $ "PR.AC.BA.PI.SC", pois desta forma iria validar erroneamente aceitando até o ponto, por exemplo: "R."
O costume, nesse tipo de verificação, não é limitar a string de referência, mas a entrada do dado a comparar. Se você forçar para que sejam digitadas apenas letras, o que é fácil de fazer, o código fica mais correto e, claro, muito mais fácil de testar do que usar um punhado de testes lógicos.

Enviado: 08 Mar 2008 10:21
por Pablo César
Maligno escreveu:Ou seja, a variável era alterada fora de sua função de origem. Seria como essa VRET que aqui aparece como private. Uma função anterior à ela teria o mesmo nome e um valor importante. Alterada aqui, o valor anterior, claro, era perdido e a confusão começava. Solução: tornar essa VRET local:
Concordo. Eu sempre utilizo a variável VRET, mas tenho o cuidado de não aninhar funções com a mesma nomenclatura de varia´vel para não ocorrer oque você mencionou. No entanto, eu quiz exemplificar da forma mais simples possível para não extender e não desviar a atenção de que realmente está precisando entender essa questão de MATRIZES. O exemplo poderia ter sido:

Código: Selecionar todos

FUNCTION ESTADOS(opc) 
IF ASCAN(aMenu1,opc)=0 
   @ 24,00 SAY PADC("Sigla UF, incorreta. Tente novamente.",80) 
   RETURN .F. 
ELSE 
   RETURN .T. 
ENDIF


Mas foi bom ter mencionado, porque alguém pode simplesmente copiar e não ver a coisa toda composta de funções aninhadas e utilizando sempre a mesma variável. Bem lembrado.

Enviado: 08 Mar 2008 10:27
por Pablo César
Acho que se fóssemos nos aprofundizar, iremos desviar atenção do que foi ponderado.
Maligno escreveu:Se você forçar para que sejam digitadas apenas letras, o que é fácil de fazer, o código fica mais correto e, claro, muito mais fácil
de testar do que usar um punhado de testes lógicos.
E cómo você faria Maligno, para limitar essa máscara, lembrando que teria que aceitar somente caracter ALFA e MAIUSCULAS ? Seria muito elaborado de todas formas, não ?. Exemplifique por favor.

Enviado: 08 Mar 2008 10:38
por Pablo César
Pablo César escreveu:cómo você faria Maligno, para limitar essa máscara, lembrando que teria que aceitar somente caracter ALFA e MAIUSCULAS ?
Respondendo a mim mesmo (hihihihi). Ví que você tinha razão Maligno nessa parte da mascara no GET e decidí fazer:

Código: Selecionar todos

opc:="  "
@ 10,01 Say "Informe a UF:" get opc Pict "@!AA" VALID opc $ "PR.AC.BA.PI.SC"
READ
Incrível dizer isto: mas eu nunca usei e agora que fiz testes e conseguí fazer uma mascara composta de restrição de MAIUSCULAS e CARACTERS ALFA com o PICTURE "@!AA". Legal mais uma que aprendo !. Obrigado Maligno, as vezes nós pensamos que você é impiedoso ao criticar e no fim você está nos dizendo... tem outra forma... hihihi Thanks, good fellow !

Enviado: 08 Mar 2008 11:17
por Netavin
Wonderful !

Esses exemplos e mais as ponderações sobre este, isto ou aquilo foi de muita utilidade para mim. Quiçá para outros que estejam com a mesma dificuldade. Interessante como esta alternativa vem substituir um monte de linhas em meu sistema.
Caraca !!! vejo que tenho muito a aprender.
Muito obrigado! ... este foi mais um aprendizado.
"Tudo que sei é que nada sei."

Abraços !!! Bom final de semana.
:{

Netavin.

Enviado: 08 Mar 2008 11:32
por Pablo César
Quê bom que tudo isto tenha servido a você e aos demais. Com certeza que a utilização de vetores ou matrizes (como queira chamar) são de grandissima utilidade e da maior praticidade (reduzindo muito quantidade de codigo) e é por isso que eu sempre insisto nos exemplos e no aprendizado quando indico aos colegas.

É de muito bom prazer receber suas palavras de agradecimento e do seu reconhecimento que é bem característico de todo bom NISSEY, porque sabem valorizar o lado humilde de todo indivíduo.

Enviado: 09 Mar 2008 15:11
por Maligno
Conforme havíamos conversado no sábado a respeito de metodologias, é sempre bom lembrar que há várias formas de se fazer a mesma coisa. Nos extremos, a pior e a melhor forma. Quando melhor fizermos, tanto menor será o problema futuro com manutenção, por exemplo. Por isso sempre frizo que é de grande importância observar como escrevemos nossos códigos. A questão da sua variável VRET é um exemplo do que digo. Você disse que "toma cuidado" para não causar conflitos com nomes. Pena que somos falíveis. E às vezes a coisa "passa" e não percebemos. Como se trata de um erro sutil, por vezes é difícil detectar a origem. Foi bem o caso que comentei.

A linguagem XBase é fraca e não há o menor rigor com a tipagem. Se isso existisse, problemas desse tipo não aconteceriam. Acho que a melhor solução para isso é desenvolver e praticar boas metodologias. Talvez não consegamos desenvolver sempre o melhor, mas pelo menos não faremos o pior código.

Por anos a fio a ciência da computação foi aperfeiçoando práticas e metodologias de desenvolvimento de software, sempre mirando o máximo em nível de qualidade, tanto do desenvolvimento como nos demais processos que ocorrem ao longo da vida do software. Manutenção incluída. Muitos autodidatas pulam certas fases de aprendizado por conta do imediatismo. Com isso, acabam criando vícios e conceitos bobos, como o caso que comentamos há pouco tempo sobre sempre precisar existir um ELSE para cada IF ou que uma função só deveria ter um RETURN. Conceitos bobos e deturpados como esses só levam a perda de tempo. Não acrescentam nada e criam vícios. Se milhares de programadores em todo o mundo fazem diferente deve ser porque eles estão certos.

Claro que o exemplo postado foi feito rapidamente apenas para poder ajudar o colega em dificuldade. Além do que a idéia era fazer algo muito simples e de fácil compreensão. Mas vamos pegar esse código assim mesmo, para ilustrar alguns conceitos.

Código: Selecionar todos

FUNCTION ESTADOS(opc) 
IF ASCAN(aMenu1,opc)=0 
   @ 24,00 SAY PADC("Sigla UF, incorreta. Tente novamente.",80) 
   RETURN .F. 
ELSE 
   RETURN .T. 
ENDIF
Funciona. Mas cito duas falhas comuns de se ver. Uma função de checagem é como uma caixa-preta. Não deveria fazer outra coisa a não ser a checagem. Aqui vemos duas tarefas sendo executadas: a checagem e o alerta visual. Se amanhã ou depois houver alguma alteração na interface, o alerta visual desta, e de todas as demais funções do programa que fazem algo do tipo, deverão ser alteradas uma a uma.

A segunda falha é justamente o ELSE desnecessário. Duvido que o compilador Clipper, e mesmo o XHarbour, seja inteligente o suficiente para perceber a falha e eliminar esse ELSE automaticamente na compilação. Código desnecessário que, estando numa malha, fará o programa ficar mais lento. Uma alternativa:

Código: Selecionar todos

//--------------------------------------------
function bla...bla...bla...      // alto nível
get cUF ...
read

if !ExistUF(cUF)
   // Alerta por meio de função global única
   // ErrorWarn("UF não encontrada!")
end
return nil


//--------------------------------------------
function ExistUF(cUF)            // apoio
return cUF $ "XX,YY,ZZ,..."
Já ficou melhorzinho. Como é um exemplo simples, pode dar a impressão de não ser tão necessário. Mas à medida em que as coisas vão ficando mais complicadas, essa metodologia se mostra mais vantajosa.
Note que agora a função de pesquisa, que serve de apoio à função de alto nível, desempenha apenas uma tarefa. Pra qualquer função que a utilize existe apenas uma entrada: o valor de teste. E uma única saída: o resultado do teste. Só há uma hipótese da necessidade de alterar essa função: a criação ou extinção de um Estado. Fora isso, ela é uma caixa-preta. Ninguém precisa saber o que acontece dentro dela, mas apenas os argumentos de entrada e o valor de retorno.

A função global de alerta segue o mesmo estilo. É ela que tem vínculo com a geometria e as particularidades da interface, não a função que a utiliza. Isso se chama abstração. Se algo tiver que ser alterado na interface, você não terá que correr todo o seu código fonte para modificar função por função. Você só precisará ir a um lugar: o sub-sistema que cuida da interface.

O exemplo poderia ser mais elaborado para enfatizar melhor esses conceitos, mas eu só estava esperando uma pessoa aqui em casa e resolvi escrever um pouco a respeito do que havíamos conversado.

Não espero mudar a vida de ninguém, mas se conseguir deixar uma pulga atrás de orelha de uma única pessoa que seja, já terá valido a pena. :)

matrizes

Enviado: 09 Mar 2008 22:43
por rosalvo rosa
Bastante esclarecedor este tópico sobre matrizes. Apenas para complementar, caso alguém pesquise aqui, as MATRIZES são muito práticas também no trato com DBF´s.

Para jogarmos um campo de um registro de DBF na MATRIZ:

Func ColherDados()
aTest:= {} // cria uma matriz
Use qualquer.dbf
Go Top
While !Eof()
If condicao // filtro
AAdd(aTest,{campoDbf1,campodbf2,0,0})
// adicionar os campos do dbf para dentro da matriz
EndIf
Skip
EndDo

Depois de colhido os dados que vc filtrar, vc pode usar sua matriz e trabalhar com ela com varios comandos. Ex:

Asort(aTest, Nil, Nil, { |x, y| x[2] < y[2] })

onde vc estará ordenando a matriz = aTest[a][c][d] = pelo campo .

Depois de colhidos os dados para sua MATRIZ, sempre considerando que
vc está trabalhando com registros DBF´s :

For z:=1 To Len(aTest)
If condicao = x
aTest[z][4]:= x
// aqui a matriz recebe um valor x , ou ainda, o campo do dbf
end
Next

Para o autor do tópico:

depois de começar a lidar com MATRIZES, vc nunca mais deixará de
usá-las. sempre aparecerão situacões que elas serão muito úteis.

Caso queira mais detalhes sobre algum ex, vc sabe, só postar que os caras aqui do forum vão nos ajudar.

Enviado: 10 Mar 2008 09:52
por gvc
Private aMenu1
aMenu1:={"PR","AC","BA","PI","SC"}
opc:=Space(2)
@ 10,01 Say "Informe a UF: " get opc Pict "!!" valid ascan(aMenu1, opc) > 0
Read


isso resolve a pesquisa.