I got a function that takes a string and search it for decimals and output them.
What I got so far is:
getDecimal :: String -> [Int]
getDecimal[] = 0
getDecimal (x:y:xs) =
if
isDecimal x y
then
//output list
else
getDecimal xs
(for example: getDecimal "he12llo035" will output 12 , 035
But cant fill in the then because I simply cant find the solution, can you guys give me tips?
You try to tackle this in an imperative-like way: "look at this part of the list, is it what we want? Well then do that thing... oh, no it's not? Well then proceed there..."
It sure can't hurt to know how to implement it in such terms as well, but anyway better to learn more concise, explanative, declarative approaches right away. "The Haskell way".
That normally means roughly: split up your goal in subtasks. You'll first want to group all numbers down to one element, rather than a group of characters within the list. Well, you can hoogle for "group"!
Indeed there's a group comparison, but it simply puts together elements that are equal. That's a bit too strong, we sure want to allow different digits in each number! groupBy is the version that allows this. So, according to what do we want to group? Well, depending on whether it's a number. There's a nice helper from the Data.Function module that allows you to formulate this very much to the point:
groupBy ((==) `on` isDigit)
Meaning, we want to group such elements that have "the same status of digit-being".
After that, you'll have a lists of strings, and know each string is either a complete number, or something unrelated. The latter is to be thrown away...
filter (isDigit . head)
(I've just checked if the first character is a digit; the following are sure to be as well because that's what we've grouped by.
Now you've a list of numbers still in string form, all that's left to be done is parse (read!) each of them.
import Data.Char
import Data.List
import Data.Function
getDecimalNums :: String -> [Int]
getDecimalNums = map read . filter (isDigit . head) . groupBy ((==) `on` isDigit)
Related
I have just started to learn ocaml and I find it difficult to extract small list of chars from a bigger list of chars.
lets say I have:
let list_of_chars = ['#' ; 'a' ; 'b' ; 'c'; ... ; '!' ; '3' ; '4' ; '5' ];;
I have the following knowledge - I know that in the
list above I have '#' followed by a '!' in some location further in the list .
I want to extract the lists ['a' ;'b' ;'c' ; ...] and ['3' ; '4' ; '5'] and do something with them,
so I do the following thing:
let variable = match list_of_chars with
| '#'::l1#['!']#l2 -> (*[code to do something with l1 and l2]*)
| _ -> raise Exception ;;
This code doesn't work for me, it's throwing errors. Is there a simple way of doing this?
(specifically for using match)
As another answer points out, you can’t use pattern matching for this because pattern matching only lets you use constructors and # is not a constructor.
Here is how you might solve your problem
let split ~equal ~on list =
let rec go acc = function
| [] -> None
| x::xs -> if equal x on then Some (rev acc, xs) else go (x::acc) xs
in
go [] list
let variable = match list_of_chars with
| '#'::rest ->
match split rest ~on:'!' ~equal:(Char.equal) with
| None -> raise Exception
| Some (left,right) ->
... (* your code here *)
I’m now going to hypothesise that you are trying to do some kind of parsing or lexing. I recommend that you do not do it with a list of chars. Indeed I think there is almost never a reason to have a list of chars in ocaml: a string is better for a string (a chat list has an overhead of 23x in memory usage) and while one might use chars as a kind of mnemonic enum in C, ocaml has actual enums (aka variant types or sum types) so those should usually be used instead. I guess you might end up with a chat list if you are doing something with a trie.
If you are interested in parsing or lexing, you may want to look into:
Ocamllex and ocamlyacc
Sedlex
Angstrom or another parser generator like it
One of the regular expression libraries (eg Re, Re2, Pcre (note Re and Re2 are mostly unrelated)
Using strings and functions like lsplit2
# is an operator, not a valid pattern. Patterns need to be static and can't match a varying number of elements in the middle of a list. But since you know the position of ! it doesn't need to be dynamic. You can accomplish it just using :::
let variable = match list_of_chars with
| '#'::a::b::c::'!'::l2 -> let l1 = [a;b;c] in ...
| _ -> raise Exception ;;
For a school exercise i need to generate a series of symbols with a given array of numbers. given is [3,3,2,1] output "+===+===+==+=+".
My approach would be to use map and replicate "=" on the array then intercalate "+" and finally concat the array to a single string.
My solution is something like this (while standing knee deep in errors)
printLine arr = map (replicate "=") arr >>> intercalate '*' >>> concat
what is the correct syntax? or shouldn't i use map at all?
you are on the right track, you just mixed up the functions a bit:
replicate will take a number n and repeat the second argument n-times into a list (so you just got the order wrong - you could use flip or an aux. function like I did bellow)
you have to watch out if you want Char or String ('=' VS "=" for example) - read the type-definitions (try :t intercalate or Hoogle) carefully and remember: String ~ [Char]!
intercalate actually does the concatenation so you don't need concat at all
Here is a almost working version:
eqSigns :: Int -> String
eqSigns n = replicate n '='
mixIn :: [Int] -> String
mixIn = intercalate "+" . map eqSigns
try it and see if you get the missing parts in there ;)
here is the version with flip instead:
mixIn :: [Int] -> String
mixIn = intercalate "+" . map (flip replicate '=')
PS: are you coming from some ML/F# background?
I just started working with Haskell and stumbled on a problem.
According to Haskell, I have a pattern match failure, but I fail to see how.
This is the code I try to execute:
statistics :: [Int] -> (Int, Int, Int)
statistics [gradelist] = ( amountParticipants, average, amountInsufficient)
where
amountParticipants= length [gradelist]
average= sum[gradelist] `div` amountParticipants
amountInsufficient= length [number| number<- [gradelist], number<6]
I call 'statistics' with:
statistics[4,6,4,6]
this causes a pattern match failure, while I expect to see : (4, 5, 2)
statistics[6]
gives the answer : ( 1, 6, 0 ) (which is correct).
Can someone tell me why my first call causes this pattern match? Because I'm pretty sure I give a list as an argument
If you write statistics [gradelist] = ... you are pattern matching against a singleton list containing a sole element referred to as gradelist. Hence, your function is only defined for lists of length exactly 1 (such as [6]); it is undefined for the empty list ([]) or lists with two or more elements (such as [4,6,4,6]).
A correct version of your function would read
statistics :: [Int] -> (Int, Int, Int)
statistics gradelist = (amountParticipants, average, amountInsufficient)
where
amountParticipants = length gradelist
average = sum gradelist `div` amountParticipants
amountInsufficient = length [number| number <- gradelist, number < 6]
As #thoferon remarked, you will also need to make special arrangements for the case in which gradelist is empty, in order to avoid dividing by zero when computing average.
Just replace your [gradelist]'s by gradelist as said before. Also, you might want to match against the empty list with [], in order to avoid dividing by zero in average, like :
statistics [] = (0,0,0)
The list syntax [ ] in a pattern deconstructs a list. The pattern [gradelist] matches a list holding exactly one value, and it names the value in the list gradelist. You get a pattern match failure if you try to call the function with a list holding four values.
To match a value without deconstructing it, use a variable as the pattern.
Let's say I want to consider input of the form
[int_1, int_2, ..., int_n]
[int_1, int_2, ..., int_m]
...
where the input is read in from a text file. My goal is to obtain the maximum size of this list. Currently I have a regular expression that recognizes this pattern:
let input = "[1,2,3] [1,2,3,4,5]"
let p = input =~ "(\\[([0-9],)*[0-9]\\])" :: [[String]]
Output:
[["[1,2,3]","[1,2,3]","2,"],["[1,2,3,4,5]","[1,2,3,4,5]","4,"]]
So what I'm after is the max of the third index + 1. However, where I'm stuck is trying to consider this index as an int. For instance I can refer to the element just fine:
(p !! 0) !! 2
> "2,"
But I can't convert this to an int, I've tried
read( (p !! 0) !! 2)
However, this does not work despite the fact that
:t (p !! 0) !! 2
> (p !! 0) !! 2 :: String
Appears to be a string. Any advice as to why I can't read this as an int would be greatly appreciated.
Thanks again.
I'm not entirely sure that your approach is one I'd recommend, but I'm struggling to wrap my head around the goal, so I'll just answer the question.
The problem is that read "2," can't just produce an Int, because there's a leftover comma. You can use reads to get around this. reads produces a list of possible parses and the strings left over, so:
Prelude> (reads "2,") :: [(Int,String)]
[(2,",")]
In this case it's unambiguous, so you get one parse from which you can then pull out the int, although regard for your future self-respect suggests being defensive and not assuming that there will always be a valid parse (the Safe module is good for that sort of thing).
Alternatively, you could modify your regex to not include the comma in the matched group.
I have a 2 lists of strings
eg:
listx = ["name","age","rank"]
input = ["name","age"]
How can I compare the two lists to check whether the listx contains "name" & "age" given in input?
B is a subset of A iff B \ A is empty
so another way to do it is
import Data.List ((\\))
null (input \\ listx)
all (flip elem listx) input
comes to mind. No idea how efficient it is though...
Is this homework? :)
You need to create one or two recursive functions to walk through both lists, and search for every string in the input.
Or, you could look up some good functions in the Prelude that helps here.
One more way.
import Data.Set
(fromList input) `isSubsetOf` (fromList listX)