Using "let" to assign a tuple to a value in sml - tuples

Im trying to write a function that does the following, takes in :
[ #"t" ,#"h" ,#"e" ,#" " ,#"c" ,#"a" ,#"t" ]
The following is the output:
( [#"t" ,#"h" ,#"e" ] , [#" " ,#"c" ,#"a" ,#"t" ] )
so far I have..
fun isLetter c = #"a" <= c andalso c <= #"z";
//(this works fine and is used within the main function wordPop)
fun wordPop [] = ([],[])
| wordPop (hd::tl) = if not (isLetter hd)
then ([], hd::tl)
else (* ...not too sure... (1) *)
I know that I have to do something that looks like this at (1)
let (wordPop tl) in (x,y) end;
and somehow add hd to x. But not 100% sure how to do this.

Sounds like homework, so here is a hint:
In the non-basis case (hd::tl), if isLetter hd is false then you are ready to directly return something (no need for a recursive function call). Think carefully about what you want to return if the input looks like just explode(" cat") (i.e. [#" ", "c", "a", "t"] -- note the space).
As far as the other case (isLetter hd evaluating to true) goes, suppose you are processing the characters in "he cat". Then hd = #"h" and tl = [#"e", #" ", "c", "a", "t"].
If in this context you execute
let val (x,y) = wordPop tl
then x = [#"e"] and y = [#" ", "c", "a", "t"].
Given such x and y -- where do you want to place hd = #"h" before returning?

The final solution I got:
fun isLetter c = #"a" <= c andalso c <= #"z";
fun wordPop [] = ([],[]) |
wordPop (hd::tl) = if(not (isLetter hd))
then ([],(hd::tl))
else let val (x,y) = wordPop tl in (hd::x,y) end;

Related

Trying to get first word from character list

I have a character list [#"h", #"i", #" ", #"h", #"i"] which I want to get the first word from this (the first character sequence before each space).
I've written a function which gives me this warning:
stdIn:13.1-13.42 Warning: type vars not generalized because of value
restriction are instantiated to dummy types (X1,X2,...)
Here is my code:
fun next [] = ([], [])
| next (hd::tl) = if(not(ord(hd) >= 97 andalso ord(hd) <= 122)) then ([], (hd::tl))
else
let
fun getword [] = [] | getword (hd::tl) = if(ord(hd) >= 97 andalso ord(hd) <= 122) then [hd]#getword tl else [];
in
next (getword (hd::tl))
end;
EDIT:
Expected input and output
next [#"h", #"i", #" ", #"h", #"i"] => ([#"h", #"i"], [#" ", #"h", #"i"])
Can anybody help me with this solution? Thanks!
This functionality already exists within the standard library:
val nexts = String.tokens Char.isSpace
val nexts_test = nexts "hi hi hi" = ["hi", "hi", "hi"]
But if you were to build such a function anyway, it seems that you return ([], []) sometimes and a single list at other times. Normally in a recursive function, you can build the result by doing e.g. c :: recursive_f cs, but this is assuming your function returns a single list. If, instead, it returns a tuple, you suddenly have to unpack this tuple using e.g. pattern matching in a let-expression:
let val (x, y) = recursive_f cs
in (c :: x, y + ...) end
Or you could use an extra argument inside a helper function (since the extra argument would change the type of the function) to store the word you're extracting, instead. A consequence of doing that is that you end up with the word in reverse and have to reverse it back when you're done recursing.
fun isLegal c = ord c >= 97 andalso ord c <= 122 (* Only lowercase ASCII letters *)
(* But why not use one of the following:
fun isLegal c = Char.isAlpha c
fun isLegal c = not (Char.isSpace c) *)
fun next input =
let fun extract (c::cs) word =
if isLegal c
then extract cs (c::word)
else (rev word, c::cs)
| extract [] word = (rev word, [])
in extract input [] end
val next_test_1 =
let val (w, r) = next (explode "hello world")
in (implode w, implode r) = ("hello", " world")
end
val next_test_2 = next [] = ([], [])

SML - Get Indices of List

I'm working on a program that appends either a '+' or '-' to an element of a list, depending on whether the index of that element is odd or even (i.e an alternating sums list).
However, I'm having trouble identifying what the index of each element is. I have code that I believe should append the correct symbol, using if statements and mod
fun alternating([]) = 0
| alternating(l) =
if List.nth(l,hd(l)) mod 2 == 0 then '+'#hd(l)#alternating(tl(l))
else '-'#hd(l)#alternating(tl(l))
However, List.nth(l,hd(l)) always returns the element at the second index, not the first.
On the off chance that you really just want to negate integers them so you can pass them into some kind of summation, I would just negate the argument if it's odd. Using mutual recursion one can do it without any explicit index bookkeeping:
fun alternate l =
let
fun alternate1 [] = []
| alternate1 (x::xs) = (~x) :: alternate2 xs
and alternate2 [] = []
| alternate2 (x::xs) = x :: alternate1 xs
in
alternate1 l
end
It works like so:
- alternate [1,2,3,4];
val it = [~1,2,~3,4] : int list
I would strongly encourage you to use pattern matching instead of hd.
Edit discussing hd
As a rule of thumb, if you need hd you probably need tl as well. hd is a partial function--it's going to throw Empty if your list is empty. If you pattern match, you conveniently get variables for the head and tail of the list right there, and you get a visual reminder that you need to handle the empty list. It's more aesthetically pleasing, IMO, to see:
fun foo [] = ...
| foo (x::xs) = ...
than the equivalent
fun foo l =
if null l
then ...
else (hd l) ... (tl l)
In other words, you get shorter, cleaner code with an automatic reminder to make it correct. Win/win. To my knowledge there's no significant advantage to doing it the other way. Of course, you may find yourself in a situation where you know the list will have at least one element and you don't need to do anything else. You still have to consider the cases you're given, but it's a good rule of thumb.
If you want to decorate your list with an index you could try something like the following
fun add_index l =
let
fun add_index_helper (nil, _) = nil
| add_index_helper (h::tl,i) = (h,i) :: add_index_helper (tl,1+i)
in
add_index_helper (l,0)
end
val x = add_index [0,1,4,9,16,25]
but you can also just directly compute parity with the same method
fun add_sign l =
let
fun add_sign_helper (nil, _) = nil
| add_sign_helper (h::tl,i) = (h,i) :: add_sign_helper (tl,1-i)
in
add_sign_helper (l,0)
end
val y = add_sign [0,1,4,9,16,25]
then you can map the parity to a string
fun sign_to_char (x,0) = (x,"+")
| sign_to_char (x,_) = (x,"-")
val z = List.map sign_to_char y
or you can just add the sign directly
fun add_char l =
let
fun add_char_helper (nil, _) = nil
| add_char_helper (h::tl,0) = (h,"+") :: add_char_helper (tl,1)
| add_char_helper (h::tl,_) = (h,"-") :: add_char_helper (tl,0)
in
add_char_helper (l,0)
end
val zz = add_char [0,1,4,9,16,25]
Alternatively if you had a string list and you wanted to add chars you could try something like this
fun signs L =
let
datatype parity = even | odd
fun signs_helper ( nil ,_) = nil
| signs_helper (x::xs,even) = ("+" ^ x) :: signs_helper(xs,odd)
| signs_helper (x::xs,odd) = ("-" ^ x) :: signs_helper(xs,even)
in
signs_helper (L,even)
end
val z = signs ["x","2y","3z","4"]
(* this gives you val z = ["+x","-2y","+3z","-4"] : string list *)

ocaml listReverse error

This is the code I have to make a palindrome function. I already created the listReverse and explode function before that I use to make the palindrome. Can someone help me finnish the palindrome function?
let rec listReverse l = match l with
|[] -> []
|head :: tail -> (listReverse tail) # [head]
(* explode : string -> char list
* (explode s) is the list of characters in the string s in the order in
* which they appear
* e.g. (explode "Hello") is ['H';'e';'l';'l';'o']
*)
let explode s =
let rec _exp i =
if i >= String.length s then [] else (s.[i])::(_exp (i+1)) in
_exp 0
let rec palindrome w =
let a = explode w in
let b = listReverse a in
if c :: d
else false
You should use the List.rev standard function to reverse lists. Ocaml being a free software, you should look at its implementation (file stdlib/list.ml)
Try to explain in plain English (not code) what you are trying to achieve when you write
if c :: d
else false
Also, note that
if foo = bar then true else false
should be simplified to
foo = bar
You can replace your if statement with this:
(* tells wheter its a palindrome or not; most is (List.length a)/2*)
let rec same l1 l2 cur most =
match l1, l2 with
| h1::t1, h2::t2 when h1 = h2 ->
if cur < most then same t1 t2 (cur+1) most
else true
| _ -> false in
same a b 0 ((List.length a)/2)

string to list of char

I want to write a function that taking a string and return a list of char. Here is a function, but I think it is not do what I want ( I want to take a string and return a list of characters).
let rec string_to_char_list s =
match s with
| "" -> []
| n -> string_to_char_list n
Aside, but very important:
Your code is obviously wrong because you have a recursive call for which all the parameters are the exact same one you got in. It is going to induce an infinite sequence of calls with the same values in, thus looping forever (a stack overflow won't happen in tail-rec position).
The code that does what you want would be:
let explode s =
let rec exp i l =
if i < 0 then l else exp (i - 1) (s.[i] :: l) in
exp (String.length s - 1) []
Source:
http://caml.inria.fr/pub/old_caml_site/FAQ/FAQ_EXPERT-eng.html#strings
Alternatively, you can choose to use a library: batteries String.to_list or extlib String.explode
Try this:
let explode s = List.init (String.length s) (String.get s)
Nice and simple:
let rec list_car ch =
match ch with
| "" -> []
| ch -> String.get ch 0 :: list_car (String.sub ch 1 (String.length ch - 1));;
How about something like this:
let string_to_list str =
let rec loop i limit =
if i = limit then []
else (String.get str i) :: (loop (i + 1) limit)
in
loop 0 (String.length str);;
let list_to_string s =
let rec loop s n =
match s with
[] -> String.make n '?'
| car :: cdr ->
let result = loop cdr (n + 1) in
String.set result n car;
result
in
loop s 0;;
As of OCaml 4.07 (released 2018), this can be straightforwardly accomplished with sequences.
let string_to_char_list s =
s |> String.to_seq |> List.of_seq
Here is an Iterative version to get a char list from a string:
let string_to_list s =
let l = ref [] in
for i = 0 to String.length s - 1 do
l := (!l) # [s.[i]]
done;
!l;;
My code, suitable for modern OCaml:
let charlist_of_string s =
let rec trav l i =
if i = l then [] else s.[i]::trav l (i+1)
in
trav (String.length s) 0;;
let rec string_of_charlist l =
match l with
[] -> ""
| h::t -> String.make 1 h ^ string_of_charlist t;;

Ocaml introduction

i'm trying to learn ocaml right now and wanted to start with a little program, generating all bit-combinations:
["0","0","0"]
["0","0","1"]
["0","1","0"]
... and so on
My idea is the following code:
let rec bitstr length list =
if length = 0 then
list
else begin
bitstr (length-1)("0"::list);
bitstr (length-1)("1"::list);
end;;
But i get the following error:
Warning S: this expression should have type unit.
val bitstr : int -> string list -> string list = <fun>
# bitstr 3 [];;
- : string list = ["1"; "1"; "1"]
I did not understand what to change, can you help me?
Best regards
Philipp
begin foo; bar end executes foo and throws the result away, then it executes bar. Since this makes only sense if foo has side-effects and no meaningful return value ocaml emits a warning if foo has a return value other than unit, since everything else is likely to be a programmer error (i.e. the programmer does not actually intend for the result to be discarded) - as is the case here.
In this case it really does make no sense to calculate the list with "0" and then throw it away. Presumably you want to concatenate the two lists instead. You can do this using the # operator:
let rec bitstr length list =
if length = 0 then
[list]
else
bitstr (length-1)("0"::list) # bitstr (length-1)("1"::list);;
Note that I also made the length = 0 case return [list] instead of just list so the result is a list of lists instead of a flat list.
Although sepp2k's answer is spot on, I would like to add the following alternative (which doesn't match the signature you proposed, but actually does what you want) :
let rec bitstr = function
0 -> [[]]
| n -> let f e = List.map (fun x -> e :: x) and l = bitstr (n-1) in
(f "0" l)#(f "1" l);;
The first difference is that you do not need to pass an empty list to call the function bitsr 2 returns [["0"; "0"]; ["0"; "1"]; ["1"; "0"]; ["1"; "1"]]. Second, it returns a list of ordered binary values. But more importantly, in my opinion, it is closer to the spirit of ocaml.
I like to get other ideas!
So here it is...
let rec gen_x acc e1 e2 n = match n with
| 0 -> acc
| n -> (
let l = List.map (fun x -> e1 :: x) acc in
let r = List.map (fun x -> e2 :: x) acc in
gen_x (l # r) e1 e2 (n - 1)
);;
let rec gen_string = gen_x [[]] "0" "1"
let rec gen_int = gen_x [[]] 0 1
gen_string 2
gen_int 2
Result:
[["0"; "0"]; ["0"; "1"]; ["1"; "0"]; ["1"; "1"]]
[[0; 0]; [0; 1]; [1; 0]; [1; 1]]