Is there a better solution? - ocaml

I am making a function that returns the number of times a given number appears in an int list. I have a solution, but I feel it is rather elementary and could be done better (maybe with match and rec??). I also feel the styling is off, but am not too sure how to style OCaml just yet.
Here is the code:
let num_occurs (n : int) (nums : int list) : int =
let x = List.fold_left (+) 0
(List.filter (fun (x : int) ->
if x = n then true else false) nums) in
x / n ;;

So what you have looks pretty good, though I'm not sure what x / n is intended to do. Using List.fold_left and List.filter is a good intuition for OCaml. To make this a bit cleaner, though, just use List.length! As a bonus, you can use x = n directly rather than if then else.
let num_occurs (n : int) (nums : int list) : int =
List.length (List.filter (fun x -> x = n) nums)

Related

Is there a better way to write this filter function?

I am supposed to create a function what takes a list and a range and filter the list to be within that range of numbers. Here is what I have so far:
let filter_range (nums : int list) (range : int * int) : int list =
match range with
| (a, b) -> if a > b then []
else List.filter (fun (x : int) ->
if a <= x && x <= b then true else false) nums ;;
I feel like this could be much cleaner, any suggestions welcome!
Here you are:
let filter_range nums (lo, hi) =
List.filter (fun x -> lo <= x && x <= hi) nums

How to use list comprenhension in Ocaml

This code is in Haskell. How can i do the same thing in OCAML?
perfect n = [x | x<-[1..n], sum(f x) == x]
f x = [i | i<-[1..x-1], x `mod` i ==0]
While Jeffrey's answer is correct, using appropriate libraries (in this case, sequence), you can get something that is similar in terseness and semantics to the Haskell style:
module S = Sequence
let sum = S.fold (+) 0
let f x = S.filter (fun i -> x mod i = 0) S.(1 -- (x-1))
let perfect n = S.filter (fun x -> sum (f x) = x) S.(1 -- n)
You're using many (really nice) features of Haskell that don't exist in OCaml.
For list comprehensions, you can use List.filter.
For the notation [x .. y] you can use this range function:
let range a b =
let rec go accum i =
if i > b then List.rev accum else go (i :: accum) (i + 1)
in
go [] a
For sum you can use this:
let sum = List.fold_left (+) 0

Core's `List.init` in Pervasives?

I'm used to JaneStreet's Core library. Its List module has a neat init function:
List.init;;
- : int -> f:(int -> 'a) -> 'a list = <fun>
It allows you to create a list with using a custom function to initialize elements:
List.init 5 ~f:(Fn.id);;
- : int list = [0; 1; 2; 3; 4]
List.init 5 ~f:(Int.to_string);;
- : string list = ["0"; "1"; "2"; "3"; "4"]
However, this function doesn't seem to exist in Pervasives, which is sad. Am I missing something, or do I have to implement it myself? And if I do need to write it, how do I achieve this?
EDIT:
I have written an imperative version of init, but it doesn't feel right to have to resort to OCaml's imperative features in such a case. :(
let init n ~f =
let i = ref 0 in
let l = ref [] in
while !i < n do
l := (f !i) :: !l;
incr i;
done;
List.rev !l
;;
EDIT 2:
I've opened a pull request on OCaml's GitHub to have this feature included.
EDIT 3:
The feature was released in OCaml 4.06.
A recursive implementation is fairly straightforward. However, it is not tail-recursive, which means that you'll risk a stack overflow for large lists:
let init_list n ~f =
let rec init_list' i n f =
if i >= n then []
else (f i) :: (init_list' (i+1) n f)
in init_list' 0 n f
We can transform it into a tail-recursive version using the usual techniques:
let init_list n ~f =
let rec init_list' acc i n f =
if i >= n then acc
else init_list' ((f i) :: acc) (i+1) n f
in List.rev (init_list' [] 0 n f)
This uses an accumulator and also needs to reverse the intermediate result, as the list is constructed in reverse. Note that we could also use f (n-i-1) instead of f i to avoid reversing the list, but this may lead to unexpected behavior if f has side-effects.
An alternative and shorter solution is to simply use Array.init as a starting point:
let init_list n ~f = Array.(init n f |> to_list)
You can copy the code from JaneStreet and use it.
The code look's like (but not exactly the same) :
let init n ~f =
if n < 0 then raise (Invalid_argument "init");
let rec loop i accum =
if i = 0 then accum
else loop (i-1) (f (i-1) :: accum)
in
loop n []
;;
You can find the original code inside core_list0.ml from the package core_kernel.

Adding no value to return list

I'm having a problem with understanding how F# works. I come from C# and I think that I'm trying to make F# work like C#. My biggest problem is returning values in the correct format.
Example:
Let's say I have function that takes a list of integers and an integer.
Function should print a list of indexes where values from list match passed integer.
My code:
let indeks myList n = myList |> List.mapi (fun i x -> if x=n then i else 0);;
indeks [0..4] 3;;
However it returns:
val it : int list = [0; 0; 0; 3; 0]
instead of just [3] as I cannot ommit else in that statement.
Also I have targeted signature of -> int list -> int -> int list and I get something else.
Same goes for problem no. 2 where I want to provide an integer and print every number from 0 to this integer n times (where n is the iterated value):
example:
MultiplyValues 3;;
output: [1;2;2;3;3;3]
Best I could do was to create list of lists.
What am I missing when returning elements?
How do I add nothing to the return
example: if x=n then n else AddNothingToTheReturn
Use List.choose:
let indeks lst n =
lst
|> List.mapi (fun i s -> if s = n then Some i else None)
|> List.choose id
Sorry, I didn't notice that you had a second problem too. For that you can use List.collect:
let f (n : int) : list<int> =
[1 .. n]
|> List.collect (fun s -> List.init s (fun t -> s))
printfn "%A" (f 3) // [1; 2; 2; 3; 3; 3]
Please read the documentation for List.collect for more information.
EDIT
Following s952163's lead, here is another version of the first solution without the Option type:
let indeks (lst : list<int>) (n : int) : list<int> =
lst
|> List.fold (fun (s, t) u -> s + 1, (if u = n then (s :: t) else t)) (0, [])
|> (snd >> List.rev)
This one traverses the original list once, and the (potentially much shorter) newly formed list once.
The previous answer is quite idiomatic. Here's one solution that avoids the use of Option types and id:
let indeks2 lst n =
lst
|> List.mapi (fun i x -> (i,x))
|> List.filter (fun x -> (fst x) % n = 0 )
|> List.map snd
You can modify the filter function to match your needs.
If you plan to generate lots of sequences it might be a good idea to explore Sequence (list) comprehensions:
[for i in 1..10 do
yield! List.replicate i i]
If statements are an expression in F# and they return a value. In this case both the IF and ELSE branch must return the same type of value. Using Some/None (Option type) gets around this. There are some cases where you can get away with just using If.

number_in_month exercise (Count elements in a list)

I have been trying to count elements in a list of integer 3-tuples, that equals a given integer using SML, but it's not working. Can anyone help me figure out what's wrong with the below code or straighten it up for me?
fun number_in_month(x : int*int*int list, m: int) =
if null x then 0
else
let fun inc x = x + 1;
in
val counter = 0;
if m = #2 (hd x) andalso m > 0 then inc counter
number_in_month((tl x), m)
` else
number_in_month((tl x), m)
end
This function is supposed to return the number of times m equals to the second element of each tuple in the list.
Clearly you have a hard time to let go of your imperative thinking.
Let me try and address some of your issues
You should be using pattern matching instead of using null x, hd x and tl x.
This also apply to decomposing tuples and records. For example
fun number_in_month ((x1, x2, x3) :: xs, m) = ...
or, since we don't ever use x1 and x3
fun number_in_month ((_, x2, _) :: xs, m) = ...
This way it is clearly seen that the first argument is a list of 3-tuples, and no type annotation
is needed
Also when you omit the explicit type annotation, which is the whole idea of having a type system
that can infer them for you (see next point), then this code
fun foo42 xs = map (fn x => #2 x) xs
will give you some nasty errors on "unresolved flex record" (this error message is from SML/NJ)
/tmp/sml20620PlF:105.5-105.44 Error: unresolved flex record
(can't tell what fields there are besides #2)
which is easily fixed by decomposing the 3-tuple
fun foo42 xs = map (fn (_, x2, _) => x2) xs
Speaking of type annotations. They are (almost always) not needed, and they clutter up the
readability of the code. Not to mention that they unnecessarily restricts the types you function
may be used on.
Also the type annotation you have given is erroneous according to what you really wan't. You
should have places parenthesis around the int * int * int. Currently it is interpreted as a
3-tuple of two ints and an int list int * int * (int list).
If you really insist in type annotating your function, then you can do it like this
val number_in_month : (int * int * int) list * int -> int =
fn ([] , m) => 0
| ((_,x2,_) :: xs, m) => 42
This is "almost" like Haskell, where the type is given just before the function declaration.
Try to be more consistent in they way you indent your code. That will give you better clarity.
Here I'm specifically thinking of the way you have indented the else part end the in ... end
part. The below part is clearly still erroneous in so many ways i can't begin to imagine, but it
gives an idea as how to do it
fun number_in_month(x : int*int*int list, m: int) =
if null x then 0
else
let fun inc x = x + 1;
in
val counter = 0;
if m = #2 (hd x) andalso m > 0 then
inc counter
number_in_month((tl x), m)
else
number_in_month((tl x), m)
end
You can't declare a variable val counter = 0 inside the in ... end part of a let-expression.
The semantics of a let-expression is
let
dec
in
exp_1; ...; exp_n
end
thus all declarations (function and value bindings, etc) must go in the let ... in part.
There is no need on earth to have an increment function, it just clutters the readability.
Remember that SML uses single assignment, thus variables are immutable after they are declared.
The sequence-thing inside your nested if-expression
inc counter
number_in_month((tl x), m)
makes absolutely no sense. The only way you can have more than one expression inside the
then ... else part (actually any place, where a single expression is expected), is with a
sequence (exp_1; ...; exp_n). However this is only usable when all but the last expression has
side effect(s), as their results is ignored/thrown away
- (print "Foo\n"; print "Bar\n"; 42);
Foo
Bar
val it = 42 : int
If you search a bit here on SO, you will see that a quite similar question has recently been asked and answered. Though it differs in the the type of the last argument, you might still get some useful pointers.
All in all a solution might look like
fun number_in_month ([], _) = 0
| number_in_month ((_,x2,_) :: xs, m) =
if x2 = m then
1 + number_in_month(xs, m)
else
number_in_month(xs, m)
However since your problem is simpler than the previously stated one, you could easily use some of the higher-order functions from the list module in the basis library
fun number_in_month (xs, m) = length (List.filter (fn (_, x2, _) => x2 = m) xs)
Or even (arguably) simpler, by folding over the list and incrementing a variable along the way each time it matches
fun number_in_month (xs, m) = foldl (fn ((_, x2, _), b) => if x2 = m then b+1 else b) 0 xs
fun number_in_month (L : (int*int*int) list, m : int) =
if L = nil
then 0
else
(if #2 (hd L) = m then 1 else 0) + number_in_month (tl L,m);
TESTING:
number_in_month ([] , 2);
number_in_month ([(1,2,3)] , 2);
number_in_month ([(1,2,3),(2,2,2)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,2,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19),(16,2,7)] , 2);
Reference:
http://www.cs.sunysb.edu/~leo/CSE215/smllistexamples.txt
http://www.standardml.org/Basis/list.html