matt
matt

Reputation: 2039

String substitution operators with lists in Haskell

I would like to be able to create a string using printf like functionality, with the variables drawn from a list and inserted into a template string.

i.e.

let templateStr = "First comes %s, then comes %s, after which comes %s"
let vars = ["one","two", "three"]

and some function returns:

function returns >>> First comes one, then comes two, after which comes three

i.e. in Python I could do something like:

>>> templateStr = "First comes %s, then comes %s, after which comes %s"
>>> vars = ["one","two", "three"]
>>> outputStr = tempStr % tuple(vars)
>>> print outputStr
First comes one, then comes two, after which comes three

My Attempt

mergeList :: [a] -> [a] -> [a]
mergeList [] ys = ys
mergeList (x:xs) ys = x:mergeList ys xs

-- not actually needed * use: Prelude.concat
listConcat :: [[a]] -> [a]
listConcat [] = []
listConcat (x:xs) = x ++ listConcat xs

-- via @dfeuer list concat is not need because of Prelude.concat
printf' :: String -> [String] -> String
printf' s v = concat $ mergeList (splitOn "%s" s) v

Attempt via @Reid Barton

printf' :: String -> [String] -> String
printf' ('%':'s':rest) (v:vs) = v ++ printf' rest vs
printf' (c:rest)        vs    = c :  printf' rest vs
printf' []              _     = []

both attempts give

>>> let templateStr = "First comes %s, then comes %s, after which comes %s"
>>> let vars = ["one","two", "three"]
>>> printf' templateStr vars
"First comes one, then comes two, after which comes three"

Upvotes: 3

Views: 353

Answers (2)

Reid Barton
Reid Barton

Reputation: 14999

The outline of another, more direct approach:

printf' ('%':'s':rest) (v:vs) = ...
printf' (c:rest)       vs     = ...
... -- handle the remaining cases too

Upvotes: 3

dfeuer
dfeuer

Reputation: 48581

Great start! mergeList is very clean.

mergeList :: [a] -> [a] -> [a]
mergeList [] ys = ys
mergeList (x:xs) ys = x:mergeList ys xs



listConcat :: [String] -> String
listConcat [] = []
listConcat (x:xs)
  | null xs   = x
  | otherwise = x ++ listConcat xs

You can do better than this for listConcat. In particular, you currently use two base cases for the recursion, but you only need one. Furthermore, you can change the type signature to listConcat :: [[a]] -> [a] to make it more general. Once you've cleaned up your version: There's a function like that in the standard library. Can you find it?

Upvotes: 1

Related Questions