helen
helen

Reputation: 587

SML - error in finding elements in list

I'm new to SML. I've written a function which takes 2 int and a list of tuples as input.

fun move(x,y,mylist:(int * int)list): NOxNO =
    let
      val counter = ref y-1
    in
      if y=1 then (x,y)
      else (
        while !counter > 0 do (
          if List.exists (fn s => s = (x,!counter)) mylist
          then counter := !counter - 1
          else break
        );
        if !counter = 0 then (x,y) else (x,y-1)
      )
    end

I may have syntax error since I'm a beginner. What the function is trying to do is: it will check the list to find all the tuples whose first element is x and second element varies from 1 to y-1 (tuples like this: (x,1) (x,2) ... (x,y-1) ) and if all of them exist in the list it will return (x,y) else (x,y-1). I used a while loop and a counter. counter is set to y-1 at first and in while loop if (x,counter) was found, counter's value will decrease. At last if counter=0 it means we have found all the tuples. After running the program I encountered this error:

Caught Error ../compiler/TopLevel/interact/evalloop.sml:296.17-296.20
             ../compiler/TopLevel/interact/evalloop.sml:44.55
             ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27

What's wrong?

Upvotes: 1

Views: 451

Answers (2)

sshine
sshine

Reputation: 16125

Here's some feedback:

  1. (Error) As Andreas Rossberg said, break doesn't exist. But if you use (), the loop won't terminate when y > 1 and the predicate evaluates to false. You probably want to "break" by setting counter := 0 or counter := -1, depending on what you want the subsequent if !counter = 0 ... to do.

  2. (Error) As Andreas Rossberg said, ref y-1 gives the following type error:

    ! Toplevel input:
    ! val r = ref y-1;
    !               ^
    ! Type clash: expression of type
    !   int
    ! cannot have type
    !   int ref
    

    This is because function application (ref y) binds tighter than infix operators (y-1). What you mean is ref (y-1), since you can't subtract 1 from a reference.

  3. This isn't very comprehensible or robust. I tried to run it in the simplest case I could think of,

    val test1 = move (1,1,[])
    

    But that's a weird base case not handled by the loop. If I change the numbers slightly,

    val test2 = move (5,6,[])
    

    then it returns either (5,6) or (5,5) depending on what you change break into.

Based on your description below the code, here is a suggested implementation, although I'm still not completely certain I understand the use of this function:

(* verticalPointsExist (x, y, ps) checks that
 * (x,1), (x,2), ..., (x,y-1) are all in ps. *)
fun verticalPointsExist (_, 0, _) = true
  | verticalPointsExist (x, y, ps) = List.exists (fn p => (x,y) = p) ps
                             andalso verticalPointsExist (x, y - 1, ps)

fun move (x, y, ps) =
    if verticalPointsExist (x, y, ps) then (x,y) else (x,y-1)

Considerations I made:

  • Use recursion rather than iteration.

  • Split the checking part into a helper function, so move doesn't do two things.

  • Give the functions good names so the code reads more easily. Since I don't know the domain and am really guessing as to whether y is some kind of vertical dimension, there are probably even better names out there. (verticalLineExists? verticalPathClear?) Maybe a more general function will have a better name, e.g. one that took two points and saw that the line is clear between them.

Upvotes: 1

Andreas Rossberg
Andreas Rossberg

Reputation: 36118

There is no break in ML. You probably just want to write () there. Also, you'll need parens around the argument to ref in line 3.

Upvotes: 1

Related Questions