1

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?

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.