new to Haskell and functional programing...in the learning process. What is wrong with this code:
import System.IO
import Data.Char
import System.Environment
main = do
args <- getArgs
progName <- getProgName
content <- readFile $ head args
putStrLn $ show $ getWordsInfo content
getWordsInfo = let
wordList = filter (\x -> length x > 2 && all isAlpha x) . words
in foldl foldingFunction 0 wordList
where foldingFunction acc tWord = acc + length tWord
When I try to compile it, I get the following
Couldn't match expected type `[[a0]]'
with actual type `String -> [[Char]]'
In the third argument of `foldl', namely `wordList'
In the expression: foldl foldingFunction 0 wordList
In the expression:
let
wordList = filter (\ x -> length x > 2 && all isAlpha x) . words
in foldl foldingFunction 0 wordList
You appear to be using point free notation incorrectly.
The only line of this that is wrong is:
let wordList = filter (\x -> length x > 2 && all isAlpha x) . words
The error message is saying that when you call wordList it hasn't been applied to enough arguments, it is expecting a list of lists, but instead has been given a function which takes a string and produces a list of lists. So, we simply need to give the wordList function the input string.
You can rewrite it two ways:
The first is by explicitly specifying the argument:
getWordsInfo xs = let wordList = filter (\x -> length x > 2 && all isAlpha x) (words xs)
in foldl foldingFunction 0 wordList
where foldingFunction acc tWord = acc + length tWord
The second is by keeping the point free bit not in a let binding:
getWordsInfo = foldl foldingFunction 0 . filter (\x -> length x > 2 && all isAlpha x) . words
where foldingFunction acc tWord = acc + length tWord
Your folding function is taking the length of each word and summing them up, which can be simplified by mapping over the list, and taking the length, then summing the list.
getWordsInfo = sum . map length . filter (\x -> length x > 2 && all isAlpha x) . words
And that line is getting a bit long, so we should probably factor some of it out into another definition giving us finally:
import Data.Char (isAlpha)
getWordsInfo = sum . map length . filter isLongWord . words
where isLongWord x = length x > 2 && all isAlpha x
Usage:
λ> getWordsInfo "apple banana orange a a b b punctuation!!"
17
λ> getWordsInfo "aa bb cc"
0
λ> getWordsInfo "!!!"
0
λ> getWordsInfo "apple"
5
λ>
Related
I want to rotate a string in haskell, so if I give "Now I want to scream" to rotate [[want to scream now I],[scream now I want to]] , if the string start with "I" or "to" then must eliminate it. Till now I still have problems with the rotation.
reverseWords :: String -> String
reverseWords = unwords . reverse . words
shiftt :: [a] -> Int -> [a]
shiftt l n = drop n l ++ take n l
rot::String->[String]
rot l = [ reverseWords l i | i <- [0 .. (length l) -1]]
create a list of all rotations, then filter out based on your predicate. For example,
rotations x = take (length x) $ iterate rot1 x
where rot1 = drop 1 x ++ take 1 x
filteredRots = map unwords . filter (\x -> length (head x) > 2) . rotations . words
and use as
> filteredRots "Now I want to scream"
["Now I want to scream","want to scream Now I","scream Now I want to"]
Prelude>
New to Haskell:
Hi can't seem to figure this out.
What I am trying to do is take a string, turn it in to a [Int] (with map ord)
Change some numbers that fulfils something (in this case x mod 3 == 0).
afterwards I'd like to turn the unchanged numbers back to char, and changed numbers still numbers. Combine this into a string again..
This is my problem:
*Main> fromStringToList "hihello"
[104,105,104,101,108,108,111]
*Main> changeB3 [104,105,104,101,108,108,111]
"'h'210'h''e'216216222"
What I want is:
"h210he216216222"
I'm stuck figuring out how to use show and map to get this to work without the '_' from Char. Thanks.
My Code:
import Data.Char
fromStringToList :: String -> [Int]
fromStringToList "" = []
fromStringToList myString = map ord myString
{-
changeB3
PRE: True
POST: every Int that can be divided by 3 is multiplied by 2 and
kept as int, otherwise transformed to char
-}
changeB3 :: [Int] -> String
changeB3 [] = ""
changeB3 (x:xs)
| x `mod ` 3 == 0 = show map (x * 2 ) ++ changeB3 xs
|otherwise = map chr x ++ changeB3 xs
I will comment your code.
fromStringToList :: String -> [Int]
fromStringToList "" = []
fromStringToList myString = map ord myString
The second line is redundant: when myString is empty, map returns [] anyway. You should remove it.
changeB3 :: [Int] -> String
changeB3 [] = ""
changeB3 (x:xs)
| x `mod ` 3 == 0 = show map (x * 2 ) ++ changeB3 xs
|otherwise = map chr x ++ changeB3 xs
You seem to be confused here. You use a recursive function, but want to use map. You use either recursion or map here, not both.
Assuming you want to use map, you should start by defining how to handle a single Int.
changeB3Single :: Int -> String
changeB3Single x | x `mod` 3 == 0 = ...
| otherwise = ...
Then you map that over the whole list. A first attempt might be
changeB3 :: [Int] -> String
changeB3 xs = map changeB3Single xs -- type error!
but this won't work, since map here returns a list of strings, rather than a single string. We just need to concatenate them.
changeB3 xs = concat (map changeB3Single xs)
Indeed, concat (map ...) is so commonly found that it has its own function in the libraries:
changeB3 xs = concatMap changeB3Single xs
(One could make that pointfree, but there's no need to -- especially for a beginner.)
If I was given a string like skhfbvqa, how would I generate the next string? For this example, it would be skhfbvqb, and the next string of that would be skhfbvqc, and so on. The given string (and the answer) will always be N characters long (in this case, N=8).
What I tried:
I tried to generate the entire (infinite) list of possible combinations, and get the required (next) string of the given string, but unsurprisingly, it's so slow, that I don't even get the answer for N=6.
I used list comprehension:
allStrings = [ c : s | s <- "" : allStrings, c <- ['a'..'z'] ]
main = do
input <- readFile "k.in"
putStrLn . head . tail . dropWhile (not . (==) input) . map reverse $ allStrings
(Please excuse my incredibly bad Haskell-ing :) Still a noob)
So my question is, how can I do this? If there are multiple methods, a comparison between them is much appreciated. Thanks!
Here's a version with base conversion (this way you could add and subtract arbitrarily if you like):
encode x base = encode' x [] where
encode' x' z | x' == 0 = z
| otherwise = encode' (div x' base) ((mod x' base):z)
decode num base =
fst $ foldr (\a (b,i) -> (b + a * base^i,i + 1)) (0,0) num
Output:
*Main> map (\x -> toEnum (x + 97)::Char)
$ encode (decode (map (\x -> fromEnum x - 97) "skhfbvqa") 26 + 1) 26
"skhfbvqb"
I would go and make a helper function f :: Integer -> String and one g :: String -> Integer, where f 1 = "a", ... f 27 = "aa", f 28 = "ab" and so on and the inverse g.
Then incrementString = f . succ . g
Note: I omitted the implementation of f on purpose for learning
Update
for a different approach you could define a increment with carry function inc' :: Char -> (Char, Bool), and then
incString :: String -> String
incString = reverse . incString'
where incString' [] = []
incString' (x:xs) = case inc' x of (x',True) -> x': incString' xs
(x',False) -> x':xs
Note: this function is not tail recursive!
I found this to work. It just uses pattern matching to see if the string begins with a z and adds an additional a accordingly.
incrementString' :: String -> String
incrementString' [] = ['a']
incrementString' ('z':xs) = 'a' : incrementString' xs
incrementString' (x:xs) = succ x : xs
incrementString :: String -> String
incrementString = reverse . incrementString' . reverse
I want to create function that split string to list of substrings where each substring have length of k:
*Main> split_string_to_kmers "some_text" 3
["som","ome","me_","e_t","_te","tex","ext"]
Here is my solution:
split_string_to_kmers s k = split_string_to_kmers_helper s k []
where split_string_to_kmers_helper [] k acc = acc
split_string_to_kmers_helper s k acc
| length s >= k = split_string_to_kmers_helper (tail s) k (acc ++ [(take k s)])
| otherwise = acc
I am just wondering if there is a way to rewrite my code so it will be more haskell specific.
I guess this slightly different enough.
import Data.List (tails)
mySplit :: String -> Int -> [String]
mySplit str k = filter (\s -> length s == k) $ map (take k) (tails str)
You could make this more efficient by combining the filter and the map. But that is up to you.
Simple solution is next (not the same tail of list):
import Data.List.Split(chop)
splitRepN n = chop (\xs -> (take n xs,tail xs))
And we have next results:
> splitRepN 3 "some_text"
["som","ome","me_","e_t","_te","tex","ext","xt","t"]
And we cut short tails for full solution:
splitRepN' n = takeWhile ((== n). length) . splitRepN n
> splitRepN' 3 "some_text"
["som","ome","me_","e_t","_te","tex","ext"]
I need to be able to write a function that shows repeated words from a string and return a list of strings in order of its occurrence and ignore non-letters
e.g at hugs prompt
repetitions :: String -> [String]
repetitions > "My bag is is action packed packed."
output> ["is","packed"]
repetitions > "My name name name is Sean ."
output> ["name","name"]
repetitions > "Ade is into into technical drawing drawing ."
output> ["into","drawing"]
To split a string into words, use the words function (in the Prelude).
To eliminate non-word characters, filter with Data.Char.isAlphaNum.
Zip the list together with its tail to get adjacent pairs (x, y).
Fold the list, consing a new list that contains all x where x == y.
Someting like:
repetitions s = map fst . filter (uncurry (==)) . zip l $ tail l
where l = map (filter isAlphaNum) (words s)
I'm not sure that works, but it should give you a rough idea.
I am new to this language so my solution could be a kind of ugly in the eyes of an Haskell veteran, but anyway:
let repetitions x = concat (map tail (filter (\x -> (length x) > 1) (List.group (words (filter (\c -> (c >= 'a' && c <= 'z') || (c>='A' && c <= 'Z') || c==' ') x)))))
This part will remove all non letters and non spaces from a string s:
filter (\c -> (c >= 'a' && c <= 'z') || (c>='A' && c <= 'Z') || c==' ') s
This one will split a string s to words and group the same words to lists returning list of lists:
List.group (words s)
When this part will remove all lists with less than two elements:
filter (\x -> (length x) > 1) s
After what we will concatenate all lists to one removing one element from them though
concat (map tail s)
This might be inelegent, however it is conceptually very simple. I'm assuming that its looking for consecutive duplicate words like the examples.
-- a wrapper that allows you to give the input as a String
repititions :: String -> [String]
repititions s = repititionsLogic (words s)
-- dose the real work
repititionsLogic :: [String] -> [String]
repititionsLogic [] = []
repititionsLogic [a] = []
repititionsLogic (a:as)
| ((==) a (head as)) = a : repititionsLogic as
| otherwise = repititionsLogic as
Building on what Alexander Prokofyev answered:
repetitions x = concat (map tail (filter (\x -> (length x) > 1) (List.group (word (filter (\c -> (c >= 'a' && c <= 'z') || (c>='A' && c <= 'Z') || c==' ') x)))))
Remove unnecessary parenthesis:
repetitions x = concat (map tail (filter (\x -> length x > 1) (List.group (word (filter (\c -> c >= 'a' && c <= 'z' || c>='A' && c <= 'Z' || c==' ') x)))))
Use $ to remove more parenthesis (each $ can replace an opening parenthesis if the ending parenthesis is at the end of the expression):
repetitions x = concat $ map tail $ filter (\x -> length x > 1) $ List.group $ word $ filter (\c -> c >= 'a' && c <= 'z' || c>='A' && c <= 'Z' || c==' ') x
Replace character ranges with functions from Data.Char, merge concat and map:
repetitions x = concatMap tail $ filter (\x -> length x > 1) $ List.group $ word $ filter (\c -> isAlpha c || isSeparator c) x
Use a section and currying in points-free style to simplify (\x -> length x > 1) to ((>1) . length). This combines length with (>1) (a partially applied operator, or section) in a right-to-left pipeline.
repetitions x = concatMap tail $ filter ((>1) . length) $ List.group $ word $ filter (\c -> isAlpha c || isSeparator c) x
Eliminate explicit "x" variable to make overall expression points-free:
repetitions = concatMap tail . filter ((>1) . length) . List.group . word . filter (\c -> isAlpha c || isSeparator c)
Now the entire function, reading from right to left, is a pipeline that filters only alpha or separator characters, splits it into words, breaks it into groups, filters those groups with more than 1 element, and then reduces the remaining groups to the first element of each.