I have a question regarding Haskell that's been stumping my brain. I'm currently required to write a function that removes a string i.e. "word" from a list of strings ["hi", "today", "word", "Word", "WORD"] returns the list ["hi", "today", "Word", "WORD"]. I cannot use any higher-order functions and can only resort to primitive recursion.
Thinking about the problem, I thought maybe I could solve it by using a recursion where you search the head of the first string, if it matches "w" then compare the next head from the tail, and see if that matches "o". But then I soon realized that after all that work, you wouldn't be able to delete the complete string "word".
My question really being how do I compare a whole string in a list rather than only comparing 1 element at a time with something like: removeWord (x:xs). Is it even possible? Do I have to write a helper function to aid in the solution?
Consider the base case: removing a word from an empty list will be the empty list. This can be trivially written like so:
removeWord [] _ = []
Now consider the case where the list is not empty. You match this with x:xs. You can use a guard to select between these two conditions:
x is the word you want to remove. (x == word)
x is not the word you want to remove. (otherwise)
You don't need a helper function, though you could write one if you wanted to. You've basically got 3 conditions:
You get an empty list.
You get a list whose first element is the one you want to remove.
You get a list whose first element is anything else.
In other languages, you would do this with a set of if-else statements, or with a case statement, or a cond. In Haskell, you can do this with guards:
remove_word_recursive:: String -> [String] -> [String]
remove_word_recursive _ [] = []
remove_word_recursive test_word (x:xs) | test_word == x = what in this case?
remove_word_recursive test_word (x:xs) = what in default case?
Fill in the correct result for this function in these two conditions, and you should be done.
I think what you're looking for is a special case of the function sought for this question on string filters: Haskell - filter string list based on some conditions . Reading some of the discussion on the accepted answer might help you understand more of Haskell.
Since you want to remove a list element, it's easy to do it with List Comprehension.
myList = ["hi", "today", "word", "Word", "WORD"]
[x | x <- myList, x /= "word"]
The result is:
["hi","today","Word","WORD"]
If isInfixOf is not considered as higher order, then
import Data.List (isInfixOf)
filter (not . isInfixOf "word") ["hi", "today", "word", "Word", "WORD"]
Related
So i am currently working with Haskell at my college, but kinda struggling with pattern-matching and to be more specific i'll give the program, that i am to solve:
My function is awaiting a list of lists ( with each list containing at least 3 elements ) and in each list the second element is replaced with the number of the third element.
Example:
[[1,2,3],[4,5,6,7],[8,9,10]] should become [[1,3,3],[4,6,6,7],[8,10,10]
So far I've made the following:
f [] = []
f [[x]] = []
f [[x,y]] = []
f [[x,y,z]] = [[x,z,z]]
f ([x,y,z]:xs) = [x,z,z]:f(xs)
My questions are:
How can i identify, that some lists may contain more than 3 elements and that the list must remain the same, only the 2nd element changes.
How can i make the recursion, so that it handles exceptions (for example the first list has 4 elements).
Thank you in advance!
It may help to first write the function that swaps the second value in a list with the third.
swap (x:y:z:rest) = x:z:z:rest
swap xs = xs
In the above, x:y:z:rest matches a list of at least length 3. The value of rest, since it is at the end of the pattern, will match any kind of list, both empty and full. The second pattern match of xs is a catch-all for any other type of list, which will just return the same list if it is less than 3 items long.
From there, you can write f in terms of mapping over the outer list and applying swap:
f = map swap
When I use sub_string("abberr","habberyry") , it returns True, when obviously it should be False. The point of the function is to search for the first argument within the second one. Any ideas what's wrong?
sub_string :: (String, String) -> Bool
sub_string(_,[]) = False
sub_string([],_) = True
sub_string(a:x,b:y) | a /= b = sub_string(a:x,y)
| otherwise = sub_string(x,y)
Let me give you hints on why it's not working:
your function consumes "abber" and "habber" of the input stings on the initial phase.
Now "r" and "yry" is left.
And "r" is a subset of "yry". So it returns True. To illustrate a more simple example of your problem:
*Main> sub_string("rz","rwzf")
True
First off, you need to switch your first two lines. _ will match [] and this will matter when you're matching, say, substring "abc" "abc". Secondly, it is idiomatic Haskell to write a function with two arguments instead of one with a pair argument. So your code should start out:
substring :: String -> String -> Bool
substring [] _ = True
substring _ [] = False
substring needle (h : aystack)
| ...
Now we get to the tricky case where both of these lists are not empty. Here's the problem with recursing on substring as bs: you'll get results like "abc" being a substring of "axbxcx" (because "abc" will match 'a' first, then will look for "bc" in the rest of the string; the substring algorithm will then skip past the 'x' to look for "bc" in "bxcx", which will match 'b' and look for "c" in "xcx", which will return True.
Instead your condition needs to be more thorough. If you're willing to use functions from Data.List this is:
| isPrefixOf needle (h : aystack) = True
| otherwise = substring needle aystack
Otherwise you need to write your own isPrefixOf, for example:
isPrefixOf needle haystack = needle == take (length needle) haystack
As Sibi already pointed out, your function tests for subsequence. Review the previous exercise, it is probably isPrefixof (hackage documentation), which is just a fancy way of saying startsWith, which looks very similar to the function you wrote.
If that is not the previous exercise, do that now!
Then write sub_string in terms of isPrefixOf:
sub_string (x, b:y) = isPrefixOf ... ?? ???
Fill in the dots and "?" yourself.
I am new to SML programming and I have a problem to create a function to remove occurrences of an atom A from a list of integers. This list can be nested to any levels,
means we can have list like [1,2,3] and we can have list like [[1,2],[2,3]] as well as list like [[[1,2],[1,2]],[[2,3],[2,3]]].
So my problem is how can I check if the given item is a list or an atom as I have not found any such function in SMLNJ so far?
I have created a function that checks if the list is empty or not and then it calls a helper iterative function to check if the head of the list is a list or an atom. If it's an atom then replace it with another atom and continue with the rest of the tail.
Inside the helper function if I check that tail of the head of list is empty then it gives an error as tail function can have a list only.
So I have to do it like
tl([hd(a)), and if I do that, then it will always be empty.
If I apply it on the first list I get head as 1 and wrapping it in [] results in [1], so tail of this will be []. Same way if I get head of second list it will be [1,2] and wrapping it in [] will result in [[1,2]], so tail of this is again [].
So is there any way how I can check if the given item is an atom or again a list?
Thanks in advance for all responses.
"This list can be nested to any levels" is not possible in SML, because it's statically typed, and a list type has a specific element type. You either have an int list, which is a list whose elements are all int, or int list list, which is a list whose elements are all int list. You can't have a mixture.
The closest to what you are talking about would be to make an algebraic datatype with two cases, a leaf, or a nested list of elements of this datatype again. Then you can use pattern matching to deconstruct this datatype.
As stated in the other answer:
You could define your own data type
datatype 'a AtomList = Atom of 'a | List of 'a AtomList list
Then, with this data type you could define all the atom lists you mentioned above:
val x = List([Atom(1),Atom(2),Atom(3)])
val y = List([List([Atom(1),Atom(2)]),List([Atom(3),Atom(4)])])
val z = List([
List([
List([Atom(1),Atom(2)]),
List([Atom(1),Atom(2)]),
List([
List([Atom(2), Atom(3)]),
List([Atom(2), Atom(3)])
])
])
])
Then to go over your atom list, you would use pattern matching, as in:
fun show xs =
case xs of
Atom(x) => (*do something with atom*)
| List(ys) => (*do something with list of atoms *)
I'm fairly new to Haskell and as input I want to take an array of string for example as
["HEY" "I'LL" "BE" "RIGHT" "BACK"] and look for lets say "BE" "RIGHT" "BACK" and replace it with a different word, lets say "CHEESE". I have a function made for single words but I want this to work if a string contains a certain phrase to replace it with a word. Oh and I don't want to use external libraries.
Code:
replace :: [String] -> [String]
replace [] = []
replace (h:t)
| h == "WORD" = "REPLACED" : replace t
| otherwise = h : replace t
What you have now could also be implemented as
replace ("WORD":rest) = "REPLACED" : replace rest
replace (x:rest) = x : replace rest
replace [] = []
And this could be extended to your example as
replace ("BE":"RIGHT":"BACK":rest) = "CHEESE" : replace rest
replace (x:rest) = x : replace rest
replace [] = []
But obviously this is not a great way to write it. We'd like a more general solution where we can pass in a phrase (or sub-list) to replace. To start with we know the following things:
Input is a list of n elements (decreases as we recurse)
Phrase is a list of m elements (stays constant as we recurse)
If m > n, we definitely don't have a match
If m <= n, we might have a match
If we don't have a match, keep the head and try with the tail
While there are more efficient algorithms out there, a simple one would be to check our lengths at each step along the list. This can be done pretty simply as
-- Phrase Replacement Sentence New sentence
replaceMany :: [String] -> String -> [String] -> [String]
replaceMany phrase new sentence = go sentence
where
phraseLen = length phrase
go [] = []
go sent#(x:xs)
| sentLen < phraseLen = sent
| first == phrase = new : go rest
| otherwise = x : go xs
where
sentLen = length sent
first = take phraseLen sent
rest = drop phraseLen sent
Here we can take advantage of Haskell's laziness and just go ahead and define first and rest without worrying if it's valid to do so. If they aren't used, they never get computed. I've opted to also use some more complex pattern matching in the form sent#(x:xs). This matches a list with at least one element, assigning the entire list to sent, the first element to x, and the tail of the list to xs. Next, we just check each condition. If sentLen < phraseLen, there's no possible chance that there's a match in the rest of the list so just return the whole thing. If the first m elements equals our phrase, then replace it and keep searching, and otherwise just put back the first element and keep searching.
I'm new to Haskell and I'm trying out a few tutorials.
I wrote this script:
lucky::(Integral a)=> a-> String
lucky 7 = "LUCKY NUMBER 7"
lucky x = "Bad luck"
I saved this as lucky.hs and ran it in the interpreter and it works fine.
But I am unsure about function definitions. It seems from the little I have read that I could equally define the function lucky as follows (function name is lucky2):
lucky2::(Integral a)=> a-> String
lucky2 x=(if x== 7 then "LUCKY NUMBER 7" else "Bad luck")
Both seem to work equally well. Clearly function lucky is clearer to read but is the lucky2 a correct way to write a function?
They are both correct. Arguably, the first one is more idiomatic Haskell because it uses its very important feature called pattern matching. In this form, it would usually be written as:
lucky::(Integral a)=> a-> String
lucky 7 = "LUCKY NUMBER 7"
lucky _ = "Bad luck"
The underscore signifies the fact that you are ignoring the exact form (value) of your parameter. You only care that it is different than 7, which was the pattern captured by your previous declaration.
The importance of pattern matching is best illustrated by function that operates on more complicated data, such as lists. If you were to write a function that computes a length of list, for example, you would likely start by providing a variant for empty lists:
len [] = 0
The [] clause is a pattern, which is set to match empty lists. Empty lists obviously have length of 0, so that's what we are having our function return.
The other part of len would be the following:
len (x:xs) = 1 + len xs
Here, you are matching on the pattern (x:xs). Colon : is the so-called cons operator: it is appending a value to list. An expression x:xs is therefore a pattern which matches some element (x) being appended to some list (xs). As a whole, it matches a list which has at least one element, since xs can also be an empty list ([]).
This second definition of len is also pretty straightforward. You compute the length of remaining list (len xs) and at 1 to it, which corresponds to the first element (x).
(The usual way to write the above definition would be:
len (_:xs) = 1 + len xs
which again signifies that you do not care what the first element is, only that it exists).
A 3rd way to write this would be using guards:
lucky n
| n == 7 = "lucky"
| otherwise = "unlucky"
There is no reason to be confused about that. There is always more than 1 way to do it. Note that this would be true even if there were no pattern matching or guards and you had to use the if.
All of the forms we've covered so far use so-called syntactic sugar provided by Haskell. Pattern guards are transformed to ordinary case expressions, as well as multiple function clauses and if expressions. Hence the most low-level, unsugared way to write this would be perhaps:
lucky n = case n of
7 -> "lucky"
_ -> "unlucky"
While it is good that you check for idiomatic ways I'd recommend to a beginner that he uses whatever works for him best, whatever he understands best. For example, if one does (not yet) understand points free style, there is no reason to force it. It will come to you sooner or later.