I have this function that splits a string into parts, and I need to place these parts into the right list within the params map.
parts = String.split(term_string, " ")
params = %{
search_terms: [],
wildcard_terms: [],
minus_terms: [],
room_terms: [],
messages_to_terms: [],
messages_from_terms: [],
date_before_terms: [],
date_after_terms: [],
date_on_terms: [],
date_during_terms: []
}
Enum.reduce(parts, params, fn p ->
cond do
String.ends_with?(p, "*") ->
params[:wildcard_terms] = [p | params[:wildcard_terms]]
true ->
params[:search_terms] = [p | params[:search_terms]]
end
end)
I'm getting cannot invoke remote function Access.get/2 inside match error right now and I'm not sure how to address the issue.
Any suggestions?
You need to return a new map with the updated list inside in the reducing function. One way to do that is to use Map.update!:
Enum.reduce(parts, params, fn p ->
cond do
String.ends_with?(p, "*") ->
Map.update!(params, :wildcard_terms, fn ps -> [p | ps] end)
true ->
Map.update!(params, :search_terms, fn ps -> [p | ps] end)
end
end)
While the #Dogbert’s solution is surely perfectly robust, I would refactor this code a bit to make it more succinct:
Enum.reduce(parts, params, fn p ->
key =
if String.ends_with?(p, "*") do
:wildcard_terms
else
:search_terms
end
Map.update!(params, key, fn ps -> [p | ps] end)
end)
or, if you prefer more verbose (and slow—see the valuable Dogbert’s comment below) but easy-to-read approach (based on matching the mapper argument):
parts
|> Enum.map(&String.reverse/1)
|> Enum.reduce(params, fn
<<"*", _::binary>> = p ->
%{params | wildcard_terms: [p | params.wildcard_terms]}
p ->
%{params | search_terms: [p | params.search_terms]}
end)
Related
I coded the following function to compute the cartesian product of two lists.
let rec descartes a = function
| _ when a = [] -> []
| [] -> []
| t::q -> (List.map (function x -> t, x) a) # (descartes q a) ;;
But when I want to simplify the function
let rec descartes a = function
| _ when a = [] | [] -> []
| t::q -> (List.map (function x -> t, x) a) # (descartes q a) ;;
I get a syntax error.
According to this the syntax of pattern matching is:
pattern-matching ::= [ | ] pattern [when expr] -> expr { | pattern [when
expr] -> expr }
And you can find the syntax of pattern here. So as you can see when is not a part of pattern, which explains the syntax error.
But in any case, your code is redundant, once you have [] -> [] you don't need _ when a = [] -> [] anymore. So you can simply write:
let rec descartes a = function
| [] -> []
| t::q -> (List.map (function x -> t, x) a) # (descartes q a) ;;
I'm just a begginer in Ocaml, and I wanted to study the graph theory, but with implementations in Ocaml. And I've got a trouble to do something : I just wanted to list the connected components of a graph by using a Depth first search. So, I did :
#open "stack" ;;
let non_empty pile =
try push (pop pile) pile ; true with Empty -> false ;;
let connected_comp g =
let already_seen = make_vect (vect_length g) false in
let comp = [] in
let dfs s lst =
let totreat = new () in
already_seen.(s) <- true; push s totreat;
let rec add_neighbour l = match l with
| [] -> ()
| q::r when already_seen.(q) = false -> push q totreat; already_seen.(q) <- true; add_neighbour r
| q::r -> add_neighbour r
in
while non_empty totreat do
let s = pop totreat in
already_seen.(s) <- true;
(* we want to add s to the list lst *) s::lst;
add_neighbour g.(s);
done
in
let rec head_list l = match l with
| [] -> failwith "Empty list"
| p::q -> p
in
let rec aux comp t = match t with
| t when t = vect_length g -> comp
| t when already_seen.(t) = true -> aux comp (t+1)
| t -> aux ((dfs t [])::comp) (t+1) (* we want that dfs t [] return the list lst modified *)
in aux comp 0;;
And I obtain :
> | t -> (dfs t [])::comp ; aux comp (t+1) (* we want that dfs t [] return the list lst modified *)
> ^^^^^^^^^^^^^^^^
Warning : this expression is of type unit list,
but is used with the type unit.
connected_comp : int list vect -> unit list = <fun>
- : unit list = []
- : unit = ()
Of course, I'm not surprised. But what I want to do is that the function dfs return the list sent on argument (the lst list) but modified, and here it's not the case as the function is of type unit, cause it return nothing. But in Ocaml, as the language is made for returning the last expression I think, I don't know how to do. I could as well use recursive algorithm for the dfs function, as, through filtering, it would permit me to return the list, but I just wanted to learn about Ocaml, and so modified (even if it's not optimal) my algorithm.
Someone could help me, please ?
Edit : As we ask me, I will try to reduce my code and get to the point. So, I have the function dfs which correspond to a Depth first search (for a graph)
let dfs s lst =
let totreat = new () in
already_seen.(s) <- true; push s totreat;
let rec add_neighbour l = match l with
| [] -> ()
| q::r when already_seen.(q) = false -> push q totreat; already_seen.(q) <- true; add_neighbour r
| q::r -> add_neighbour r
in
while non_empty totreat do
let s = pop totreat in
already_seen.(s) <- true;
(* we want to add s to the list lst *) s::lst;
add_neighbour g.(s);
done
in
(alreadyseen is a vector of boolean, defined previously)
And my only problem is that I want that the function return the list lst modified (in the loop while), when, at this point, it's a unit function.
I tried to define lst as a reference, but then I don't know how to return it...
I hope it's more clear, I'm not familiar with all of this at the moment...
Thank you !
Here is a degraded version of your code that demonstrate one way to do what you want.
let non_empty _ = false
let dfs s lst =
let local_lst = ref lst in
while non_empty () do
(*do stuff here*)
let s = 1 in
local_lst := s::!local_lst;
(*do stuff here*)
done;
!local_lst
I first initialize a local mutable value local_lst to the list lst given as an argument. I then update this value in the while loop. And finally I return the value stored in local_lst.
What would be the syntax (if possible at all) for returning the list of lists ([[a]]) but without the use of empty list ([]:[a])?
(similar as the second commented guard (2) below, which is incorrect)
This is a function that works correctly:
-- Split string on every (shouldSplit == true)
splitWith :: (Char -> Bool) -> [Char] -> [[Char]]
splitWith shouldSplit list = filter (not.null) -- would like to get rid of filter
(imp' shouldSplit list)
where
imp' _ [] = [[]]
imp' shouldSplit (x:xs)
| shouldSplit x = []:imp' shouldSplit xs -- (1) this line is adding empty lists
-- | shouldSplit x = [imp' shouldSplit xs] -- (2) if this would be correct, no filter needed
| otherwise = let (z:zs) = imp' shouldSplit xs in (x:z):zs
This is the correct result
Prelude> splitWith (== 'a') "miraaaakojajeja234"
["mir","koj","jej","234"]
However, it must use "filter" to clean up its result, so I would like to get rid of function "filter".
This is the result without the use of filter:
["mir","","","","koj","jej","234"]
If "| shouldSplit x = imp' shouldSplit xs" is used instead the first guard, the result is incorrect:
["mirkojjej234"]
The first guard (1) adds empty list so (I assume) compiler can treat the result as a list of lists ([[a]]).
(I'm not interested in another/different solutions of the function, just the syntax clarification.)
.
.
.
ANSWER:
Answer from Dave4420 led me to the answer, but it was a comment, not an answer so I can't accept it as answer. The solution of the problem was that I'm asking the wrong question. It is not the problem of syntax, but of my algorithm.
There are several answers with another/different solutions that solve the empty list problem, but they are not the answer to my question. However, they expanded my view of ways on how things can be done with basic Haskell syntax, and I thank them for it.
Edit:
splitWith :: (Char -> Bool) -> String -> [String]
splitWith p = go False
where
go _ [] = [[]]
go lastEmpty (x:xs)
| p x = if lastEmpty then go True xs else []:go True xs
| otherwise = let (z:zs) = go False xs in (x:z):zs
This one utilizes pattern matching to complete the task of not producing empty interleaving lists in a single traversal:
splitWith :: Eq a => (a -> Bool) -> [a] -> [[a]]
splitWith f list = case splitWith' f list of
[]:result -> result
result -> result
where
splitWith' _ [] = []
splitWith' f (a:[]) = if f a then [] else [[a]]
splitWith' f (a:b:tail) =
let next = splitWith' f (b : tail)
in if f a
then if a == b
then next
else [] : next
else case next of
[] -> [[a]]
nextHead:nextTail -> (a : nextHead) : nextTail
Running it:
main = do
print $ splitWith (== 'a') "miraaaakojajeja234"
print $ splitWith (== 'a') "mirrraaaakkkojjjajeja234"
print $ splitWith (== 'a') "aaabbbaaa"
Produces:
["mir","koj","jej","234"]
["mirrr","kkkojjj","jej","234"]
["bbb"]
The problem is quite naturally expressed as a fold over the list you're splitting. You need to keep track of two pieces of state - the result list, and the current word that is being built up to append to the result list.
I'd probably write a naive version something like this:
splitWith p xs = word:result
where
(result, word) = foldr func ([], []) xs
func x (result, word) = if p x
then (word:result,[])
else (result, x:word)
Note that this also leaves in the empty lists, because it appends the current word to the result whenever it detects a new element that satisfies the predicate p.
To fix that, just replace the list cons operator (:) with a new operator
(~:) :: [a] -> [[a]] -> [[a]]
that only conses one list to another if the original list is non-empty. The rest of the algorithm is unchanged.
splitWith p xs = word ~: result
where
(result, word) = foldr func ([], []) xs
func x (result, word) = if p x
then (word ~: result, [])
else (result, x:word)
x ~: xs = if null x then xs else x:xs
which does what you want.
I guess I had a similar idea to Chris, I think, even if not as elegant:
splitWith shouldSplit list = imp' list [] []
where
imp' [] accum result = result ++ if null accum then [] else [accum]
imp' (x:xs) accum result
| shouldSplit x =
imp' xs [] (result ++ if null accum
then []
else [accum])
| otherwise = imp' xs (accum ++ [x]) result
This is basically just an alternating application of dropWhile and break, isn't it:
splitWith p xs = g xs
where
g xs = let (a,b) = break p (dropWhile p xs)
in if null a then [] else a : g b
You say you aren't interested in other solutions than yours, but other readers might be. It sure is short and seems clear. As you learn, using basic Prelude functions becomes second nature. :)
As to your code, a little bit reworked in non-essential ways (using short suggestive function names, like p for "predicate" and g for a main worker function), it is
splitWith :: (Char -> Bool) -> [Char] -> [[Char]]
splitWith p list = filter (not.null) (g list)
where
g [] = [[]]
g (x:xs)
| p x = [] : g xs
| otherwise = let (z:zs) = g xs
in (x:z):zs
Also, there's no need to pass the predicate as an argument to the worker (as was also mentioned in the comments). Now it is arguably a bit more readable.
Next, with a minimal change it becomes
splitWith :: (Char -> Bool) -> [Char] -> [[Char]]
splitWith p list = case g list of ([]:r)-> r; x->x
where
g [] = [[]]
g (x:xs)
| p x = case z of []-> r; -- start a new word IF not already
_ -> []:r
| otherwise = (x:z):zs
where -- now z,zs are accessible
r#(z:zs) = g xs -- in both cases
which works as you wanted. The top-level case is removing at most one empty word here, which serves as a separator marker at some point during the inner function's work. Your filter (not.null) is essentially fused into the worker function g here, with the conditional opening1 of a new word (i.e. addition1 of an empty list).
Replacing your let with where allowed for the variables (z etc.) to became accessible in both branches of the second clause of the g definition.
In the end, your algorithm was close enough, and the code could be fixed after all.
1 when thinking "right-to-left". In reality the list is constructed left-to-right, in guarded recursion ⁄ tail recursion modulo cons fashion.
I am writing a ocaml project, in which I have a function that replace all '' in a char-list with 'E'. Here's my code for this propose:
let rec string_lst_change_E lst =
match lst with
[] -> let a ='E'; a::[]
|(h::t) if (h = '') -> 'E'::(string_lst_change_E t)
|(h::t) -> h::(string_lst_change_E t)
;;
It says I have a syntax error... But I cannot figure out by myself.
I tried to modify it like this:
let rec string_lst_change_E lst =
match lst with
[] -> 'E'::[]
|(h::t) ->if (h = '') then 'E'::(string_lst_change_E t) else h::(string_lst_change_E t)
;;
but still there's syntax error...(on the line |(h::t) -> .... char 18-21)
Please help me to take a look at it. Thank you!
This is where the first error lies: [] -> let a ='E'; a::[] If you want to use a after declaring it, you should instead write [] -> let a = 'E' in a ::[]. Obviously, [] -> ['E'] is simpler.
The second is the use of if in a pattern match. You should use when instead: |(h::t) when h = '' -> 'E'::(string_lst_change_E t)
But what's '' anyway? The empty character? How would you get this in a string? Typing '' is itself a syntax error. Try it in the toplevel! To make your code compile, I replaced '' by ' '.
let rec string_lst_change_E lst =
match lst with
| [] -> let a ='E' in a::[]
| (h::t) when h = ' ' -> 'E'::(string_lst_change_E t)
| (h::t) -> h::(string_lst_change_E t)
Note that you can simply use function here:
let rec string_lst_change_E = function
| [] -> let a ='E' in a::[]
| (h::t) when h = ' ' -> 'E'::(string_lst_change_E t)
| (h::t) -> h::(string_lst_change_E t)
I am working on a project with OCaml and there are some problems regarding to arrays that I am not sure with. I am not allowed to use the List module, so please give me some idea or suggestion with my works.
First, I already implemented a function 'a list -> 'a list called uniq that return a list of the uniq elements in an array, for example uniq [5;6;5;4] => [6;5;4]
Here is my implementation:
let rec uniq x =
let rec uniq_help l n =
match l with
[] -> []
| h :: t -> uniq_help t, n if (n = h) else (h :: (uniq_help(t, n)))
match x with
[] -> []
| h::t -> uniq_help t, h
;;
I mot sure this is a correct implementation, can someone give me some suggestion or correctness?
You functions are syntactically incorrect for various reasons:
uniq_help takes two elements so you have to invoke it using uniq_help t n, not uniq_help(t, n) and the like.
an if/else expression should have the form of if cond then expr1 else expr2.
to use uniq_help locally in uniq, you need an in keyword.
After fixing syntax errors, your function looks like:
let rec uniq x =
let rec uniq_help l n =
match l with
| [] -> []
| h :: t -> if n = h then uniq_help t n else h::(uniq_help t n) in
match x with
| [] -> []
| h::t -> uniq_help t h
However, to be sure that each element is unique in the list, you have to check uniqueness for all of its elements. One quick fix could be:
let rec uniq x =
(* uniq_help is the same as above *)
match x with
| [] -> []
| h::t -> h::(uniq_help (uniq t) h)