1
votes

I need help with an exercise. I'm trying to write a function called "refer" that will take a list of novels and any given text with citations ([n]) and will return that text with the citations replaced by the novel's name.

refer will have the following signature of:

refer :: [(String, String, Int)] -> String -> String

For example, this is how it will be ran:

> refer [("author1", "novel1", 1999),("author2", "novel2", 2000)] txt
> "novel1(author1, 1999) and novel2(author2, 2000) are my favorite books of all time, but I like novel2(author2, 2000) the most!"

I wrote a function called, txt, which will show my text that I will use.

txt :: String
txt = "[1] and [2] are my favorite books of all time, but I like [2] the most!"

I wrote a helper function called, format, which will help me format the novels from ("author1", "novel1", 1999) to "novel1(author1, 1999)"

format :: (String, String, Int) -> String
format (name, novel, yearInt) = novel  ++ " (" ++ name ++
                            ", " ++ (show yearInt) ++ ")" 

WHAT I THINK I NEED TO DO:

Step 1: I need to use words to break the input into a list of strings.
Step 2: I should make a helper function to parse through the list and if I find a citation, I should use format to replace that citation and recursively parse through the rest of the list until I've checked everything.
Step 3: Make a helper function to convert the string representation of the citation number into an Int (possibly, unwords) since I have to replace the citation with its corresponding element in the given list.
Step 4: Then I need to use rewords to turn my updated list back into a string.

WHAT I HAVE SO FAR:

refer :: [(String, String, Int)] -> String -> String
refer [] "" = ""
refer books txt = [string'| y <- words txt, ........... ]
-- I'm trying to say that I want to take the inputted list of
-- novels and text and turn them all into strings and store
-- them into y which will be represented by string'. I am not
-- sure what to do after this.
2

2 Answers

1
votes

You could use words, but then you lose information about the white space between the words - i.e. words "a b" equals words "a b". Maybe this is not important, but it is something to keep in mind.

Without providing the exact solution, here is a function which replaces a with a' and b with b' in a list:

replace a a' b b' [] = []       -- base case
replace a a' b b' (x:xs) =  c : replace a a' b b' xs
   where c = if x == a then a' else if x == b then b' else x

Perhaps you can figure out how to adapt this to your problem.

Note also that this function may be written using map:

replace a a' b b' xs = map f xs
  where f x = if x == a then a' else if x == b then b' else x

Another approach to this kind of string processing is to pattern match against the characters. Here is a function which removes all occurrences of "cat" from a string:

removeCat :: String -> String
removeCat ('c':'a':'t':rest)   = rest   -- "cat" found so remove it
removeCat (x:rest)             = x : removeCat rest
0
votes

If this is for a homework problem please don't copy verbatim.

It's easier to solve the generic problem.

First, you need a replace function to replace a substring. A straightforward implementation can be

replace :: String -> String -> String -> String
replace old new [] = []
replace old new input = if (take n input == old) then new++(recurse n)
                    else (take 1 input)++(recurse 1)
                    where n = length old
                          recurse k = replace old new $ drop k input 

tries to match the "old" from the beginning of input and if matched replace and skip the length of old, if not matched move one position and repeat.

So far so good, this will replace all occurrences of "old" to "new". However, you need multiple different replacements. For that let's write another function. To minimize validations assume we paired all replacements.

replaceAll :: [(String,String)] -> String -> String
replaceAll [] input = input
replaceAll ((old,new):xs) input = replaceAll xs $ replace old new input

You can make the top level signature better by defining a type such as

type Novel = (String,String,Int)

finally,

refer :: [Novel] -> String -> String
refer [] text = text
refer ns text = replaceAll p text
                   where p = [ ("["++show i++"]",format n) | (i,n) <- zip [1..] ns]

notice that the citation input is derived from the position of the Novels.