I just started learning functional programming and I find myself very confused by the concept of pattern matching (i'm using SML). Take for example the following expression to insert an element in an ordered list:
fun insert (n,nil) = [n]
| insert (n, L as x::l) =
if(n < x) then n::L
else x::(insert(n,l))
How can the list L be expressed as x::l? I know x refers to the first element of the list and l to the rest, but I don't know what to call this construct or how it can be used. I have read a lot but all the documentation I find doesn't mention this at all. Here is another example that doesn't use the 'as' keyword.
(*returns a list with the sum of each element added of two lists added together*)
fun addLists (nil,L) = L
| addLists (L,nil) = L
| addLists (x::xs,y::ys) =
(x + y)::(addLists(xs,ys))
Thank you for your help!
For the insert function here:
fun insert (n,nil) = [n]
| insert (n, L as x::l) =
if(n < x) then n::L
else x::(insert(n,l))
The | insert (n, L as x::l) part is a pattern which will be matched against. L as x::l is called an as pattern. It allows us to:
Pattern match against a non empty list, where x is the head of the list and l is the tail of the list
Refer to the entire matched list x::l with the name L
This is similar (although not totally the same) as doing:
| insert (n, x::l)
except that if you do that, the insert function will become:
fun insert (n,nil) = [n]
| insert (n, x::l) =
if(n < x) then n::x::l
else x::(insert(n,l))
So the big advantage of using the L as x::l as pattern over a non as pattern is that it allows us to refer to the entire list, not just its head and tail, and avoids an additional list construction when we need to refer to the entire list. Observe that the only difference in the 2 pieces of code is n::L and n::x::l. Since we use the as pattern L as x::l in the first insert function, we are able to do n::L instead of n::x::l. This avoids one :: operation (also known as cons operation).
As for this:
fun addLists (nil,L) = L
| addLists (L,nil) = L
| addLists (x::xs,y::ys) =
(x + y)::(addLists(xs,ys))
For the second pattern | addLists (x::xs,y::ys), in nowhere do we reconstruct the list x::xs and y::ys in the code following it, so we do not need as patterns. You could write it like:
fun addLists (nil,L) = L
| addLists (L,nil) = L
| addLists (ListX as x::xs, ListY as y::ys) =
(x + y)::(addLists(xs,ys))
and it'll still work, except that we do not refer to ListX or ListY here, and hence these two as patterns are unnecessary.
Related
I'm required to write a function that takes a list and splits it into 2 lists. The first list will hold elements in odd position and 2nd list hold elements in even position. Here's my attempt which gives me the following warning:
Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
How to improve this?
fun splt (lst: int list) =
let
fun splt2 (lst: int list, count: int, lst1: int list, lst2: int list) =
if null lst
then []
else if (count mod 2 = 0)
then splt2 (tl lst, count+1, hd lst::lst1, lst2)
else splt2 (tl lst, count+1, lst1, hd lst::lst2)
in
splt2 (lst,1,[],[])
end
Here's a 2nd correct implementation that I found but I'm mainly interested in fixing the 1st one!!
I want to split a list into a tupple of odd and even elements
fun split [] = ([], [])
| split [x] = ([x], [])
| split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
((x1::ys), (x2::zs))
end;
UPDATE: Improvement is just replace
if null lst then
[]
with this:
if null lst then
[lst1]#[lst2]
Here's some feedback for your code:
Give the function a proper name, like split or partition. The connotations that I have for those names are: Splitting (or exploding) takes something and returns one list of sub-components (e.g. string → char list), while partitioning takes a list of something and divides into two based on a predicate (e.g. List.partition), but they're not really set in stone.
Make up some variable names that aren't lst, since this is just an abbreviation of the type - surely redundant when even the type is there. For generic methods, good names can be hard to come by. A lot of ML code uses stuff like xs to imply a generic, plural form.
Ditch the type annotations; you'll get a polymorphic function that reads more easily:
fun split input =
let
fun split' (xys, count, xs, ys) = ...
in
split' (input, 1, [], [])
end
But really, the version you found online has some advantages: Pattern matching ensures that your lists have the right form before the function body is triggered, which minimizes run-time bugs. The functions hd and tl don't.
You could optimize the order of the cases slightly; i.e. list the most common case first. The parenthesis around x::xs and y::ys is unnecessary. Also, the two other cases (of one or zero elements) can be combined for brevity, but it doesn't matter much.
fun split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
(x1::ys, x2::zs)
end
| split rest = (rest, [])
You could also use case-of instead of let-in-end:
fun split (x1::x2::xs) =
(case split xs of
(ys, zs) => (x1::ys, x2::zs))
| split rest = (rest, [])
Lastly, you may want to make this function tail-recursive:
fun split xys =
let fun split' (x1::x2::xs, ys, zs) = split' (xs, x1::ys, x2::zs)
| split' (rest, ys, zs) = (rev (rest # ys), rev zs)
in
split' (xys, [], [])
end
To help get you over the error you are encountering
you need to look at the type of the function which you have given
val splt = fn : int list -> 'a list
and ask yourself what does an 'a list hold?
- val foo = "foo"::(splt[1,2,3,4,5]);
val foo = ["foo"] : string list
- val bar = 52::splt[1,2,3,4,5];
val bar = [52] : int list
it can hold anything, but the compiler cannot tell by itself.
I'm building a merge sort function and my split method is giving me a value restriction error. I'm using 2 accumulating parameters, the 2 lists resulting from the split, that I package into a tuple in the end for the return. However I'm getting a value restriction error and I can't figure out what the problem is. Does anyone have any ideas?
let split lst =
let a = []
let b = []
let ctr = 0
let rec helper (lst,l1,l2,ctr) =
match lst with
| [] -> []
| x::xs -> if ctr%2 = 0 then helper(xs, x::l1, l2, ctr+1)
else
helper(xs, l1, x::l2, ctr+1)
helper (lst, a, b, ctr)
(a,b)
Any input is appreciated.
The code, as you have written it, doesn't really make sense. F# uses immutable values by default, therefore your function, as it's currently written, can be simplified to this:
let split lst =
let a = []
let b = []
(a,b)
This is probably not what you want. In fact, due to immutable bindings, there is no value in predeclaring a, b and ctr.
Here is a recursive function that will do the trick:
let split lst =
let rec helper lst l1 l2 ctr =
match lst with
| [] -> l1, l2 // return accumulated lists
| x::xs ->
if ctr%2 = 0 then
helper xs (x::l1) l2 (ctr+1) // prepend x to list 1 and increment
else
helper xs l1 (x::l2) (ctr+1) // prepend x to list 2 and increment
helper lst [] [] 0
Instead of using a recursive function, you could also solve this problem using List.fold, fold is a higher order function which generalises the accumulation process that we described explicitly in the recursive function above.
This approach is a bit more concise but very likely less familiar to someone new to functional programming, so I've tried to describe this process in more detail.
let split2 lst =
/// Take a running total of each list and a index*value and return a new
/// pair of lists with the supplied value prepended to the correct list
let splitFolder (l1, l2) (i, x) =
match i % 2 = 0 with
|true -> x :: l1, l2 // return list 1 with x prepended and list2
|false -> l1, x :: l2 // return list 1 and list 2 with x prepended
lst
|> List.mapi (fun i x -> i, x) // map list of values to list of index*values
|> List.fold (splitFolder) ([],[]) // fold over the list using the splitFolder function
This question already has answers here:
F# insert/remove item from list
(5 answers)
Closed 8 years ago.
I'm trying to the learn the basics of F# right now and was wondering how you could replace a element in a list recursively. So you would do something like this.
let replace index sub = List.mapi (fun i x -> if i = index then sub else x)
replace 0 'd' ['a';'b';'c']
> val it : char list = ['d'; 'b'; 'c']
This meets your criteria, with two minor changes to make it more conformant with F# style:
the index is zero-based
the list is the last argument, to support piping
A modification of the answer I linked to directly answer the question:
let rec insert v i l = //v - value to substitute, i - index at which to substitute, l - the list
match i, l with
| 0, x::xs -> v::xs //this line does the actual replace
| i, x::xs -> x::insert v (i - 1) xs //simply iterates one further through the list
| i, [] -> failwith "index out of range" // the given index is outside the bounds of the list
Note that this is not tail recursive and will Stackoverflow for large lists. Arrays or List would probably be a better choice if you need to do this type of operation.
I wrote a recursive version of index as follows
let index list value =
let rec counter num = function
| [] -> -1
| h::t ->
if h == value
then num
else (counter (num + 1)) t
in counter 0 list;;
It works, but then our professor said we should use a tail recursive version in order to not timeout on the server, so I wrote a new index function using fold, but I can't seem to figure out why if it doesn't find the element, it returns a number greater than the length of the list, even though I want it to return -1.
let index2 list value = fold (fun i v ->
if i > (length list) then -1
else if v == value then i
else i+1) 0 list;;
Here's my fold version as well:
let rec fold f a l = match l with
[] -> a
| (h::t) -> fold f (f a h) t;;
Your folded function is called once for each element of the list. So you'll never see a value of i that's greater than (length list - 1).
As a side comment, it's quite inefficient (quadratic complexity) to keep calculating the length of the list. It would be better to calculate it once at the beginning.
As another side comment, you almost never want to use the == operator. Use the = operator instead.
EDIT
Why do you redefine fold instead of using List.fold_left?
Your first version of index is already tail recursive, but you can improve its style by:
using option type instead of returning -1 if not found;
directly call index recursively instead of a count function;
use = (structural) comparator instead of == (physical);
use a guard in your pattern matching instead of an if statement.
So
let index list value =
let rec index' list value i = match list with
| [] -> None
| h :: _ when h = value -> Some i
| _ :: t -> index' t value (succ i)
in index' list value 0
And as already said, index2 does not work because you'll never reach an element whose index is greater than the length of the list, so you just have to replace i > (length list) with i = (length list) - 1 to make it work.
But index2 is less efficient than index because index stops as soon as the element is found whereas index2 always evaluate each element of the list and compare the list length to the counter each time.
I am trying to produce the solution for an intersection of two sets using tail recursion and an empty list [] as an accu:
let rec setintersect list list =
let rec setintersect2 a b c =
match a with
| [] -> (match b with [] -> (setsimplify c) | h::t -> (setsimplify c))
| h1::t1 -> (match b with [] -> (setsimplify c) |h2::t2 -> (if (elementof h1 b) then (setintersect2 t1 b (c#[h1])) else (setintersect2 t1 b c))) in
setintersect2 list list [];;
Elementof takes takes "an int and a list" and is correctly working to give true if x is an element of the list, false otherwise..
Here is the problem:
# setintersect [5;2;1] [2;6;9];;
- : int list = [2; 6; 9]
and it should give [2].
What am I doing wrong?
I feel like there's something really simple that I am misunderstanding!
Edit:
Thanks for the responses so far.
setsimplify just removes the duplicates.
so [2,2,3,5,6,6] becomes [2,3,5,6]. Tested and made sure it is working properly.
I am not supposed to use anything from the List library either. Also, I must use "tail recursion" with the accumulator being a list that I build as I go.
Here is the thought:
Check the head element in list1, IF it exists in list2, THEN recurse with the "tail of list1, list2, and list c with that element added to it". ELSE, then recurse with "tail of list1, list2 and list c(as it is)".
end conditions are either list1 or list2 are empty or both together are empty, return list c (as it is).
let rec setintersect list list = is wrong: the two arguments should be named differently (you should of course update the call to setintersect2 accordingly), otherwise the second will shadow the first. I would have thought that OCaml would have at least warned you about this fact, but it appears that it is not the case.
Apart from that, the code seems to do the trick. There are a couple of things that could be improved though:
setintersect itself is not recursive (only setintersect2 is), you thus don't need the rec
you should find a different name for the argument of setintersect2. In particular, it is not obvious which is the accumulator (acc or accu will be understood by most OCaml programmers in these circumstances).
c#[h1] is inefficient: you will traverse c completely each time you append an element. It's better to do h1::c and reverse the result at the end
As a bonus point, if you append element at the beginning of c, and assume that a is ordered, you don't have to call setsimplify at the end of the call: just check whether c is empty, and if this is not the case, append h1 only if it is not equal to the head of c.
First, You didn't list out your setsimplify function.
To write an ocaml function, try to split it first, and then combine if possible.
To solve this task, you just go through all elements in l1, and for every element, you check whether it is in l2 or not, right?
So definitely you need a function to check whether an element is in a list or not, right?
let make one:
let rec mem x = function
| [] -> false
| hd::tl -> hd = x || mem x tl
Then you can do your intersection:
let rec inter l1 l2 =
match l1 with
| [] -> []
| hd::tl -> if mem hd l2 then hd::(inter tl l2) else inter tl l2
Note that the above function is not tail-recursive, I guess you can change it to tail-recursive as an excise.
If you use std library, then it is simple:
let intersection l1 l2 = List.filter (fun x -> List.mem x l2) l1