I'm supposed to split a string and return the substring that occurs before a passed character, but we're just starting Haskell, and it is like Chinese to me. I've been messing with it, but no luck.
Here's what I have so far:
--spanString returns substring of string s before char c
spanString (c, [s])::(c, [s]) -> []
spanString (c, a:b) =
let (x, y) = spanString (c, b)
in
if a < c then (a:x,y)
else (x, a:y)
What am I messing up?
First of all, your type signature is completely messed up. It must either be absent or be of the form spanString :: <some type>. Even if we ignore the (c, [s]) standing before the double colon, the rest is still something strange. One can read it as "a function taking values of type (c, [s]) to values of type [] for any c and s" (c and s are type variables). First, there is no type [] in Haskell. There is not going be a list type without its element type. Next, we can't work with any c and s. We must be able to compare them, right?
Actually, let's avoid using polymorphism for now and specify exactly which types we want. We want a character and a list of characters, packed up into a tuple for some reason: (Char, [Char]). Note that Char starts with a capital letter, which means it's not a type variable, but rather a concrete type. What about our result type? If you trust the problem description, you need to return a list of characters ([Char]), but if you look at the code, it obviously returns tuples of lists (([Char], [Char])). Okay, maybe the second list is useful, let's leave it for now:
spanString :: (Char, [Char]) -> ([Char], [Char])`
Now your code compiles.
However, when run, it crashes with exception: Non-exhaustive patterns in function spanString. This is because you don't handle the case when the passed list is empty. If you do that, by adding an equation like
spanString (_, []) = ([], [])
, your function runs well, but now let's look at what it does. It turns out you have a function for list partitioning: it returns all characters of the given string less than c as the first element of the tuple and all other characters as the second element. Seems like a bug to me (you've implemented a completely different function!).
Err, quite a lot.
First, your type declaration is wrong. Haskell uses upper case names for types, and it doesn't pass parameters in brackets like most languages do. We write
y = sin x
instead of
y = sin (x)
You probably want something like
spanString :: Char -> String -> String
Your definition of spanString is syntactically right, but still wrong. Think about it this way: if first character doesn't match then you want to spanString the rest of the string and then return the result with the first character prepended. If the first character does match then you want to return "".
Your type definition is wrong .
spanString :: Char-> String-> String
spanString _ [] = []
spanString c (x:xs) | c==x = []
| otherwise = x:spanString c xs
Just for information, utility functions like this can almost always be found in Prelude, or one of the standard libraries. In this case, takeWhile will help:
spanString :: (Char, String) -> String
spanString (c, s) = takeWhile (/= c) s
(i.e., keep taking characters while they don't equal c).
Passing arguments in a tuple is slightly odd, but if that's what's required then so be it.
Related
I have an exercise that asks for a function that converts all characters of a string to uppercase using
System.Char.ToUpper
So first I changed the string to a char array and changed the array into a list of chars
let x = s.ToCharArray()
List.ofArray x
Next, I thought I would use List.iter to iterate through my list and use the System.Char.ToUpper function on each character.
List.iter (fun z -> (System.Char.ToUpper(z)))
This is not working however. I get an error 'The expression was supposed to have unit but here has char.' What am I doing wrong? Is it a flaw in logic or syntax?
This needs some unpacking.
First, your core mistake: System.Char.ToUpper is a function. It takes a char and returns another char. It doesn't somehow "update" its argument to a new value.
let x = 'a'
let y = System.Char.ToUpper x // y = 'A', x = 'a'.
In the above code, I give name y to the result of the function. The value of y is 'A', but the value of x is still 'a'. After calling the function, x hasn't changed.
From this mistake, all the rest follows.
Second, List.iter is a function that, for every element of a list, makes something "happen". It doesn't replace each element of a list with something new, nor does it create a new list. It just makes something happen for every element. The simplest example of such "something" is printing out to console:
List.iter (fun x -> printfn "%i" x) [1; 2; 3] // Prints "1", then "2", then "3"
Notice that this function takes two arguments: the function that represents the something that need to happen, and the list from which to take the elements. In your question, you seem to be missing the second argument. How would List.iter know which list to use?
The first argument of List.iter needs to be a function that returns unit. This is a special type in F# that basically means "no value". When a function returns no value, it means that the only reason for calling it was to make something external happen (known in functional programming as "side-effect"). This is why List.iter requires the function to return unit - it's extra protection from accidentally supplying wrong function, just as you did, actually: the function you provided returns char. This is why you receive the error that you receive.
Third, just like with ToUpper, calling List.ofArray doesn't somehow "update" x to be a list. Instead, it returns a list. If you don't give that returned list a name, it will just be lost. Which means that the way you're calling List.ofArray is futile.
What you actually need is to (1) take the sequence of characters in your string, then (2) convert it to a new sequence where each character is upper case, then (3) glue those characters back together to get a new string.
Step (1) is a no-op, because .NET strings are already sequences of chars (i.e. they implement IEnumerable<char>). Step (2) is accomplished via a common operation called Seq.map. It's an operation that converts a sequence to a new sequence by applying given function to every element. The "given function" in this case will be System.Char.ToUpper. Step (3) can be accomplished via String.concat, but you'd need to convert each char to a string first, because String.concat takes a sequence of strings, not chars.
let chars = s
let upperChars = Seq.map System.Char.ToUpper chars
let strChars = Seq.map string upperChars
let result = String.concat "" strChars
Or this can be done in a shorter way, without giving each step's result a separate name, but by piping each result straight into the next operation:
let result =
s
|> Seq.map System.Char.ToUpper
|> Seq.map string
|> String.concat ""
And finally, there is actually a much shorter way to do it, but it's so ridiculously obvious, it feels like cheating.
The thing is, because strings are sequences, it kinda makes sense for them to have all the sequence operations. And guess what? They do! Specifically, there is a function String.map, which does the same thing as Seq.map, but for strings:
let result = String.map System.Char.ToUpper s
I am trying to split a string in to a list of characters and removing any non-letters in the process. Here is my code so far:
getCharList :: String -> [String]
getCharList x = [filter isLetter c | c <- splitOn "" x, c /= ""]
The output I receive from, for example:
getCharList "Why doesn't this work"
is:
["W","h","y","","d","o","e","s","n","","t","","t","h","i","s","","w","o","r","k"]
Could someone please explain why this doesn't seem to be able to remove the empty characters?
For your direct question: I will simply point out that filter isLetter c may be the empty list even when c is not the empty list; hence c /= "" does not ensure filter isLetter c /= "".
Popping up a level, I find your existing code to have a few oddities. The main thing you should realize is that String is exactly the same type as [Char] (it is just another name for it). There's no need to do any processing to convert a String into a list of its elements -- splitOn "" is basically unneeded work. The literal "foo" is just shorthand for the literal ['f', 'o', 'o'] (try them both in ghci!). I therefore propose a complete rewrite of your function, along the following lines:
getLetters :: String -> [Char]
getLetters = filter isLetter
(I used [Char] instead of String in the return type to emphasize that I intend to think of the returned thing as a list of letters rather than as a reasonable string. This is a human-level difference only; GHC would be just as happy to call the returned value a String.) This is perfectly idiomatic. If you prefer, you can also include the argument, writing instead:
getLetters s = filter isLetter s
I try to define function with the following protocol:
[(1,2), (6,5), (9,10)] -> [3, 11, 19]
Here is what I have now:
fun sum_pairs (l : (int * int) list) =
if null l
then []
else (#1 hd(l)) + (#2 hd(l))::sum_pairs(tl(l))
According to type checker I have some type mismatch, but I can't figure out where exactly I'm wrong.
This code runs in PolyML 5.2:
fun sum_pairs (l : (int * int) list) =
if null l
then []
else ((#1 (hd l)) + (#2 (hd l))) :: sum_pairs(tl l)
(* ------------^-------------^ *)
The difference from yours is subtle, but significant: (#1 hd(l)) is different from (#1 (hd l)); the former doesn't do what you think - it attempts to extract the first tuple field of hd, which is a function!
While we're at it, why don't we attempt to rewrite the function to make it a bit more idiomatic? For starters, we can eliminate the if expression and the clunky tuple extraction by matching on the argument in the function head, like so:
fun sum_pairs [] = []
| sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest)
We've split the function into two clauses, the first one matching the empty list (the recursive base case), and the second one matching a nonempty list. As you can see, this significantly simplified the function and, in my opinion, made it considerably easier to read.
As it turns out, applying a function to the elements of a list to generate a new list is an incredibly common pattern. The basis library provides a builtin function called map to aid us in this task:
fun sum_pairs l = map (fn (a, b) => a + b) l
Here I'm using an anonymous function to add the pairs together. But we can do even better! By exploiting currying we can simply define the function as:
val sum_pairs = map (fn (a, b) => a + b)
The function map is curried so that applying it to a function returns a new function that accepts a list - in this case, a list of integer pairs.
But wait a minute! It looks like this anonymous function is just applying the addition operator to its arguments! Indeed it is. Let's get rid of that too:
val sum_pairs = map op+
Here, op+ denotes a builtin function that applies the addition operator, much like our function literal (above) did.
Edit: Answers to the follow-up questions:
What about arguments types. It looks like you've completely eliminate argument list in the function definition (header). Is it true or I've missed something?
Usually the compiler is able to infer the types from context. For instance, given the following function:
fun add (a, b) = a + b
The compiler can easily infer the type int * int -> int, as the arguments are involved in an addition (if you want real, you have to say so).
Could you explain what is happening here sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest). Sorry for may be dummy question, but I just want to fully understand it. Especially what = means in this context and what order of evaluation of this expression?
Here we're defining a function in two clauses. The first clause, sum_pairs [] = [], matches an empty list and returns an empty list. The second one, sum_pairs ((a, b)::rest) = ..., matches a list beginning with a pair. When you're new to functional programming, this might look like magic. But to illustrate what's going on, we could rewrite the clausal definition using case, as follows:
fun sum_pairs l =
case l of
[] => []
| ((a, b)::rest) => (a + b)::sum_pairs(rest)
The clauses will be tried in order, until one matches. If no clause matches, a Match expression is raised. For example, if you omitted the first clause, the function would always fail because l will eventually be the empty list (either it's empty from the beginning, or we've recursed all the way to the end).
As for the equals sign, it means the same thing as in any other function definition. It separates the arguments of the function from the function body. As for evaluation order, the most important observation is that sum_pairs(rest) must happen before the cons (::), so the function is not tail recursive.
Here's what I've got so far...
fun positive l1 = positive(l1,[],[])
| positive (l1, p, n) =
if hd(l1) < 0
then positive(tl(l1), p, n # [hd(l1])
else if hd(l1) >= 0
then positive(tl(l1), p # [hd(l1)], n)
else if null (h1(l1))
then p
Yes, this is for my educational purposes. I'm taking an ML class in college and we had to write a program that would return the biggest integer in a list and I want to go above and beyond that to see if I can remove the positives from it as well.
Also, if possible, can anyone point me to a decent ML book or primer? Our class text doesn't explain things well at all.
You fail to mention that your code doesn't type.
Your first function clause just has the variable l1, which is used in the recursive. However here it is used as the first element of the triple, which is given as the argument. This doesn't really go hand in hand with the Hindley–Milner type system that SML uses. This is perhaps better seen by the following informal thoughts:
Lets start by assuming that l1 has the type 'a, and thus the function must take arguments of that type and return something unknown 'a -> .... However on the right hand side you create an argument (l1, [], []) which must have the type 'a * 'b list * 'c list. But since it is passed as an argument to the function, that must also mean that 'a is equal to 'a * 'b list * 'c list, which clearly is not the case.
Clearly this was not your original intent. It seems that your intent was to have a function that takes an list as argument, and then at the same time have a recursive helper function, which takes two extra accumulation arguments, namely a list of positive and negative numbers in the original list.
To do this, you at least need to give your helper function another name, such that its definition won't rebind the definition of the original function.
Then you have some options, as to which scope this helper function should be in. In general if it doesn't make any sense to be calling this helper function other than from the "main" function, then it should not be places in a scope outside the "main" function. This can be done using a let binding like this:
fun positive xs =
let
fun positive' ys p n = ...
in
positive' xs [] []
end
This way the helper function positives' can't be called outside of the positive function.
With this take care of there are some more issues with your original code.
Since you are only returning the list of positive integers, there is no need to keep track of the
negative ones.
You should be using pattern matching to decompose the list elements. This way you eliminate the
use of taking the head and tail of the list, and also the need to verify whether there actually is
a head and tail in the list.
fun foo [] = ... (* input list is empty *)
| foo (x::xs) = ... (* x is now the head, and xs is the tail *)
You should not use the append operator (#), whenever you can avoid it (which you always can).
The problem is that it has a terrible running time when you have a huge list on the left hand
side and a small list on the right hand side (which is often the case for the right hand side, as
it is mostly used to append a single element). Thus it should in general be considered bad
practice to use it.
However there exists a very simple solution to this, which is to always concatenate the element
in front of the list (constructing the list in reverse order), and then just reversing the list
when returning it as the last thing (making it in expected order):
fun foo [] acc = rev acc
| foo (x::xs) acc = foo xs (x::acc)
Given these small notes, we end up with a function that looks something like this
fun positive xs =
let
fun positive' [] p = rev p
| positive' (y::ys) p =
if y < 0 then
positive' ys p
else
positive' ys (y :: p)
in
positive' xs []
end
Have you learned about List.filter? It might be appropriate here - it takes a function (which is a predicate) of type 'a -> bool and a list of type 'a list, and returns a list consisting of only the elements for which the predicate evaluates to true. For example:
List.filter (fn x => Real.>= (x, 0.0)) [1.0, 4.5, ~3.4, 42.0, ~9.0]
Your existing code won't work because you're comparing to integers using the intversion of <. The code hd(l1) < 0 will work over a list of int, not a list of real. Numeric literals are not automatically coerced by Standard ML. One must explicitly write 0.0, and use Real.< (hd(l1), 0.0) for your test.
If you don't want to use filter from the standard library, you could consider how one might implement filter yourself. Here's one way:
fun filter f [] = []
| filter f (h::t) =
if f h
then h :: filter f t
else filter f t
I have a list of lists like so:
[["BBBBBBBB",
"BWFFFPFGB",
"BWFFFPFGB",
"BWFFMPFGB",
"BWFFFPF_B",
"BWFFFPF6B",
"BBBBBBB"]]
I've done a little research and have found out how to access individual elements using the !! operator. But when it comes to searching for a certain element 'M' I'm not sure how to go about that. My friend said I need to use something like (x:xs):xss on a list, but when I try this in the WinGHCi haskell program I get this.
Prelude> let list = [["BBBBBBBB",
"BWFFFPFGB",
"BWFFFPFGB",
"BWFFMPFGB",
"BWFFFPF_B",
"BWFFFPF6B",
"BBBBBBB"]]
Prelude> head(x:xs):xss
<interactive>:192:2: Not in scope: `x'
<interactive>:192:4: Not in scope: `xs'
<interactive>:192:8: Not in scope: `xss'
I understand that I declare the name as list and not x:xs but even when I declare it as x:xs I still get the errors. I'm probably still a little new to haskell to really understand what to do so I may be going about this way wrong.
I've looked here Replace individual list elements in Haskell? because eventually I want to replace the M with something different but I'm not completely sure how I would implement that.
Any help/guidance is appreciated, thanks!
First let's see how to replace a W with M
charWM :: Char -> Char
charWM 'W' = 'M' -- If you see W, put M.
charWM x = x -- If you see anything else, put it back as is.
You can rewrite that function how you like by adding other letter transformations.
Now let's make that work over a list. There's a great function map :: (a ->b) -> [a] -> [b] that lets you apply a function on every element on a list.
stringWM :: String -> String
stringWM xs = map charWM xs -- do charWM to everything in xs.
For example stringWM "QWERTY WILL WIN" = "QMERTY MILL MIN"
Next we can do that to a list of lists:
lolWM :: [String] -> [String]
lolWM xss = map stringWM xss
(String is a type synonym for [Char].)
Let's test that out in ghci:
*Main> list'
["BBBBBBBB","BWFFFPFGB","BWFFFPFGB","BWFFMPFGB","BWFFFPF_B","BWFFFPF6B","BBBBBBB"]
*Main> lolWM list'
["BBBBBBBB","BMFFFPFGB","BMFFFPFGB","BMFFMPFGB","BMFFFPF_B","BMFFFPF6B","BBBBBBB"]
All good.
Your example wasn't exactly list', it was [list'] which has 1 element, so to work on that we'd need to map lolWM. Often we wouldn't bother writing stringWM or lolWM and go directly to lists of lists of lists, if that's what we needed:
lololWM = (map.map.map) charWM
map.map.map means map the map of the map. You can allow that to blow your mind a little, or you can just say list of list of list of Char, so map map map - one map per list level.
In the future, maybe you'll want to replace W with Strings instead of characters.
rewriteChar :: Char -> String
rewriteChar 'W' = "--^--"
rewriteChar x = [x] -- put x in a list to make it a string
This time, map isn't enough: map rewriteChar "QWERTY WILL WIN" gives
["Q","--^--","E","R","T","Y"," ","--^--","I","L","L"," ","--^--","I","N"]
We could use concat on that to flatten it into a single list, but it's more fun to do
rewriteString = concatMap rewriteChar
So now rewriteString "QWERTY WILL WIN" give us "Q--^--ERTY --^--ILL --^--IN".
For more mindblowing things to try, there's "QWERTY WILL WIN" >>= rewriteChar and "Hello Mum" >>= \x -> [x,x,x]
First of all, virtually all "variables" in Haskell are immutable, so there's no "changing a list", there are modified copies.
Second, you need to find an element by some criteria. To do that, you need to traverse a list. - This can be done using recursion. Filtering can be done using a function passed as an argument of your traversing function (this function must take an element and return a boolean value).
Try to put the above together and make your own function. Start with a type signature, it shows what you want to do: to take a list of Char (it's better to generalize to a generic type) and a function which possibly changes an element and return a modified list:
replaceFunc :: (Char -> Char) -> String -> String
Also, read http://www.haskell.org/haskellwiki/How_to_work_on_lists , there's a hint there how to apply some function to specific elements only.