I have written the following function in SMLNJ:
fun f(id : int, l : int list list) =
let
val i : int = length(l) - 1
in
while i > 0 do
(
if (exists(id, List.nth(l, i))) then
List.hd(List.nth(l, i)) := 1
else();
i = i - 1
)
end;
The error received is as follows:
Error operator and operand don't agree [tycon mismatch]
operator domain: 'Z ref * 'Z
operand: int * [int ty]
in expression:
List.hd (List.nth (l,i)) := 1
I am aware that the operator domain is what the function expects while the operand is what is provided.
I presume this occurs as an int
cannot be assigned to a list
type. However, I am unclear as to how List.hdList.nth(l, i))
would result in anything other than an int
.
Please advise on how I may fix this error and the supporting logic.
SML/NJ's error message does not make this terribly clear. If you were to put this code inside the Moscow ML REPL, and include the function contains
which you've used but not defined, you'd get the following error:
! Toplevel input:
! List.hd(List.nth(l, i)) := 1
! ^
! Type clash: expression of type
! int list list
! cannot have type
! 'a ref list list
Your program fails because you're treating int values as if they were int ref values.
In functional programming you generally try to avoid mutable variables (ref values).
To elaborate on the problem you're having:
List.hd(List.nth(l, i)) := 1
means "Set the reference returned by List.hd(List.nth(l, i))
to 1
. Since l
is an int list list, then List.nth(l, i)
returns the i
th element of that (or crashes), which is an int list. Then List.hd(...)
takes the 1st element of that (or crashes), which is an int. Not an int ref.
For this line to work, you'd need for l : int ref list list
.
But you don't want that.
i = i - 1
is a boolean expression that returns true
if i
is equivalent to itself minus 1. This is not true for any int. You probably intend to subtract 1 from i
and place the result in i
, but you can't, because i
is not a mutable variable, and the operator for updating a ref is called :=
.
If your problem were to convert the list
val m = [ [ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ] ]
into the list
val n = [ [ 1, 2, 3 ],
[ 1, 5, 6 ],
[ 1, 8, 9 ] ]
then a short way to do this would be to use List.map
:
val n = List.map (fn row => 1 :: List.drop (row, 1)) m
A more manual way that practices recursion and pattern matching over iteration like while ... do
(that only works if you have mutable variables) and partial functions like List.hd
and List.nth
(that might crash) could be:
fun f [] = []
| f (row::m) = (1 :: List.drop (row, 1)) :: f m
val n = f m
If you want a mutable version, consider the Array2
module instead of an int list list.
Here is one Array2
solution where the cursor is increased using recursion:
fun appulate f from to =
if from > to then ()
else (f from; appulate f (from+1) to)
fun f col m =
appulate (fn row => Array2.update (m, row, col, 1))
0 (Array2.nRows m - 1)
fun show m =
Array2.appi Array2.RowMajor (fn (_, col, c) =>
print (Int.toString c ^ (if col + 1 = Array2.nCols arr then "\n" else "" )))
{base=arr,row=0,col=0,nrows=NONE,ncols=NONE};
val m' = Array2.fromList m
val n' = f 0 m'
val _ = show n'
I realize that I haven't given any examples of a while ... do
, or any collection of int refs on which one operates with ref
, !
and :=
.