I'm working on this code and can't make it run. I've checked it few times and still can't figure out why it's not working.
fun date_to_string (date : (int * int * int)) =
let
val months = ["January", "February","March", "April",
"May", "June","July", "August", "September", "October", "November", "December"];
fun get_nth (xs : string list, n : int) =
if n=1
then hd xs
else get_nth(tl xs, n-1)
in
get_nth(months, Int.toString(#2 date)) ^ " " ^ Int.toString(#3 date) ^ ", " ^ Int.toString(#1 date)
end
Here is what I get back when I try to run it:
When you see an error of the form:
operator domain: <type1>
operand : <type2>
then it is saying that it is expecting something of type <type1>, but you are giving it something of type <type2>. In your case, get_nth is expecting a tuple, where the first element is a list of strings and the second element is an int. You are providing a tuple where the first element is a list of strings (which is correct), but your second argument is a string, which should be an int. You are going to want to change
get_nth(months, Int.toString(#2 date))
to
get_nth(months, #2(date))
Related
So im really confused as i am new to sml and I am having trouble with syntax of how i want to create my function.
the instructions are as follows...
numberPrefix: char list → string * char list
Write a function named numberPrefix that returns (as a pair) a string representing the digit characters at the
beginning of the input list and the remaining characters after this prefix. You may use the Char.isDigit and
String.implode functions in your implementation.
For example,
numberPrefix [#"a", #"2", #"c", #" ", #"a"];
val it = ("", [#"a", #"2", #"c", #" ", #"a") : string * char list
numberPrefix [#"2", #"3", #" ", #"a"];
val it = ("23", [#" ", #"a"]) : string * char list
Here is my code so far...
fun numberPrefix(c:char list):string*char list =
case c of
[] => []
|(first::rest) => if isDigit first
then first::numberPrefix(rest)
else
;
I guess what i am trying to do is append first to a seperate list if it is indeed a digit, once i reach a member of the char list then i would like to return that list using String.implode, but I am banging my head on the idea of passing in a helper function or even just using the "let" expression. How can I essentially create a seperate list while also keeping track of where i am in the original list so that I can return the result in the proper format ?
First of all, the function should produce a pair, not a list.
The base case should be ("", []), not [], and you can't pass the recursive result around "untouched".
(You can pretty much tell this from the types alone. Pay attention to types; they want to help you.)
If you bind the result of recursing in a let, you can access its parts separately and rearrange them.
A directly recursive take might look like this:
fun numberPrefix [] = ("", [])
| numberPrefix (cs as (x::xs)) =
if Char.isDigit x
then let val (number, rest) = numberPrefix xs
in
((str x) ^ number, rest)
end
else ("", cs);
However, splitting a list in two based on a predicate – let's call it "splitOn", with the type ('a -> bool) -> 'a list -> 'a list * 'a list – is a reasonably useful operation, and if you had that function you would only need something like this:
fun numberPrefix xs = let val (nums, notnums) = splitOn Char.isDigit xs
in
(String.implode nums, notnums)
end;
(Splitting left as an exercise. I suspect that you have already implemented this splitting function, or its close relatives "takeWhile" and "dropWhile".)
(* Write a function number_in_month that takes a list
of dates and a month (i.e., an int) and
returns how many dates in the list are in the given month.*)
fun number_in_month(datelist : (int*int*int) list, month : int) =
if null(tl (datelist))
then if #2(hd (datelist)) = month then 1 else 0
else if #2(hd (datelist)) = month
then 1 + number_in_month(tl datelist, month)
else number_in_month(tl datelist, month)
(* Write a function number_in_months that takes a list of dates and a list of months
(i.e., an int list) and returns the number of dates in the list of dates that are
in any of the months in the list of months. Assume the list of months
has no number repeated. Hint: Use your answer to the previous problem. *)
fun number_in_months(datelist : (int*int*int) list, monthlist : int list)
if null(tl (monthlist))
then number_in_month(datelist, hd monthlist)
else number_in_month(datelist, hd monthlist)
+ number_in_months(datelist, tl monthlist)
The second function gives me this error when I try to compile it:
hw1.sml:42.5 Error: syntax error: inserting EQUALOP
[unexpected exception: Compile]
uncaught exception Compile [Compile: "syntax error"]
raised at: ../compiler/Parse/main/smlfile.sml:19.24-19.46
../compiler/TopLevel/interact/evalloop.sml:45.54
../compiler/TopLevel/interact/evalloop.sml:306.20-306.23
../compiler/TopLevel/interact/interact.sml:65.13-65.16
-
"syntax error: inserting EQUALOP" means that SML is expecting a = character.
The error messages from SML/NJ is one of the things that haven't improved one bit over the past twenty years. They often report what the parser does in order to try to recover from an error rather than what the error might be.
List recursion (and most everything else) is much nicer to write with pattern matching than with conditionals and selectors:
fun number_in_month ([], _) = 0
| number_in_month ((_, m, _)::ds, m') = (if m = m' then 1 else 0) + number_in_month(ds, m');
fun number_in_months (_, []) = 0
| number_in_months (ds, m::ms) = number_in_month(ds, m) + number_in_months(ds, ms);
This also lets SML let you know when you have forgotten a case, for instance the case of the empty list (which you forgot about).
Answer: Forgot the = sign. This is correct:
fun number_in_months(datelist : (int*int*int) list, monthlist : int list) =
if null(tl (monthlist))
then number_in_month(datelist, hd monthlist)
else number_in_month(datelist, hd monthlist)
+ number_in_months(datelist, tl monthlist)
I am writing a recursive ML function, that takes a string, and an index value, and splits the string at the given index. The function should return a list containing two strings.
I understand that I need two base cases one to check if the index has been reached, and one to check if the string is out of characters. I am stuck on how I assign the characters to different strings. Note, I used a helper function to clean up the initial call, so that explode will not need to be typed on every function call.
fun spliatHelp(S, num) =
if null S then nil
else if num = 0 then hd(S) :: (*string2 and call with tl(S)*)
else hd(S) :: (*string1 and call with tl(S)*)
fun spliat(S, num) =
spliatHelp(explode(S), num);
From an input of spliat("theString", 3);
My ideal output would be ["the", "String"];
For the num = 0 case, you just need to return [nil, S] or (equivalently) nil :: S :: nil.
For the other case, you need to make the recursive call spliatHelp (tl S, num - 1) and then examine the result. You can use either a let expression or a case expression for that, as you prefer. The case expression version would look like this:
case spliatHelp (tl S, num - 1)
of nil => nil (* or however you want to handle this *)
| [first, second] => [hd S :: first, second]
| raise (Fail "unexpected result")
Incidentally, rather than returning a string list with either zero or two elements, I think it would be better and clearer to return a (string * string) option. (Or even just a string * string, raising an exception if the index is out of bounds.)
I am doing a programming assignment with SML. One of the functions requires me to return a list of triple tuples of ints ( (int * int * int) list ) use to other lists. The function sorts through dates and months to see if any of them coincide, if they do, then they add it to the list. Here is the code for that.
fun dates_in_month (dates : (int * int * int) list, month : int) =
if null dates
then []
else
if #2 (hd dates) = month
then (hd dates) :: dates_in_month(tl dates, month)
else dates_in_month(tl dates, month)
fun dates_in_months (dates : (int * int * int) list, months : int list) =
if null months orelse null dates
then []
else
dates_in_month(dates, hd months) ::
dates_in_months(dates, tl months)
Using this code works to a point, however the function returns an (int * int * int) list list, instead of a (int * int * int) list. I think the problem lies with the
then [] statement. Any help would be appreciated.
The problem is not the then [], the problem lies here:
dates_in_month(dates, hd months) ::
dates_in_months(dates, tl months)
Here you take the result of dates_in_month(dates, hd months), which is a list, and use it as the first argument to ::. As you know, h :: t produces a list whose first element is h. So in this case you create a list whose first element is a list. That is you're creating a list of lists.
Since you don't want that, you shouldn't use ::. You can use #, which takes two lists as its operands and concatenates them. So while [1,2] :: [3,4] :: [] would produce [[1,2], [3,4]], [1,2] # [3,4] # [] will produce [1,2,3,4], which is what you want.
I am new to Standard ML, and can't figure out why I am getting this type mismatch error:
fun number_in_month (month : int, dates : int list) =
if null dates
then 0
else if (month = (hd (tl (hd dates))))
then number_in_month(month, (tl dates)) + 1
else number_in_month(month, (tl dates))
Evaluating this function results in the following error:
Error: operator and operand don't agree [tycon mismatch]
5 operator domain: 'Z list
6 operand: int
7 in expression:
8 tl (hd dates)
However, at the REPL, if i do the following:
val x = [[84, 12, 23], [83, 01, 18]]
12 = (hd (tl (hd x))) (* -> val it = true : bool *)
I am not sure what the type-checking rules are in this case, and I don't see why the same expression would work on the REPL but not when I try to evaluate the subexpression in the function.
You're getting the head of the tail of the head of a list. Your x (in the REPL) is
a int list list (a list of a list of ints). But your function definition declares it
as an int list. Re-declaring number_in_month with dates: int list list should solve
your problem:
fun number_in_month (month : int, dates : int list list) =
...
It works as you expect in the REPL because you define x without explicitly declaring it's type. SML infers that the type of x is int list list which is why (hd (tl (hd x)))
passes the type-checker.
UPDATE
(was trying to add this right when stackoverflow went down)
If you're interested, here's some ideas on how you could re-write your code to make it
more ML-ish:
First, you could use pattern matching:
fun number_in_month (month: int, []) = 0
| number_in_month (month: int, ([y,m,d]::rest)) =
if month = m then number_in_month(month, rest) + 1
else number_in_month(month, rest)
So number_in_month takes a tuple of a month and a list of dates, which is logically either [] or ([y,m,d]::rest). This is compatible with how you've chosen to represent dates
(as a list of ints), but this will compile with a match nonexhaustive warning. That makes sense, because what happens if you pass in dates as [[84], [83]]? The pattern match approach at least warns you about this, but with code like (hd (tl (hd dates))) you'll get
a runtime error though your program has type-checked successfully. You could add another
pattern match for lists of dates where the date has less/more than 3 elements, but if
possible, it might be cleaner to represent dates as tuples of 3 ints.
type date = (int * int * int)
Then you could have:
fun number_in_month (month: int, []: date list) = 0
| number_in_month (month: int, ((y,m,d)::rest)) =
if month = m then number_in_month(month, rest) + 1
else number_in_month(month, rest)
Also, if you'd rather reuse code, you could try higher-order functions (such as foldr):
fun number_in_month (month: int, dates: date list) =
foldl (fn ((_,m,_), c) => if m = month then c+1 else c) 0 dates
Or
fun number_in_month (month: int, dates: date list) =
length (List.filter (fn (_,m,_) => m = month) dates)
More than you asked for, but I hope it helps.