1

I want to make a function that takes in a string of multiple "grades" of varying length and convert it to a list of grades.

Grade is just a data structure that looks like this (just an arbitrary grading system):

data Grade = A+ | A | A- | B+ | B | B- | P | F
    deriving (Show, Eq)

As you can see, the grades have varying length. If they had length 1 or consistent length, this would have been much easier.

Here is the function that I want to make:

This is what the string input looks like "PA+FABA+B-A"

stringToGrade :: String -> Grade
stringToGrade stringGrade
  | stringGrade == "A+" = A+
  | stringGrade == "A" = A
  -- and so on

extractGrades :: String -> [Grade]
extractGrades stringGrades = case stringGrades of
  [] -> []
  x:y:ys
    | x == "A" && y == "+" -> [stringToGrade (x : y)] : extractGrades ys
    | x == "A" -> [stringToGrade x] : extractGrades y:ys
    -- and so on

As you can see, this is not going anywhere.

Is there an elegant and easy way I cam do this instead of had coding everything?

user6005857
  • 631
  • 9
  • 25
  • Will there be any delimiters between grades in the string? (It's a well-posed problem even without that, but it's easier that way...) – Andrew Jaffe Apr 20 '16 at 12:25
  • Oh, I will edit the question. It looks like this `"A+FABA+B-A"` – user6005857 Apr 20 '16 at 12:27
  • I actually don't want it to be split by alphabetical characters because I want this to work for data structures that have the same type. – user6005857 Apr 20 '16 at 12:33
  • is it only me - you are basically there if you say `| x == 'A' && y == '+' -> A+ : extractGrades ys` following with `| x == 'A' -> A : extractGrades (y:ys)` and so on - yes you have to type it all in but hey it's supposed to be working not looking neat for iteration 0 right? – Random Dev Apr 20 '16 at 12:33
  • the only problem you seem to have is that you don't understand the differences between `Char` and `[Char] = String` and `:` vs `++` - you will also need a final case with just one character left – Random Dev Apr 20 '16 at 12:35
  • @zakyggaps I made a new update and added a Pass grade. I am ok with separating the list by alphabetical characters. – user6005857 Apr 20 '16 at 12:44
  • Minor point: in Haskell `A+` is not a valid constructor name, I'd recommend `Ap` instead. – chi Apr 20 '16 at 12:57
  • @chi this point is fluctuating ;) – Random Dev Apr 20 '16 at 13:22
  • You (or others who find this question close to their own) may also like [this question about creating a parsec parser that chooses among many strings that may share prefixes](http://stackoverflow.com/q/34356668/791604). – Daniel Wagner Apr 20 '16 at 18:15

2 Answers2

4

We can apply pattern matching so to match a string prefix. Here's an example:

foo :: String -> [Int]
foo [] = []
foo ('h':'e':'l':'l':'o':rest) = 1 : foo rest
foo ('b':'o':'b':rest) = 2 : foo rest
foo ('b':rest) = 3 : foo rest
foo _ = error "foo: invalid input syntax"

Sample usage:

foo "hellobbobbobhello" ==> [1,3,2,2,1]
chi
  • 111,837
  • 3
  • 133
  • 218
0

You can split the string into tokens using combination of split functions.

split (keepDelimsR $ oneOf "+-") "PA+FABA+B-A"

will create this form, where the suffixes are attached.

["PA+","FABA+","B-","A"]

Now, you can split this further with a custom splitter

splitInit [] = []
splitInit [x] = [[x]]
splitInit [x,y] = [[x,y]]
splitInit (x:xs) = [x] : splitInit xs

a combination will give you

concatMap splitInit $ split (keepDelimsR $ oneOf "+-") "PA+FABA+B-A"
["P","A+","F","A","B","A+","B-","A"]

where you can map through your constructors

karakfa
  • 66,216
  • 7
  • 41
  • 56