So I tried the following code in haskell where I try to detect if the user has entered a "no" or "No" in the string. Also I tried replacing [[Char]] with Strings but it gives compilation errors.
wantGifts :: [[Char]] -> [[Char]]
wantGifts st = [if (x == "No" || x== "no") then "No gifts given" else "But why" | x <- st, x == head st]
The above code compiles but when I pass a string to it, it returns an error message:
*Main> wantGifts "no I dont"
<interactive>:8:11:
Couldn't match type ‘Char’ with ‘[Char]’
Expected type: [[Char]]
Actual type: [Char]
In the first argument of ‘wantGifts’, namely ‘"no I dont"’
In the expression: wantGifts "no I dont"
In an equation for ‘it’: it = wantGifts "no I dont"
Look closely at the type of wantGifts, it requires a List of List of Chars. But "no I dont" is of type String which is just [Char]. With your current construction, you have to use:
wantGifts ["no I dont"]
There are several ways to improve this, best is to use Text.
import Data.Text (Text)
import qualified Data.Text as T
wantGifts :: Text -> Text
wantGifts txt = if (T.isInfixOf "no" . T.toLower) txt then "No gifts given" else "But why"
You have defined wantGifts as taking a list of strings. [[Char]] is equivalent to [String]. In the REPL, you are passing it a single string.
If you instead did this, it would compile:
wantGifts ["no I dont"]
However, I have a hunch this isn't what you want.
If you were trying to detect whether the word "no" was anywhere in the string, you could use the words function:
containsNo :: String -> Bool
containsNo = any (\w -> w == "no" || w == "No") . words
Related
Im trying to replace all strings which contain a substring by itself, in a list.
I've tried it by using the map function:
cleanUpChars = map(\w -> if isInfixOf "**" w then map(\c -> if c == "*" then ""; else c); else w)
To me this reads as: map elements in a list, such that if a character of a word contains * replace it with nothing
To Haskell: "Couldnt match expected type [[Char]] -> [[Char]] with actual type [Char] in the expression: w" (and the last w is underlined)
Any help is appreciated
To answer the revised question (when isInfixOf has been imported correctly):
cleanUpChars = map(\w -> if isInfixOf "**" w then map(\c -> if c == "*" then ""; else c); else w)
The most obvious thing wrong here is that c in the inner parentheses will be a Char (since it's the input to a function which is mapped over a String) - and characters use single quotes, not double quotes. This isn't just a case of a typo or wrong syntax, however - "" works fine as an empty string (and is equivalent to [] since Strings are just lists), but there is no such thing as an "empty character".
If, as it seems, your aim is to remove all *s from each string in the list that contains **, then the right tool is filter rather than map:
Prelude Data.List> cleanUpChars = map(\w -> if isInfixOf "**" w then filter (/= '*') w; else w)
Prelude Data.List> cleanUpChars ["th**is", "is", "a*", "t**es*t"]
["this","is","a*","test"]
(Note that in the example I made up, it removes all asterisks from t**es*t, even the single one. This may not be what you actually wanted, but it's what your logic in the faulty version implied - you'll have to be a little more sophisticated to only remove pairs of consecutive *'s.)
PS I would certainly never write the function like that, with the semicolon - it really doesn't gain you anything. I would also use the infix form of isInfixOf, which makes it much clearer which string you are looking for inside the other:
cleanUpChars :: [String] -> [String]
cleanUpChars = map (\w -> if "**" `isInfixOf` w then filter (/= '*') w else w)
I'm still not particularly happy with that for readability - there's probably some nice way to tidy it up that I'm overlooking for now. But even if not, it helps readability imo to give the function a local name (hopefully you can come up with a more concise name than my version!):
cleanUpChars :: [String] -> [String]
cleanUpChars = map possiblyRemoveAsterisks
where possiblyRemoveAsterisks w = if "**" `isInfixOf` w then filter (/= '*') w else w
There is a regular expression matching quoted substrings: "/\"(?:[^\"\\]|\\.)*\"/" (originally /"(?:[^"\\]|\\.)*"/, see Here). Tested on regex101, it works.
With TDFA, it's syntax:
*** Exception: Explict error in module Text.Regex.TDFA.String : Text.Regex.TDFA.String died:
parseRegex for Text.Regex.TDFA.String failed:"/"(?:[^"\]|\.)*"/" (line 1, column 4):
unexpected "?"
expecting empty () or anchor ^ or $ or an atom
Is there a way co correct it?
Test string: Is big "problem", no?
Expected result: "problem"
UPD:
This is full context:
removeQuotedSubstrings :: String -> [String]
removeQuotedSubstrings str =
let quoteds = concat (str =~ ("/\"(?:[^\"\\]|\\.)*\"/" :: String) :: [[String]])
in quoteds
No improvement, just an acceptable solution, albeit lacking in elegance:
import qualified Data.Text as T
import Text.Regex.TDFA
-- | Removes all double quoted substrings, if any, from a string.
--
-- Examples:
--
-- >>> removeQuotedSubstrings "alfa"
-- "alfa"
-- >>> removeQuotedSubstrings "ngoro\"dup\"lai \"ming\""
-- "ngoro lai "
removeQuotedSubstrings :: String -> String
removeQuotedSubstrings str =
let quoteds = filter (('"' ==) . head)
$ concat (str =~ ("\"(\\.|[^\"\\])*\"" :: String) :: [[String]])
in T.unpack $ foldr (\quoted acc -> T.replace (T.pack quoted) " " acc)
(T.pack str) quoteds
Yes, the final purpose has always been to remove the quoted substrings.
input file is txt :
000011S\n
0001110\n
001G111\n
0001000\n
Result is:
[["0","0","0","0","1","1","S"], ["0","0","0","1","1","1","0"] [...]]
Read a text file with
file <- openFile nameFile ReadMode
and the final output
[["a","1","0","b"],["d","o","t","2"]]
is a map with list of char
try to:
convert x = map (map read . words) $ lines x
but return [[string ]]
As it could do to return the output I want? [[Char]],
is there any equivalent for word but for char?
one solution
convert :: String -> [[String]]
convert = map (map return) . lines
should do the trick
remark
the return here is a neat trick to write \c -> [c] - wrapping a Char into a singleton list as lists are a monad
how it works
Let me try to explain this:
lines will split the input into lines: [String] which each element in this list being one line
the outer map (...) . lines will then apply the function in (...) to each of this lines
the function inside: map return will again map each character of a line (remember: a String is just a list of Char) and will so apply return to each of this characters
now return here will just take a character and put it into a singleton list: 'a' -> [a] = "a" which is exactly what you wanted
your example
Prelude> convert "000011S\n0001110\n001G111\n0001000\n"
[["0","0","0","0","1","1","S"]
,["0","0","0","1","1","1","0"]
,["0","0","1","G","1","1","1"]
,["0","0","0","1","0","0","0"]]
concerning your comment
if you expect convert :: String -> [[Char]] (which is just String -> [String] then all you need is convert = lines!
[[Char]] == [String]
Prelude> map (map head) [["a","1","0","b"],["d","o","t","2"]]
["a10b","dot2"]
will fail for empty Strings though.
or map concat [[...]]
Lets say I have a list of type integer [blah;blah;blah;...] and i don't know the size of the lis and I want to pattern match and not print the first element of the list. Is there any way to do this without using a if else case or having a syntax error?
because all i'm trying to do is parse a file tha looks like a/path/to/blah/blah/../file.c
and only print the path/to/blah/blah
for example, can it be done like this?
let out x = Printf.printf " %s \n" x
let _ = try
while true do
let line = input_line stdin in
...
let rec f (xpath: string list) : ( string list ) =
begin match Str.split (Str.regexp "/") xpath with
| _::rest -> out (String.concat "/" _::xpath);
| _ -> ()
end
but if i do this i have a syntax error at the line of String.concat!!
String.concat "/" _::xpath doesn't mean anything because _ is pattern but not a value. _ can be used in the left part of a pattern matching but not in the right part.
What you want to do is String.concat "/" rest.
Even if _::xpath were correct, String.concat "/" _::xpath would be interpreted as (String.concat "/" _)::xpath whereas you want it to be interpreted as String.concat "/" (_::xpath).
I'm following an introductory course on functional programming, where we use Haskell.
Part of an excercise is to write a parser for the input string.
However I can't solve the following error, or get what is actually happening.
Parser.hs:29:71:
Couldn't match expected type `String' with actual type `Char'
In the first argument of `readPoint', namely `start'
In the expression: readPoint start
In the expression:
(readLines track, readPoint start, readLine finish)
The error originates from this line:
readTrack str = parseTrack (lines str) where
parseTrack (start : finish : track) = (readLines track, readPoint start, readLine finish)
What I expected to happen is that the input string got split into a list of lines, which get passed to parseTrack.
parseTrack would then use pattern matching to name the top two strings(lines) from the list and the rest.
However what I believe is happening is that finish is the top element from the list, and start gets assigned the top char from that string.
I would really like to know how to solve this problem and what is actually happening.
Thanks a lot!
Parser.hs
module Parser where
import Types
readFloat :: String -> Float
readFloat str = case reads str of
[] -> error "not a floating point number"
(p,_):_ -> p
readInt :: String -> Int
readInt str = case reads str of
[] -> error "not an integer"
(p,_):_ -> p
readPoint :: String -> Point
readPoint str = parsePoint (words str) where
parsePoint (x : y : _) = (readInt x, readInt y)
readLine :: String -> Line
readLine str = parseLine (words str) where
parseLine (x1 : y1 : x2 : y2 : _) = ((readInt x1, readInt y1), (readInt x2, readInt y2))
readLines :: String -> [Line]
readLines str = parseLines (lines str) where
parseLines (line : rest) = readLine line : parseLines rest
readTrack :: String -> Track
readTrack str = parseTrack (lines str) where
parseTrack (start : finish : track) = (readLines track, readPoint start, readLine finish)
Types.hs
module Types where
type Vector2D = (Int, Int)
type Point = Vector2D
type Line = (Point, Point)
type Velocity = Vector2D
type CarState = (Position, Velocity)
type Position = Vector2D
type Trace = [Position]
type Track = ([Line], Point, Line)
Your variable track was actually a list of single lines, not a string with '\n's in it. Since you've already split it into lines, you can just map readLine over it, giving:
readTrack str = parseTrack (lines str) where
parseTrack (start:finish:tracks)
= (map readLine tracks, readPoint start, readLine finish)
Here tracks :: [String], which is why you can map readLine on them all - you don't need to use readLines to split it into lines first. (You can tell it's a list because it's the final thing to the right hand side of a :.)
You say
However what I believe is happening is that finish is the top element from the list, and start gets assigned the top char from that string.
Well what was happening was: because you asked for readLines track as the first output, Haskell started there, and since you declared
readLines :: String -> [Line]
that meant that track had to be a String - that's the only thing readLines can deal with.
First, you need to remember that : has an element on its left and a list on its right, so in
3:4:stuff
stuff has to be [Integer] because it's on the right of some Integer elements. Similarly,
c:"a string"
means c has to be a Char because String = [Char].
In your code, we've worked out that track is a String, so that means that when you write
(start : finish : track)
both start and finish have to be elements you can put at the front of a String, so both start and finish have to be Char.
Haskell then looks at your code readPoint start, but because it's worked out that start has type Char, but
readPoint :: String -> Point
it complains that Char and String don't match.
I think you made the mistake because you forgot that readLines takes a single string, but it felt (from the name) like it should happily take a list of strings. Your parseLines looks like it does a similar thing, but it takes a list of strings, so copes, whereas readLines takes a single string with newline characters, so can't cope with a list.
UPD. Oh, sorry, I didn't get that track means multiple tracks and has to be of type [String]. So the answer from AndrewC fits more.
Since in haskell the pattern (x:xs) means that if x has type a then xs has to be of type [a] your pattern in parseTrack means in types smth like (a : a : [a]).
Compiler wanna evaluate type a and first what it see on the right is readLines track. Func readLines has type String -> [Line] so compiler has track as String which means [a] is of type String. Also in haskell String is [Char] so a is Char.
But you need a as String. So you just need to take first three strings and throw out the rest tail of [String]. In types it'll mean smth like (String : String : String : [String]). For it you can rewrite matching pattern in parseTrack to:
parseTrack (start : finish : track : _)