How to print in if statement in SML - functional-programming

This is the code I tried
fun printsl([], k) = true
| printsl(h::t) = if k > h then print(h) andalso printsl(t);
But when I run the code, i get the following error
= stdIn:4.68-7.8 Error: syntax error: deleting SEMICOLON ID
stdIn:8.1 Error: syntax error found at EOF
The goal of the function is to print any number in the list that is less than the value k

A few things wrong here. Let's start with your function signature.
On the first line, your function takes 2 parameters, an empty list and whatever the type of k is (it is not important yet). Then on the second line, the function takes just one parameter, a non-empty list.
The two lines should match to look like:
fun printsl([], k) = ...
| printsl(h::t, k) = ...
Now let's think about the use of andalso. andalso is an operator which takes two booleans and returns a bool. It can be considered to have the signature bool * bool -> bool.
Your usage print(h) andalso printsl(t) does not match this signature.
The type of print is string -> unit, so the type of print(h) is unit (assuming h to be a string). As such, the usage of andalso is incorrect as the types on each side are not bools.
Instead of using andalso, we can simply execute both statements (print(h); printsl(t, k)). Sequences like this are expressions which return the last value. That is to say (x; y; z) returns z.
fun printsl([], k) = true
| printsl(h::t, k) = if h < k then (print(h); printsl(t, k));
However, this is still broken as the if-else construct in SML is an expression and must have a matching else, so you could use either of the following:
fun printsl([], k) = true
| printsl(h::t, k) =
if h < k then (print(h); printsl(t))
else printsl(t, k);
fun printsl([], k) = true
| printsl(h::t, k) = (
if h < k then print(h) else ();
printsl(t, k)
);
I personally prefer the latter as it prevents repetition of printsl.
This code will compile, but the signature is wrong. As we use h directly as a parameter of print, its type is inferred to be a string. This means that the compiler determines printsl to have type string list * string -> bool, whereas we are aiming for int list * int -> bool.
This can be corrected by changing the call print(h) to print(Int.toString h), giving us:
fun printsl([], k) = true
| printsl(h::t, k) = (
if h < k then print(Int.toString h) else ();
printsl(t, k)
);
This is now a function which will print all values in the given list which are less than k, but it always returns true. This provides no extra information so I would be inclined to change the signature to int list * int -> unit, giving us (finally):
fun printsl([], k) = ()
| printsl(h::t, k) = (
if h < k then print(Int.toString h) else ();
printsl(t, k)
);
This entire program could also be written in a more functional manner using List.app and List.filter.
fun printsl (xs, k) =
List.app
(fn y => print (Int.toString y))
(List.filter
(fn x => x < k)
xs);

Related

Int64 usage in recursive functions

I have the following code written in Ocaml to try and generate the first 50 catalan numbers:
let rec f n:int64=
if n<1 then 1L
else (4*n-2)*f((n-1L))/(n+1L)
;;
for i = 0 to 50 do
Printf.printf "%d\n"(f i)
done;
But the problem is that the recursion function doesn't seem to accept Int64 values and seems to want Int instead despite me notating n as int64:
File "/proc/self/fd/0", line 3, characters 19-21:
3 | else (4*n-2)*f((n-1L))/(n+1L)
^^
Error: This expression has type int64 but an expression was expected of type
int
exit status 2
Is there a way to ensure I can int64 numbers here with recursion?
Your problem isn't recursion per se, it's that the operators in OCaml are strongly typed. So the - operator is of type int -> int -> int.
Without getting into lots of fussing with the syntax, the easiest fix is probably to use the named functions of the Int64 module. You can use Int64.sub rather than -, for example.
You can use the notation Int64.(... <expr> ...) to avoid repeating the module name. If you rewrite your code this way, you get something like this:
let rec f (n : int64) : int64 =
Int64.(
if n < 1L then 1L
else
mul (sub (mul 4L n) 2L)
(f (div (sub n 1L) (add n 1L)))
)
Looking at the results computed by this function, they don't look like Catalan numbers to me. In fact the 50th Catalan number is too large to be represented as an int64 value.
So there is still some work to do. But this is how to get past the problem you're seeing (IMHO).
If you really want to work with such large numbers, you probably want to look at the Zarith module.
Submitted as an additional suggestion to Jeffrey's answer: whether using Int64 or Zarith, you might create a module defining arithmetic operators and then locally open that to clean up your code.
E.g.
module Int64Ops =
struct
let ( + ) = Int64.add
let ( - ) = Int64.sub
let ( * ) = Int64.mul
let ( / ) = Int64.div
end
let rec f n =
Int64Ops.(
if n < 1L then 1L
else (4L * n - 2L) * f (n - 1L) / (n + 1L)
)
Or you could use a functor to make it easier to work with multiple different types of numbers.
module type S =
sig
type t
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
val div : t -> t -> t
end
module BasicArithmeticOps (X : S) =
struct
type t = X.t
let ( + ) = X.add
let ( - ) = X.sub
let ( * ) = X.mul
let ( / ) = X.div
end
# let module A = BasicArithmeticOps (Float) in
A.(8. + 5.1);;
- : float = 13.1
# let module A = BasicArithmeticOps (Int) in
A.(8 + 3);;
- : int = 11
# let module A = BasicArithmeticOps (Int64) in
A.(4L + 3L);;
- : int64 = 7L

how to find number of occurrences in ML string list?

I am new to ML, here is my attemp to writing a function that receives:
list of strings L
string str
int counter
the function should return the number of occurrences of str in L
Here is my code:
(*
* return number of occurences of str in L
* count should be initialized to zero.
*)
fun aux_num_of_occur(L: string list) (str:string) (count:int) =
if null L then 0
else if str = hd(L) then
aux_num_of_occur tl(L) str (count+1)
else
aux_num_of_occur tl(L) str count
Those are the errors i got:
Error: case object and rules don't agree [tycon mismatch]
rule domain: string list * string * int
object: ('Z list -> 'Z list) * 'Y * 'X
in expression:
(case (arg,arg,arg)
of (L : string list,str : string,count : int) =>
if null L
then 0
else if <exp> = <exp> then <exp> <exp> else <exp> <exp>)
uncaught exception Error
raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
../compiler/TopLevel/interact/evalloop.sml:44.55
../compiler/TopLevel/interact/evalloop.sml:296.17-296.20
My Questions:
What is wrong with the syntax?
it is not clear to me what the error message says: what is a
rule and an object in this case?
how can i return a int by recursively calling a function? is it by passing to it a counter as an argument?
This is a classical mistake: tl(L) and tl L are the same thing -- you don't need parentheses for function application in ML-like languages, you just juxtapose the function and the argument(s).
So aux_num_of_occur tl(L) ... is the same thing as aux_num_of_occur tl L ..., i.e. you are trying to apply aux_num_of_occur to the tl function, not to a list of strings. Now, the tl function has type 'a list -> 'a list and that is what you see in the error message (with 'a being 'Z there).
I ought to say that this style with those null, hd, and tl functions is not very idiomatic in SML -- you could use pattern-mathing instead. It is also more convenient to make aux_num_of_occur local to prevent namespace pollution, prevent incorrect usage (you control the initial value of count). Additionally, this gives you the advantage of not passing str all the time when recursing further.
fun num_of_occur ss str =
let
fun loop [] count = count
| loop (s::ss) count =
if s = str
then loop ss (count + 1)
else loop ss count
in
loop ss 0
end
Notice that num_of_occur has a more general type ''a list -> ''a -> int, where ''a means any type with equality comparison. The compiler will generate a warning
Warning: calling polyEqual
which you can either ignore or add some type annotations to num_of_occur. See here for more detail.

Remove duplicates from a list in SML

I just started learning functional programming in SML and I want to know how I can combine the following two functions into a single function. The function isolate deletes the duplicates of a list of any type ('a) using the helper function 'removes'.
fun isolate [] = []
| isolate (l as x::xs) = x::isolate(remove(x,xs))
fun remove (x,[]) = []
| remove (x,l as y::ys) = if x = y then remove(x,ys) else y::remove(x,ys)
So, for the purpose of better understanding the constructs in the SML, how would you include the function remove within isolate? This may seem trivial, but I have thinking about it and can't figure it out. Thank you for your help!
One method would be to just define remove inside isolate.
fun isolate [] = []
| isolate (l as x::xs) =
let fun remove (x,[]) = []
| remove (x,l as y::ys) = if x = y
then remove(x,ys)
else y::remove(x,ys)
in
x::isolate(remove(x,xs))
end
Alternately, to make deduplication one function, though all this really does is use the library function List.filter to do the same thing that remove does.
fun isolate [] = []
| isolate (x::xs) = x::isolate(List.filter (fn y => y <> x) xs)
My idea: define a nested function to check if there are duplicated elements in the list:
fun set(nums:int list)=
let fun duplicate(x:int, l:int list)=
if null l
then false
else hd l=x orelse duplicate(x,tl l)
in
if null nums
then []
else
let val s=set(tl nums)
in if duplicate(hd nums,s)
then s
else hd nums::s
end
end
But it will give a list that only remains the last one for every duplicated elements.
I want to propound the following solutions of this problem:
fun remove_duplicates(xs: int list) =
let
fun check(xs: int list, item: int) =
if null xs
then false
else if hd xs = item
then true
else check (tl xs, item)
fun go_through_list(xs: int list) =
if null xs
then []
else if check(tl xs, hd xs)
then go_through_list(tl xs)
else hd xs :: go_through_list(tl xs)
in
go_through_list(xs)
end
It's more lines of code than in the solution propounded by #qaphla
My idea is to first sort the list, then recursively return a new list without duplicates:
fun remove_duplicates(l: int list) =
if null(l)
then []
else if null(tl l)
then l
else
let
fun compare(x: int, y: int) = x > y
fun sort(l: int list) = ListMergeSort.sort(compare) l
val l_sorted = sort(l)
in
if (hd l_sorted) = (hd (tl l_sorted))
then remove_duplicates(tl l_sorted)
else (hd l_sorted)::remove_duplicates(tl l_sorted)
end

Standard ML - Return the index of the occurrences of a given value in a list

I'm trying to figure out how to return a list of the indexes of occurrences of a specific value in another list.
i.e.
indexes(1, [1,2,1,1,2,2,1]);
val it = [1,3,4,7] int list
I'm trying to figure out how lists work and trying to get better at recursion so I don't want to use List.nth (or any library functions) and I don't want to move into pattern matching quiet yet.
This is what I have so far
fun index(x, L) =
if null L then 0
else if x=hd(L) then
1
else
1 + index(x,tl L);
fun inde(x, L) =
if null L then []
else if x=hd(L) then
index(x, tl L) :: inde(x, tl L)
else
inde(x, tl L);
index(4, [4,2,1,3,1,1]);
inde(1,[1,2,1,1,2,2,1]);
This gives me something like [2, 1, 3, 0]. I guess I'm just having a hard time incrementing things properly to get the index. The index function itself works correctly though.
Instead you could also make two passes over the list: first add an index to each element in the list, and second grap the index of the right elements:
fun addIndex (xs, i) =
if null xs then []
else (hd xs, i) :: addIndex(tl xs, i+1)
fun fst (x,y) = x
fun snd (x,y) = y
fun indexi(n, xs) =
if fst(hd xs) = n then ... :: indexi(n, tl xs)
else indexi(n, tl xs)
(I left out part of indexi for the exercise.)
Where addIndex([10,20,30],0) gives you [(10,0),(20,1),(30,2)]. Now you can use addIndex and indexi to implement your original index function:
fun index(n, xs) = indexi(n, addIndex(xs, 0))
When you get that to work, you can try to merge addIndex and indexi into one function that does both.
However, you really want to write this with pattern matching, see for instance addIndex written using patterns:
fun addIndex ([], _) = []
| addIndex (x::xs, i) = (x,i) :: addIndex(xs, i+1)
If you do index(1,[2]), it gives 1, which is not correct. When the list is empty, it gives you zero. In a function like this, you'd probably want to use SOME/NONE feature.

Homework help converting an iterative function to recursive

For an assignment, i have written the following code in recursion. It takes a list of a vector data type, and a vector and calculates to closeness of the two vectors. This method works fine, but i don't know how to do the recursive version.
let romulus_iter (x:vector list) (vec:vector) =
let vector_close_hash = Hashtbl.create 10 in
let prevkey = ref 10000.0 in (* Define previous key to be a large value since we intially want to set closefactor to prev key*)
if List.length x = 0 then
{a=0.;b=0.}
else
begin
Hashtbl.clear vector_close_hash;
for i = 0 to (List.length x)-1 do
let vecinquestion = {a=(List.nth x i).a;b=(List.nth x i).b} in
let closefactor = vec_close vecinquestion vec in
if (closefactor < !prevkey) then
begin
prevkey := closefactor;
Hashtbl.add vector_close_hash closefactor vecinquestion
end
done;
Hashtbl.find vector_close_hash !prevkey
end;;
The general recursive equivalent of
for i = 0 to (List.length x)-1 do
f (List.nth x i)
done
is this:
let rec loop = function
| x::xs -> f x; loop xs
| [] -> ()
Note that just like a for-loop, this function only returns unit, though you can define a similar recursive function that returns a meaningful value (and in fact that's what most do). You can also use List.iter, which is meant just for this situation where you're applying an impure function that doesn't return anything meaningful to each item in the list:
List.iter f x

Resources