increasing sum of the elements of a list - ocaml

How to compute the sum of each element of a list multiplied by it's index position in OCaml? example: for [4;7;9] the result is 45 (4x1 + 7x2 + 9x3 = 45). the only authorized functions are List.hd, List.tl et List.length.
I can do it in the other direction with this code:
let rec sum l =
let n = (List.length l) + 1 in
if l = [] then 0 else
((List.hd l)*(n-1))+ (sum(List.tl l)) ;;
sum [4;7;9];;
- : int = 35 (4x3 + 7x2 + 9x1 = 35)
But the expected result is 45 (4x1 + 7x2 + 9x3 = 45).
thank you for your help.

Personally, I'd probably do something like this..
let rec new_sum l n =
match l with
| [] -> 0
| head::tail -> (head * n) + new_sum tail (n+1)
let sum l =
new_sum l 1;;
sum [4;7;9]
...if you don't like the guards for pattern matching, and prefer List.hd, List.tl, List.length then you could use...
let rec new_sum l n =
if (List.length l == 0) then 0
else ((List.hd l) * n) + new_sum (List.tl l) (n+1)
let sum l =
new_sum l 1;;
sum [4;7;9];

Related

How to compute the average of a list of a special record in OCaml?

Lets say we have a record which defines students:
type student = {
name : string;
age : int;
grades : (float) list;
}
And safe them in a list like this:
let studentlist = [ {name="alex"; age=7; grades=[1.;2.;3.]} ;
{name="bianca"; age=6; grades=[1.;1.;2.]} ];;
My aim is to compute the grade average of a special student which I choose per age, I select the student with the function search:
let search a lst = List.find( fun {age;_} -> a = age)lst
And compute the average with the help-functions , named sum, length and finally avr :
let rec sum lst =
match lst with
| [] -> 0.0
| h :: t -> h +. sum t
let length lst = float_of_int (List.length lst);;
let avr lst = sum lst /. length lst;;
I don't know how to combine those functions to compute the average properly!
Most of what you've done seems to work. For instance, search works.
utop # search 7 studentlist;;
- : student = {name = "alex"; age = 7; grades = [1.; 2.; 3.]}
If you want to access the grades field of that record, use . for record access.
utop # (search 7 studentlist).grades;;
- : float list = [1.; 2.; 3.]
Now that you have a list of float values, finding the sum or average of them should be easy by passing that value as an argument to the relevant function you've already defined.
Bear in mind that when you use List.find in search, if you search for an age that is not present, you will get a Not_found exception that you will want to handle.
As an aside, note that your avr function iterates over the list twice. Once to compute the sum, and ocne to compute the length.
It is possible to computer the sum, the length, and the average in a single pass. We can use a fold to do this. First off, we can define a basic left fold:
let rec foldl f init lst =
match lst with
| [] -> init
| x::xs -> foldl f (f init x) xs
Consider using this to compute the length of a list:
foldl (fun i _ -> i + 1) 0 [1.; 2.; 3.]
When evaluated:
foldl (fun i _ -> i + 1) 0 [1.; 2.; 3.]
foldl (fun i _ -> i + 1) (0 + 1) [2.; 3.]
foldl (fun i _ -> i + 1) (1 + 1) [3.]
foldl (fun i _ -> i + 1) (2 + 1) []
3
But we can pass a tuple of values to foldl, building up the length, sum, and average as we go.
utop # let (len, sum, avg) = foldl
(fun (len, sum, avg) x ->
let sum = sum +. x in
let len = len + 1 in
let flen = float_of_int len in
(len, sum, sum /. flen))
(0, 0., 0.)
[1.; 2.; 3.];;
val len : int = 3
val sum : float = 6.
val avg : float = 2.

This expression has type 'a list -> 'a list but an expression was expected of type int

let rec first_part n l =
if n = 0 then
[]
else
match l with
| [] -> []
| x :: xs -> x :: first_part n-1 xs
let rec second_part n l =
match l with
| [] -> []
| x :: xs ->
if n = 0 then l
else second_part n-1 xs
let rec split n l =
match n with
| 0-> ([], l)
| n -> (first_part n l , second_part n l)
This isn't a very well posed question. You don't show the details of the error or ask a specific question. You also didn't format the code in a readable way (I improved it for you).
Your problem is that
first_part n-1 xs
is parsed like this
(first_part n) - (1 xs)
Function calls in OCaml (juxtaposed expressions) have high precedence. So you need parentheses around (n - 1) in two places.

Why is this OCaml code resulting in a runtime error?

I am trying to run the following code on a coding question website and it says there is a runtime error, but running it on the top-level ocaml seems to work fine. Could there be any source of error in the code? Thanks in advance
The question is to find the number of 'good segments' within the given list and a specific number. A good segment is defined as follows:
A and B are positive integers such that A < B.
x that satisfies A <= x <= B is not an element of the given list.
The following are the inputs.
n, which is the number of elements in the list that will be given.
a, b, c, ... which are the elements of the list.
t, which is the number that must be included in the segment.
The output should be a single number printed out.
Edited Code:
let rec drop_value l to_drop =
match l with
| [] -> []
| hd :: tl ->
let new_tl = drop_value tl to_drop in
if hd = to_drop then new_tl else hd :: new_tl
;;
let rec find_start li t cur_min =
match li with
| [] -> cur_min
| hd :: tl -> let new_min = abs (t - hd) in
if new_min = 0 then find_start tl t new_min
else if new_min < cur_min && t > hd then find_start tl t new_min
else find_start tl t cur_min
;;
let rec find_end li t cur_min =
match li with
| [] -> cur_min
| hd :: tl -> let new_min = abs (t - hd) in
if new_min = 0 then find_end tl t new_min
else if new_min < cur_min && t < hd then find_end tl t new_min
else find_end tl t cur_min
;;
let rec contains_value l value =
match l with
| [] -> false
| hd :: tl -> if hd = value then true else contains_value tl value
;;
let nums = ref [];;
let n = read_int () in
for i = 1 to n do
Scanf.scanf " %d" (fun a ->
nums := a :: !nums)
done;
Scanf.scanf " %d" (fun t ->
if contains_value !nums t then print_int 0
else let start = if List.length !nums = 1 then 1 else abs (find_start !nums t 1001 - t) in
let finish = find_end (drop_value !nums start) t 1001 + t in
if t > start && t < finish then (if start = 1 && List.length ! nums = 1 then print_int ((t - start + 1) * (finish - t) - 1) else print_int ((t - start) * (finish - t) - 1))
else let start = 1 in print_int ((t - start + 1) * (finish - t) - 1))
;;
eg.
5
4 8 13 24 30
10
should give
5
=> [9, 10], [9, 11], [9, 12], [10, 11], [10, 12]
You don't describe the exact input format that your code is going to get. This makes it pretty much impossible to debug your code.
When I compile and run your code (as m.ml) using the input you describe I see this:
$ ./m
5 4 8 13 24 30 10
Fatal error: exception Failure("int_of_string")
In fact no matter what format I try for the input I get the same result.
So that is probably what is happening at the website.
In my experience it always causes more harm than good to use scanf. Combining it with other input functions is probably going to make things worse.
If you describe the expected format of the input carefully, somebody on StackOverflow can recommend a way to get your numbers.
In the meantime here's a way to read all the numbers on one line:
let rec split_at list n =
if n = 0 then
([], list)
else
match list with
| [] -> ([], [])
| h :: t ->
let (a, b) = split_at t (n - 1) in (h :: a, b)
in
let (nums, t) =
let line = read_line () in
let nstrs = Str.split (Str.regexp "[ \t][ \t]*") line in
match List.map int_of_string nstrs with
| [] -> failwith "no numbers"
| n :: rest ->
if List.length rest <> n + 1 then
failwith "bad count"
else
let (nums, tlist) = split_at rest n in
(nums, List.hd tlist)
in
. . .

OCaml - Returning list which has the highest number on a specific index on a input variable - list of list

Newbie here!
I'm trying to build a program that returns a list that has the highest number on a specific index. I've been trying so many things, and this looks the simplest code I can come with.
On the below example I was expecting the list ["2";"4";"6";"7";"8";"4"] to be returned. However I came across this error:
File "blablabla.ml", line 7, characters 63-74:
Error: This expression has type int but an expression was expected of type 'a list
Anybody can help?
let a = [["1";"2";"3";"4";"5";"6"];["2";"5";"6";"1";"5";"7"];["1";"2";"3";"4";"5";"6"];["2";"4";"6";"7";"8";"4"]];;
let rec max lista i = match lista with
| [] -> 0
| x::xs ->
let best_list = max xs i in
if (int_of_string(List.nth x i)) > (int_of_string(List.nth best_list i)) then
x
else
best_list
;;
let result = max a 4;;
result;;
EDIT:
Still haven't succeeded it, thanks to #G4143 and #glennsl I managed to go with another aproach, but know is complaining with syntax error.
let max l i = match l with
| [] -> []
| x::xs ->
let rec compare_lists x xs i =
if i < (List.length xs) then
if (List.nth x i) > (List.nth xs i) then
x
else
xs
else
failwith "Position too large for list"
;;
This is how I would approach this problem. Note: I left out the solution for the branch of many lists.
let get_max_at_pos l1 l2 pos =
if (pos < List.length l1) && (pos < List.length l2)
then
if (List.nth l1 pos) < (List.nth l2 pos)
then
l2
else
l1
else
failwith "Position too large for list"
let get_max l pos =
match l with
| [] -> None
| hd::[] -> Some hd(*should check position against hd length*)
| hd1::hd2::[] -> Some (get_max_at_pos hd1 hd2 pos)
| hd::tl -> (*now you have to solve the branch for many lists*)
This is a better and cleaner solution since you have it working now:
let a =
[
[1; 2; 3; 4; 5; 6];
[2; 5; 6; 1; 5; 7];
[1; 2; 3; 4; 5; 6];
[2; 4; 6; 7; 8; 4]
]
let get_max_of_pos l1 l2 pos =
if (pos < List.length l1) && (pos < List.length l2)
then
if (List.nth l1 pos) < (List.nth l2 pos)
then
l2
else
l1
else
failwith "List too short"
let get_max l pos =
match l with
| [] -> failwith "Empty list of list"
| hd::tl ->
List.fold_left (fun a d -> get_max_of_pos a d pos) hd tl
let ans = get_max a 5
You can rewrite the fold_left part by hand if you like.
Did you try breaking the problem down into easier steps? How about create a function that takes 2 lists of even length and returns a list with the highest element of both list by position. Something like this.
let rec get_max_at_position l1 l2 pos =
if pos < (List.length l1)
then
if (List.nth l1 pos) > (List.nth l2 pos)
then
l1
else
l2
else
failwith "Position too large for list"

building a list of ints in ocaml

I want to write a function that does builds a list between two ints, inclusive
rec myFunc x y would build a list with all the ints between x and y, including x and y
For the logic right now I have something like this:
let rec buildList i n = let x = i+1 in if i <= n then i::(buildList x n)
But this gives me an error "Expression has type 'a list but but an expression was expected of type unit.
I thought buildList is returning a list of ints, and i as an int, so the cons operator would be valid, but its saying it should be void?
Why does this happen, and how do I fix it?
If the condition is true, you return the list i::(buildList x n). If it's not true, what do you return ?
Add else [] to your function to return the empty list when the condition is not met.
When you don't have any else, the compiler supposes it is else () (hence the error message).
Your if is missing an else condition
I suggest that you use a tail recursive function:
let buildList x y =
let (x,y) = if x<y then (x,y) else (y,x) in
let rec aux cpt acc =
if cpt < x then acc
else aux (cpt-1) (cpt::acc)
in aux y []
First, make sure that you ordered your boundaries correctly (idiot-proof), and then construct the list thank to a local recursive function which takes an accumulator.
Two alternatives relying on batteries' package,
Using unfold, which purpose is to build list,
let range ~from:f ~until:u =
BatList.unfold f (function | n when n <= u -> Some (n, succ n) | _ -> None)
Using Enum, allowing to work with lazy datastructure,
# BatList.of_enum ## BatEnum.(1--9);;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
My suggestion, this respects the ordering of the arguments.
let rec iota n m =
let oper = if n < m then succ else pred in
if n = m then [n] else n :: iota (oper n) m
Edit:
The operator selection is inside the recursive part, it should better be outside like this:
let iota n m =
let oper = if n < m then succ else pred in
let rec f1 n m = if n = m then [n] else n :: f1 (oper n) m in
f1 n m
At more than 200000 elements I get a stack overflow (so here we are)
# iota 0 250000;;
Stack overflow during evaluation (looping recursion?).
Todo: tail recursion
let buildList i n =
let rec aux acc i =
if i <= n then
aux (i::acc) (i+1)
else (List.rev acc)
in
aux [] i
Test:
# buildList 1 3;;
- : int list = [1; 2; 3]
# buildList 2 1;;
- : int list = []
# buildList 0 250000;;
- : int list =
[0; 1; 2; 3; .... 296; 297; 298; ...]