Jump to content
Baderous

Parsing de string que representa lista

Recommended Posts

Baderous

Tenho de fazer uma função que pegue numa string que representa uma lista e construa a lista, por exemplo:

"[12,23,34]" -> [12,23,34]

Até agora o que tenho é isto:

stringSInt :: String -> [int]
stringSInt l = stringSIntAC [] [] ' ' l
where stringSIntAC _ ac2 _ [] = ac2
      stringSIntAC ac1 ac2 c (x:xs) | not (isDigit x) = stringSIntAC ac1 ac2 x xs
			            | otherwise = if c == '[' then stringSIntAC (ac1 ++ [x]) ac2 c xs
							      else if c == ',' then stringSIntAC [x] (ac2 ++ [strint ac1]) c xs
									       else stringSIntAC ac1 ac2 c xs

O meu raciocínio baseia-se no seguinte: uso 3 variáveis numa função auxiliar (stringSIntAC).

- ac1: guarda os resultados parciais, isto, é, no exemplo de cima, vai guardando o 12, o 23 e depois o 34.

- ac2: acumulador para onde passo os resultados parciais de ac1 e onde a lista vai sendo construída.

- ac3: variável que guarda o caracter não dígito da string (para que possa detectar quando tenho de guardar os dígitos em ac1)

O problema encontra-se em:

else if c == ',' then stringSIntAC [x] (ac2 ++ [strint ac1]) c xs

onde invoco a função auxiliar com o ac1 com aquele valor. Ora isto funciona bem para o 1º elemento, mas para os seguintes já não. Alguém tem ideias?

Share this post


Link to post
Share on other sites
Rui Carlos

A primeira coisa que acho que devias fazer, era remover os parêntesis.

Depois, acho que estás a complicar. Ainda não percebi muito bem o que queres fazer, mas vê o algoritmo que eu sugiro aqui: http://www.portugal-a-programar.pt/forums/topic/0-find-topic/?do=findComment&comment=197440

É mais ou menos o que estás a fazer, mas ligeiramente mais simples. É que ao estares a fazer testes com o c e com o x só estás a complicar...

EDIT:

Pegando na tua versão, e efectuando algumas modificações...

stringSInt :: String -> [int]
stringSInt l = stringSIntAC [] [] ' ' l
        where stringSIntAC ac1 ac2 _ [] = ac2 ++ [(read ac1)::Int]
              stringSIntAC ac1 ac2 c (x:xs) = if c == '[' then stringSIntAC ac1 ac2 x xs
                                                          else if c == ',' then stringSIntAC [] (ac2 ++ [(read ac1)::Int]) x xs
                                                                           else stringSIntAC (ac1++[c]) ac2 x xs

Share this post


Link to post
Share on other sites
Baderous

Rui Carlos, consegui fazer através do raciocínio da outra thread:

stringSInt :: String -> [int]
stringSInt l = stringIntAC [] [] l
where stringIntAC _ ac2 [] = ac2
      stringIntAC ac1 ac2 (x:xs) | not $ isDigit x = if x == '[' then stringIntAC ac1 ac2 xs
						     else stringIntAC [] (ac2 ++ [strint ac1]) xs
				 | otherwise = stringIntAC (ac1 ++ [x]) ac2 xs

Assim fica resolvido sem usar a função read (que não era suposto usar neste caso).

Quanto aos parêntesis, não sei se estavas a falar do operador $ como usei agora.

Share this post


Link to post
Share on other sites
Baderous

Ah LOL! Estavas a falar dos parêntesis da string do input! Eu pensei que era do código. Mas é bem pensado.

Share this post


Link to post
Share on other sites
Betovsky

Assim fica resolvido sem usar a função read (que não era suposto usar neste caso).

Pois.

stringSInt = read

Se não houvesse essa restrição ficava demasiado fácil ;)

Mas estás a complicar demasiado.

import Data.List
import Data.Char
import Control.Arrow

stringSInt :: String -> [int]
stringSInt ('[':s) = map read $ unfoldr fun s
  where fun [] = Nothing
        fun l = Just $ (second tail) $ (span isDigit l)

Onde está read seria a tua função strint


"Give a man a fish and he will eat for a day; Teach a man to fish and he will eat for a lifetime. The moral? READ THE MANUAL !"

Sign on a computer system consultant's desk

Share this post


Link to post
Share on other sites
punkzero

Boas pessoal,

Tou aqui devolta de um trabalho para a universidade, em que tenho de fazer parsing a um ficheiro .CSV que trata de inscrições, mas não posso utilizar o parsec ou assim, não é que seja complicado, o problema é que um dos campos da inscrição é uma lista de strings, isto é, por exemplo:

nome, idade,sexo,razoes_inscricao
José,23,M," [ 'porque sim','porque nao' ] "
António,22,M," [ 'porque sim','porque nao' ] "

eu sei como base para fazer parsing do ficheiro, a função que estavam a discutir aqui, a que eu usei é assim:

organizar_Linhas :: String -> [string]
organizar_Linhas linha = organizar_LinhasAC [] [] linha
                                              where
                                                          organizar_LinhasAC [] ac2 [] = ac2
                                                          organizar_LinhasAC ac1 ac2 [] = ac2++[ac1]
                                                          organizar_LinhasAC ac1 ac2 (x:xs) | (x==',') = organizar_LinhasAC [] (ac2++[ac1]) xs
					                                                                           | otherwise = organizar_LinhasAC (ac1++[x]) ac2 xs

peço desculpa se se parece demasiadamento igual a aqui apresentada.

o problema é que quando faço o parsing e dando como exemplo o CSV acima, ele ficaria assim:

["José","23","M","\" [ 'porque sim' "," 'porque nao' ] \" "]
["António","22","M","\" [ 'porque sim' "," 'porque nao' ] \" "] 

ou seja, ele deveria dar toda a lista de razões como apenas um campo da inscrição, assim:

["José","23","M",[ "porque sim","porque nao" ] ]

já experimentei de várias maneiras mas ainda faziam pior...alguém me podia dar alguma luz sobre isto??  :)

Share this post


Link to post
Share on other sites
Betovsky

ou seja, ele deveria dar toda a lista de razões como apenas um campo da inscrição, assim:

["José","23","M",[ "porque sim","porque nao" ] ]

Hmmm... Mas isso não é um tipo válido em Haskell.

Ou é uma lista de strings, ou então, uma lista de listas de strings.


"Give a man a fish and he will eat for a day; Teach a man to fish and he will eat for a lifetime. The moral? READ THE MANUAL !"

Sign on a computer system consultant's desk

Share this post


Link to post
Share on other sites
punkzero

boas...

então se calhar a melhor maneira será  tentar obter a lista como uma string apenas, e depois se a precisar analisar posteriormente usar uma função que chame esse campo da CSV...

basicamente ficava com a inscrição assim:

["José","23","M","[porque sim,porque nao ]" ]

mesmo assim, tenho o problema da vígula que está la nesse campo que não sei bem como resolver. o que eu quero obter é o que esta acima, o que eu tenho é, como disse no outro post

["José","23","M","[porque sim","porque nao ]" ]

Share this post


Link to post
Share on other sites
punkzero

alguém pode dar uma dar uma dica ou opinião de como poderia fazer isto, talvez chamar novamente a funçao organizar_LinhasAC até encontrar ']', mas penso que iria dar asneira...

Share this post


Link to post
Share on other sites
IceBrain

Esses dados estão mais estruturados como um tuplo que uma lista.. Algo como:

("José", 23, "M", ["porque sim", "porque não"])


❝The idea that I can be presented with a problem, set out to logically solve it with the tools at hand, and wind up with a program that could not be legally used because someone else followed the same logical steps some years ago and filed for a patent on it is horrifying.❞- John Carmack on software patents

A list  of command line apps

Share this post


Link to post
Share on other sites
punkzero

isto é  a estrutura do CSV:

nome,email,curso,numero-de-aluno,universidade,curso-1,filiacao-empresa,jantar-da-conferencia,almocos-no-restaurante-panoramico,porque-se-inscreveu-nas-join,outro-1,dt

mas eu nao estava a conseguir trabalhar com (criei até umas funções para fazer o parsing mas dava-me sempre erro, apaguei-as e agora não tenho aqui para poder mostrar):

type Inscricao = (Nome, Email, N_aluno, Universidade, Curso, Curso_ext, Empresa, Jantar_op, Almocos_op, Razoes_insc, Outras_razoes_insc, Data_insc)
type Csv_inscricoes=[inscricao]

entao optei por converter cada uma das linhas do csv em uma lista, cada lista representava uma inscrição, mas surgiu-me o problema das vírgulas nas razões de inscrição, se alguém me poder dar uma luz sobre como poderei resolver isto...

Share this post


Link to post
Share on other sites
Betovsky

Estou perdido, e não devo ser o único. Qual é o problema exactamente?

O que estás a fazer? Qual o resultado que estás a obter? E qual o resultado que era suposto obter?


"Give a man a fish and he will eat for a day; Teach a man to fish and he will eat for a lifetime. The moral? READ THE MANUAL !"

Sign on a computer system consultant's desk

Share this post


Link to post
Share on other sites
Rui Carlos

Tal como o IceBrain referiu, não faz sentido usar uma lista. Se não quiseres usar um tuplo, define um tipo usando o construtor data.

Relativamente às vírgulas na razão da inscrição, diria que isso é problema do formato de entrada. Se o conteúdo de um campo pode ter vírgulas, deveria ser usado outro separador. Tens que perguntar a quem definiu o problema como é que se deve lidar com as vírgulas.

Há várias alternativas, desde alterar o ficheiro de entrada, até considerar que só um campo é que pode conter vírgulas (e neste caso, fazes parsing de tudo o resto, e o que sobrar é o motivo).

Alguns exemplos de inputs davam jeito.

Share this post


Link to post
Share on other sites
punkzero

Vamos ver se consigo passar a ideia que realmente quero...

é asim, o meu programa recebe um ficheiro .csv e tenho de fazer o seu parsing, o formato é o seguinte:

nome, idade,sexo,razoes_inscricao
José,23,M,"['porque sim','porque nao' ] "
António,22,M,"[ 'porque sim','porque nao' ] "

Eu estou a fazer map com uma função que tirei ideia daqui para transformar cada linha do ficheiro numa lista, e o resultado final é uma lista de listas de strings:

organizar_Inscricao :: [string] -> [[string]]
organizar_Inscricao [] = []
organizar_Inscricao linha = map organizar_Linhas linha 

organizar_Linhas :: String -> [string]
organizar_Linhas linha = organizar_LinhasAC [] [] linha
                 where
                       organizar_LinhasAC [] ac2 [] = ac2
                       organizar_LinhasAC ac1 ac2 [] = ac2++[ac1]
                       organizar_LinhasAC ac1 ac2 (x:xs) | (x=='\'') = organizar_LinhasAC ac1 ac2 xs
                                                         | (x==',') = organizar_LinhasAC [] (ac2++[ac1]) xs
					         | otherwise = organizar_LinhasAC (ac1++[x]) ac2 xs

o problema, é que um dos campos, neste exemplo o "razoes_inscricao" é uma lista de strings e quando corro o programa ele dá algo do género:

[["José","23","M","\"[porque sim","porque nao]\""],["António","22","M","\"[porque sim","porque nao]\""]] 

mas eu quero que ele intrepete a lista [porque sim, porque nao] como "[porque sim,porque nao]" mas o programa esta a dividí-la por causa da vírgula e eu queria que isso não acontecesse, queria que o resultado final fosse:

[["José","23","M","[porque sim,porque nao]"],["António","22","M","[porque sim,porque nao]"]] 

Nota: o meu csv não tem apenas 4 campos, tem muitos mais, usei isto como exemplo, porque no meu caso também só tem um campo que é uma lista de strings

Share this post


Link to post
Share on other sites
Rui Carlos

Tens que ler o último campo de forma diferente. Pode ir contando os valores que já leste, ou podes simplesmente " e simplesmente devolver o resto da string quando o encontrares.

Share this post


Link to post
Share on other sites
punkzero

Tens que ler o último campo de forma diferente. Pode ir contando os valores que já leste, ou podes simplesmente " e simplesmente devolver o resto da string quando o encontrares.

no meu caso, esse campo não é o último do csv, eu já pensei nisso de analisar até encontrar novamente " , mas não consegui implementar, não podes dar um exemplo básico sobre isso?

como em haskell não há ciclos...não consegui implementar isso

Share this post


Link to post
Share on other sites
Rui Carlos

Por isso é que eu disse para apresentares um exemplo de input, pois a solução para o problema depende do input.

Adiciona um argumento à função, que serve de flag a dizer se estás a ler esse campo ou não. Quando encontras as aspas, mudas a flag para dizer que estás dentro do campo, e quando voltares voltas a mudar para dizer que estás fora. E enquanto a flag estiver a True, fazes o mesmo que fazes no otherwise.

Share this post


Link to post
Share on other sites
punkzero

não me tinha lembrado disso...acho que isso já vai servi, obrigado...depois faço um post aqui a dizer se funcionou!

Share this post


Link to post
Share on other sites
IceBrain

Isso poderia ser lido decentemente usando uma máquina de estados (que é na prática o que essa flag fará). Assim, poder-se-iam ter estados diferentes para ler cada campo, de forma a reconhecer o tipo de dados certo.


❝The idea that I can be presented with a problem, set out to logically solve it with the tools at hand, and wind up with a program that could not be legally used because someone else followed the same logical steps some years ago and filed for a patent on it is horrifying.❞- John Carmack on software patents

A list  of command line apps

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.