I'm still learning Haskell and I'm doing a few exercises, but I'm in a jam. So I have a function called "novel" which takes 2 Strings and an Int
(novel :: (String, String, Int) -> String) for its arguments. Novel's input/output must look like the following:
> novel ("Rowling", "Harry Potter", 1998)
"Harry Potter (Rowling, 1998)"
This is my code for my novel function which works as explained above:
novel :: (String, String, Int) -> String
novel (author, book, year) = book ++ " (" ++ author ++ ", " ++ (show year) ++ ")"
I am trying to write a new function called, "cite" (cite :: [(String, String, Int)] -> String). Cite's input/output should look like the following:
> cite [("author1", "book1", year1), ("author2", "book2", year2), ("author3", "book3", year3)]
"book1 (author1, year1)
book2 (author2, year2)
book3 (author3, year3)"
I am trying to use "novel," recursively, in order to get the desired output, but I am not sure how to go about this.
What I've tried:
cite :: [(String, String, Int)] -> String -- | Listed arguments
cite [] = "" -- | Base Case
cite x:xs = [(novel (author, book, year)), (novel (author, book, year)), (novel (author, book, year))]
This is honestly as far as I got. Obviously, it doesn't work, but I am not sure what to do from here.
Perhaps this will give you a head start:
cite :: [(String, String, Int)] -> String
cite [] = ""
cite (x:xs) = undefined -- put your code that recursively calls cite in here, hint: use ++ and "\n\"
The pattern match (x:xs) says this, give me the first item in the list x and the tail of the list xs. It would be the same as writing this:
cite xs' = let x = head xs'
xs = tail xs'
in undefined -- your code here
Or even
cite xs' = undefined -- your code here
where
x = head xs'
xs = tail xs'
Hope that helps push you in the right direction.
EDIT: OP asked for how to do this recursively, below is my original answer:
You should probably re-write your base case to say cite [] = "". It doesn't really make a difference, but it will help with code readability.
Let's start by putting ":t map novel" into ghci to see what you get:
> :t map novel
map novel :: [([Char], [Char], Int)] -> [[Char]]
Which we can rewrite as: map novel :: [(String, String, Int)] -> [String]
How? Because map does a transformation of one type a to another type b and applies it to every item in a list. The first argument of map is any function which takes one argument. Exactly what novel does.
But that doesn't give us what you need, we'll end up with a list of Strings instead of a String:
> cite [("author1", "book1", year1), ("author2", "book2", year2), ("author3", "book3", year3)]
["book1 (author1, year1)","book2 (author2, year2)","book3 (author3, year3)"]
And you would like it to be a single string separated by a newline character "\n". Is there a function that can take a list of strings and concatenate them into one string, but intercalate a separator between them?
First let's describe such a function: String -> [String] -> String. Next we chuck it into Hoogle to see what we get: https://www.haskell.org/hoogle/?hoogle=String+-%3E+%5BString%5D+-%3E+String
Ah, that second function intercalate sounds like what we need. It doesn't just work on Strings, it works on any list. How would it work? Something like this:
> import Data.List (intercalate)
> intercalate "\n" ["List","Of","Strings"]
"List\nOf\nStrings"
So now you can combine intercalate and map to get what you are after. I'll leave the definition for cite up to you.
EDIT: Completely forgot, there is actually a specialised function for this. If you just search for [String] -> String in Hoogle you'll find unlines
There's a reasonably simple way of doing this.
First, map novel to each element of the given list, then use Data.List.intersperse to fill the gaps with newlines. This is my implementation:
import Data.List (intersperse)
cite :: [(String, String, Int)] -> String
cite bs = intersperse '\n' (map novel bs)
Or, in a more elegant points-free style:
cite = intersperse '\n' . map novel
One could also write a nice, efficient recursive function:
cite [] = ""
cite [x] = novel x
cite (x:xs) = novel x ++ '\n' : cite xs
In future problems such as this, keep in mind functions such as map and foldr - these are two of the most integral parts of Haskell and functional programming. Also, your pattern matches need to be enclosed in parentheses.
Related
I have a list of Strings I want to filter through. My predicate is that the string should begin with an uppercase letter.
eg. when I run onlyLowercase ["boy", "girl", "Hi"]
it should give me a list of ["boy", "girl"]
I can do it using pattern matching and guards, but I'm using the learnyouahaskell (http://learnyouahaskell.com) book and I came across the topic on higher-order functions. I read about the filter function and thought it could achieve what I want to do in far fewer lines of code.
Using pattern Matching/Guards (This works well and solves my problem)
onlyLowercase :: [[Char]] -> [[Char]]
onlyLowercase [] = []
onlyLowercase (x:xs)
| isLower (head x) = x : onlyLowercase xs
| otherwise = onlyLowercase xs
Using the filter function
onlyLowercase2 :: [String] -> [String]
onlyLowercase2 [] = []
onlyLowercase2 (x:xs) = filter isLower x : onlyLowercase2 xs
Unfortunately, when I run onlyLowercase2 ["boy", "girl", "Hi"],
I get a list of ["boy", "girl", "i"].
I want to know if there's a way I can filter my list of strings using the first character in my string (without creating any auxiliary function that could check the String and return true if the first letter is lowercase).
I also tried using
onlyLowercase2 (x:xs) = filter (isLower head x) : onlyLowercase2 xs
but that didn't even compile. Basically, I'm just trying to figure out how the filter function can be used on a list of lists. Thank you, in advance, for any assistance rendered.
Thanks to Willem Van Onsem's suggestion to use a lambda expression as a filter function, I read further and came up with this 2 line solution.
onlyLowercase2 :: [String] -> [String]
onlyLowercase2 = filter (\st-> ("" /= st) && (isLower $ head st))
Not sure if it's perfect, but at least it's working.
Using Data.List and Data.Char:
import Data.List
import Data.Char
onlyLowerCase :: [String] -> [String]
onlyLowerCase = filter (all isLower)
I use the all function which checks that all elements of a list satisfy a predicate. In this case all isLower will return true if all letters in a String are lowercase. Then just filter the Strings that are all lowercase. The Haskell Report has a good reference for List and Char functions among other useful libraries.
I'm trying to write a Haskell function that takes a string of pairs of letters, and exchanges the letters of the pair in a string of all letters, but what I've come up with feels awkward and unidiomatic.
I have
swap a b = map (\x-> if x == a then b else if x == b then a else x)
sub n = foldr (.) id (zipWith swap (head <$> splitOn "." n) (last <$> splitOn "." n)) ['A'..'Z']
which works well enough giving
> sub "RB.XD.EU.ZM.IJ"
"ARCXUFGHJIKLZNOPQBSTEVWDYM"
and
> sub "YC.LU.EB.TZ.RB.XD.IJ"
"ARYXBFGHJIKUMNOPQESZLVWDCT"
but I'm new to Haskell and feel like my approach — especially my swap helper function (which I only use here) — is more elaborate than it needs to be.
Is there a better, more idiomatic, approach to this problem; especially one that takes advantage of a language feature, builtin, or library function that I've missed?
Doing a left fold over the substitution list makes the code shorter:
import Data.List
import Data.List.Split
sub = foldl' swap ['A'..'Z'] . splitOn "." . reverse
where
swap az [a,b] = map (\x -> if x == a then b else if x == b then a else x) az
Drop the reverse if you don't care whether EB or RB is swapped first.
If you'd want to replace instead of a swap:
import Data.List
import Data.List.Split
replace needle replacement haystack =
intercalate replacement (splitOn needle haystack)
rep = foldl' replace' ['A'..'Z'] . splitOn "."
where
replace' az [a,b] = replace [a] [b] az
I'd break the problem down a bit more. It's important to remember that shorter code is not necessarily the best code. Your implementation works, but it's too compact for me to quickly understand. I'd recommend something more like
import Data.Maybe (mapMaybe)
swap = undefined -- Your current implementation is fine,
-- although you could rewrite it using
-- a local function instead of a lambda
-- |Parses the swap specification string into a list of
-- of characters to swap as tuples
parseSwap :: String -> [(Char, Char)]
parseSwap = mapMaybe toTuple . splitOn "."
where
toTuple (first:second:_) = Just (first, second)
toTuple _ = Nothing
-- |Takes a list of characters to swap and applies it
-- to a target string
sub :: [(Char, Char)] -> String -> String
sub charsToSwap = foldr (.) id (map (uncurry swap) charsToSwap)
The equivalent to your sub function would be
sub swapSpec = foldr (.) id (map (uncurry swap) $ parseSwap swapSpec)
But the former is probably easier to understand for most haskellers. You could also do more transformations more easily to your swap specification as a list of tuples making it more powerful overall. You essentially decouple the representation of the swap specification and the actual swapping. Even for small programs like this it's important to maintain loose coupling so that you develop a habit for when you write larger programs!
This implementation also avoids recalculating splitOn for the swap specification string.
(I wasn't able to execute this code because I'm on a computer without Haskell installed, if anyone notices any bugs please edit to fix.) Tried it out in FPComplete, output matches #raxacoricofallapatorius'.
Some things I noticed from reading your code (I haven't tried to rewrite it). My first suggestion involves separation of concerns:
I'm trying to write a Haskell function that takes a string of pairs of letters, and exchanges the letters of the pair in a string of all letters
That means the a more natural type for your function would be:
sub :: [(Char, Char)] -> String -> String
Or, using Data.Map for more efficient lookups:
sub :: Map Char Char -> String -> String
Which is a lot more precise than taking a string with dot-separated pairs. You can then generate the associations between Chars in a separate step:
parseCharPairs :: String -> Map Char Char
Ideally you should also handle invalid inputs (e.g. AB.CDE) and empty input strings.
my swap helper function (which I only use here)
Then you probably should define it in a where clause. I would also avoid the name swap, as there is a relatively common function in Data.Tuple with the same name (swapLetters might be a nice choice).
sub n = foldr (.) id -- etc.
foldr (.) id (fmap f xs) y is the same thing as foldr f y xs. I'm almost certain this can be rewritten in a simpler way.
I'm sure these are both very stupid mistakes, but i'm trying to convert and print two lists , and i'm getting an error on ghci.
First, i want to convert from this:
["2","2","2"]
to this
[2,2,2]
for that, i wrote this function:
convert (x:xs) = [read x | x <- xs]
but that doesn't seem to be working...
Second:
Here's my printing function:
print_results [] _ = error("Empty List!")
print_results _ [] = error("Empty List!")
print_results (x:xs) (y:ys) = print x ++ " + " ++ print y : print_results xs ys
For this input:
[2,2,2] and [3,3,3]
The desired output should be:
2 + 3
2 + 3
2 + 3
Thanks in advance!
These are not "stupid" mistakes, but you're going to have to step back a bit from "what type do I write here" in order to make sense of what's going on. I notice you've asked a bunch of overlapping questions today surrounding these issues. I hope we as a community can get you an answer that will get you on the right track. In that light, I'm marking this post Community Wiki and encouraging others to edit it.
In Haskell, every value has a specific, concrete type. We can write functions that work on multiple types. Some of them work on all types: replicate 5 :: a -> [a] doesn't care at all about what a is. Some work only on some types: read :: Read a => String -> a requires that a be an instance of the class Read.
For now, you should assume that, in order to actually run a function and print a result in GHCi or compiled code, you need to replace all type variables to specific types. (This is wrong in lots of ways that I or others will probably expand on.)
After writing a function, ask GHCi for its inferred type, which is usually the most general signature possible:
> :t map read
map read :: Read a -> [String] -> [a]
> :t map read $ ["as","ew"]
> map read $ ["as","ew"] :: Read a => [a]
Notice that we still have a type variable in there. We need to choose a specific type. What #chi and I both encouraged you to do was to add a type annotation somewhere to fix that type. But if you fix that type to Int, you're trying to parse "as" and "ew" as numbers, which obviously fails.
First:
convert :: [String] -> [Int]
convert xs = [read x | x <- xs]
or even
convert :: [String] -> [Int]
convert = map read
Note that the type annotation matters, since without it Haskell does not how how it should read the string (as an Int? a Char? a tree? a list? etc.)
Second:
print_results [] [] = []
print_results [] _ = error "Empty List!"
print_results _ [] = error "Empty List!"
print_results (x:xs) (y:ys) = (show x ++ " + " ++ show y) : print_results xs ys
The above will compute a list of strings, formatted as you wanted. If you really want to print them doing an IO action, you can use
mapM_ putStrLn (print_results list1 list2)
I have to concat two string given as input into one singe string and put it in a list as output
type Language = [String]
cat :: Language -> Language -> Language
cat l1 l2 =
case l1 of
[""] -> l2
(x:xs) -> case l2 of
[""] -> l1
(y:ys) -> xs ++ ys
and the output should be:
["string1string2"]
any Idea in haskell?
Given your exact problem specification, it is solved by
concatWithinLists :: [String] -> [String] -> [String]
concatWithinLists [x] [y] = [x ++ y]
This is bad in all kinds of ways. All of them stem from your insistence that you will only ever have lists of exactly length 1, completely missing the point of lists.
I strongly recommend reconsidering everything that led you to this issue. The real problem isn't here - it's somewhere higher up in your design. It will continue to be a problem as long as you lie to the type system about the contents of your data. You aren't working with [String], you're working with String and have attached some noise for no benefit.
Why are you passing your strings through in a list? Doing so opens problems, like your code crashing should empty lists be given as an argument (with the exception of cat [""] []). Plus, your pattern matching is off: xs ++ ys becomes [] ++ [] when singleton lists are passed as arguments. This is because [x] = x:[]. A simpler solution would be:
cat :: String -> String -> [String]
cat s1 s2 = [s1 ++ s2]
I'm pretty brand new to Haskell (only written a fizzbuzz program before the current one) and am trying to write a program that takes the unix wordlist ('/usr/share/dict/words') and prints out the list of anagrams for that word, with any direct palindromes starred. I have the meat of this summed up into one function:
findAnagrams :: [String] -> [(String, [String])]
findAnagrams d =
[x | x <- map (\s -> (s, [if reverse s == t then t ++ "*" else t | t <- d, s /= t && null (t \\ s)])) d, not (null (snd x))]
However, when I run the program I get this output:
abase: babes, bases
abased: debase
abasement: basements
abasements: abatements
abases: basses
And so on, so clearly it isn't working properly. My intention is for the list comprehension to read as follows: for all t in d such that t is not equal to s and there is no difference between t and s other than order, if t is the reverse of s include as t*, otherwise include as t. The problem seems to be with the "no difference between t and s other than order" part, which I'm trying to accomplish by using "null (t \ s)". It seems like it should work. Testing in GHCI gives:
Prelude Data.List> null ("abatements" \\ "abasements")
False
And yet it passes the predicate test. My assumption is that I'm missing something simple here, but I've looked at it a while and can't quite come up with it.
In addition, any notes regarding best practice would be greatly appreciated.
If you break it out into multiple functions (remember, source code size is not really that important), you could do something like:
import Data.List
isPalindrome :: String -> Bool
isPalindrome s = s == reverse s
flagPalins :: [String] -> [String]
flagPalins [] = []
flagPalins (x:xs)
| isPalindrome x = x ++ "*"
| otherwise = x
isAnagram :: String -> String -> Bool
isAnagram s t = (isPalindrome s || s /= t) && ??? -- test for anagram
findAnagrams :: String -> [String] -> [String]
findAnagrams s ws = flagPalins $ filter (isAnagram s) ws
findAllAnagrams :: [String] -> [(String, [String])]
findAllAnagrams ws = filter (not . null . snd) ??? -- words paired with their anagrams
I've intentionally left some holes for you to fill in, I'm not going to give you all the answers ;)
There are only two spots for you to do yourself. The one in findAllAnagrams should be pretty easy to figure out, you're already doing something pretty similar with your map (\s -> ...) part. I intentionally structured isAnagram so it'll return True if it's a palindrome or if it's just an anagram, and you only need one more check to determine if t is an anagram of s. Look at the comment I made on your question for a hint about what to do there. If you get stuck, comment and ask for an additional hint, I'll give you the name of the function I think you should use to solve this problem.
If you really want to make a list comprehension, I would recommend solving it this way, then converting back to a comprehension. In general you should write more verbose code, then compress it once you understand it fully.
Think of a \\ b as "items in a that are not in b."
Consider the implications.